📡 API Documentation

A JSON API for creating and managing pastes. All requests go to https://zpst.net/api.php with an action query parameter.

🔑 Your API Key

Sign in to generate an API key. Guest pastes work without one.

Overview

Every response is JSON with an ok boolean. Errors include an error string. Every response carries rate limit headers.

ActionMethodAuthDescription
create POSTOptionalCreate a paste (JSON or multipart with image)
get GET OptionalGet paste content and metadata
raw GET OptionalGet raw paste text as text/plain
exists GET OptionalLightweight existence check
update POSTRequiredUpdate fields on an existing paste
delete GET Key or tokenDelete a paste
duplicate POSTOptionalFork a paste (copies content, language, password)
history GET RequiredList edit revisions for a paste
list GET RequiredList your pastes with filtering and cursor pagination
login POSTNoneAuthenticate and retrieve API key
register POSTNoneCreate an account
info GET NoneSite capabilities and configuration
languages GET NoneFull list of supported syntax languages

CLI Client — zpst

A bash script wrapping the API. Requires curl and python3.

zpst bash · curl · python3
Download
curl -fsSL https://zpst.net/download-client.php -o ~/.local/bin/zpst
chmod +x ~/.local/bin/zpst

echo 'ZPST_KEY=your_api_key_here' >> ~/.config/zpst/config
echo 'ZPST_URL=https://zpst.net' >> ~/.config/zpst/config

Authentication

Pass your API key using any of these methods:

MethodExample
X-API-Key header PreferredX-API-Key: your_api_key_here
Authorization headerAuthorization: Bearer your_api_key_here
JSON body field{"api_key": "your_api_key_here"}

Avoid passing the key as a URL query parameter — it will appear in server access logs.

Rate Limits

Every response includes these headers:

HeaderDescription
X-RateLimit-Limit Max requests in the window
X-RateLimit-RemainingRequests remaining
X-RateLimit-Reset Unix timestamp when the window resets
Retry-After Seconds to wait — only present on 429 responses
ActionsLimitWindow
create, update, duplicate5/IP60s
get, raw, exists300/IP30s
login10/IP60s
register5/IP3600s
Paste password attempts7/IP60s

Errors

CodeMeaning
400Bad request — missing or invalid parameter
401Authentication required, or wrong paste password
403Forbidden — banned, private paste, or not the owner
404Paste not found
409Conflict — username/email taken, or duplicate content
410Paste has expired
413Content or image exceeds the maximum size
422Rejected — too many URLs detected in content
429Rate limit exceeded — check Retry-After
500Server error — e.g. image upload to storage failed
{ "ok": false, "error": "Paste not found." }

POST Create Paste

https://zpst.net/api.php?action=create

Create a paste. Accepts JSON body or form POST. For image uploads use multipart — see Create with Image.

FieldTypeRequiredDescription
content stringPaste text. Max 4096 KB.
title stringPaste title. Max 255 chars.
language stringSyntax language key (default: plaintext). See languages.
visibilitystringpublic · unlisted (default) · private (auth required)
expiry int Seconds until expiry: 0 never · 300 · 3600 · 21600 · 86400 · 2592000
password stringPassword-protect the paste
burn bool Destroy on first view. Cannot be combined with expiry.
source stringClient identifier: extension or android
curl -X POST "https://zpst.net/api.php?action=create" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your_api_key_here" \
  -d '{
    "content": "print(\"hello\")",
    "title":   "Hello World",
    "language": "python",
    "visibility": "unlisted",
    "expiry": 3600
  }'
{
  "ok": true,
  "slug": "aB3xKq",
  "url": "https://zpst.net/p/aB3xKq",
  "raw_url": "https://zpst.net/raw/aB3xKq",
  "language": "python",
  "visibility": "unlisted",
  "expires_at": "2024-01-01 13:00:00",
  "burn": false,
  "delete_token": "abc123..."
}

delete_token is only returned for guest pastes (no API key). Save it to delete the paste later.

POST Create Paste with Image

https://zpst.net/api.php?action=create

Same endpoint as Create but with Content-Type: multipart/form-data. The paste fields are sent as form parts and the image as a file upload. Supported formats: JPG, PNG, GIF, WEBP. Max size: 5 MB.

curl -X POST "https://zpst.net/api.php?action=create" \
  -H "X-API-Key: your_api_key_here" \
  -F "content=My paste with an image" \
  -F "title=Image Paste" \
  -F "language=plaintext" \
  -F "visibility=unlisted" \
  -F "expiry=0" \
  -F "burn=0" \
  -F "image=@/path/to/photo.jpg"
{
  "ok": true,
  "slug": "cD4yLm",
  "url": "https://zpst.net/p/cD4yLm",
  "image_url": "https://your-bucket.nyc3.cdn.digitaloceanspaces.com/pastes/abc.jpg",
  "language": "plaintext",
  "visibility": "unlisted",
  "burn": false
}

Note: for burn-after-read pastes with images, the image is served to the first viewer then cleaned up automatically. The image_url in the response is valid for at least 60 seconds after viewing.

GET Get Paste

https://zpst.net/api.php?action=get&slug=<slug>

ParamDescription
slug Paste slug ✅
passwordPlaintext password for protected pastes (or use X-Paste-Password header)
# Standard
curl "https://zpst.net/api.php?action=get&slug=aB3xKq"

# Password-protected (prefer header to avoid logging)
curl -H "X-Paste-Password: secret" \
     "https://zpst.net/api.php?action=get&slug=aB3xKq"
{
  "ok": true,
  "slug": "aB3xKq",
  "title": "Hello World",
  "content": "print(\"hello\")",
  "language": "python",
  "visibility": "unlisted",
  "author": "alice",
  "views": 12,
  "burn": false,
  "image_url": null,
  "expires_at": null,
  "created_at": "2024-01-01 12:00:00",
  "url": "https://zpst.net/p/aB3xKq",
  "raw_url": "https://zpst.net/raw/aB3xKq"
}

⚠ Burn-after-read pastes are destroyed immediately on the first get call — there is no preview mode via the API. Use exists to check burn status before fetching.

GET Raw Content

https://zpst.net/api.php?action=raw&slug=<slug>

Returns paste content as text/plain — useful for piping directly to other tools.

curl -s "https://zpst.net/api.php?action=raw&slug=aB3xKq" | python3

curl -s "https://zpst.net/api.php?action=raw&slug=aB3xKq" > script.sh && bash script.sh

GET Check Exists

https://zpst.net/api.php?action=exists&slug=<slug>

Lightweight check that returns whether a slug exists and its basic properties without consuming a burn-after-read paste. Use this to check burn status before deciding to fetch.

curl "https://zpst.net/api.php?action=exists&slug=aB3xKq"
// Found
{ "ok": true, "exists": true, "slug": "aB3xKq",
  "visibility": "unlisted", "password_protected": false, "burn": true }

// Not found or expired
{ "ok": true, "exists": false, "slug": "aB3xKq" }

// Expired
{ "ok": true, "exists": false, "slug": "aB3xKq", "reason": "expired" }

Private pastes return exists: false to unauthenticated callers.

POST Update Paste

https://zpst.net/api.php?action=update&slug=<slug>

Update an existing paste. You must own the paste. Only fields you supply are changed — omitted fields are left as-is. A snapshot of the current state is saved to edit history before each update.

FieldTypeDescription
content stringNew paste text
title stringNew title ("" to clear)
language stringNew syntax language
visibilitystringpublic · unlisted · private
expiry int Seconds from now (0 removes expiry)
password stringNew password. Send "" to remove. Omit entirely to leave unchanged.
curl -X POST "https://zpst.net/api.php?action=update&slug=aB3xKq" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your_api_key_here" \
  -d '{"title": "Updated title", "visibility": "private"}'
{
  "ok": true,
  "updated": true,
  "paste": {
    "slug": "aB3xKq",
    "title": "Updated title",
    "language": "python",
    "visibility": "private",
    "views": 4,
    "expires_at": null,
    "created_at": "2024-01-01 12:00:00",
    "url": "https://zpst.net/p/aB3xKq"
  }
}

GET Delete Paste

https://zpst.net/api.php?action=delete&slug=<slug>

Delete a paste. Requires either API key ownership or the delete_token from the create response (for guest pastes).

# Authenticated owner
curl -H "X-API-Key: your_api_key_here" \
     "https://zpst.net/api.php?action=delete&slug=aB3xKq"

# Guest — use delete_token from create response
curl "https://zpst.net/api.php?action=delete&slug=aB3xKq&token=your_delete_token"
{ "ok": true, "deleted": "aB3xKq" }

Deleting a paste with an attached image also removes the image from storage.

POST Duplicate / Fork

https://zpst.net/api.php?action=duplicate&slug=<slug>

Creates a server-side copy of a paste. The fork inherits the original's content, language, visibility, and password hash — no need to re-fetch content first. Private pastes can only be forked by their owner.

curl -X POST "https://zpst.net/api.php?action=duplicate&slug=aB3xKq" \
  -H "X-API-Key: your_api_key_here"
{
  "ok": true,
  "slug": "eF5zNp",
  "url": "https://zpst.net/p/eF5zNp",
  "forked_from": "aB3xKq",
  "delete_token": null
}

After duplicating you can call update on the new slug to modify its content, title, or password independently of the original.

GET Edit History

https://zpst.net/api.php?action=history&slug=<slug>

Returns the list of past revisions for a paste. A revision is saved automatically before each update. Auth required — you must own the paste (or be an admin).

ParamDefaultDescription
slug Paste slug ✅
limit 20Max revisions to return (max 50)
offset0Pagination offset
curl -H "X-API-Key: your_api_key_here" \
     "https://zpst.net/api.php?action=history&slug=aB3xKq"
{
  "ok": true,
  "slug": "aB3xKq",
  "total": 3,
  "revisions": [
    {
      "id": 42,
      "title": "Old title",
      "language": "python",
      "visibility": "unlisted",
      "revised_by": "alice",
      "revised_at": "2024-01-02 15:30:00"
    }
  ]
}

GET List Pastes

https://zpst.net/api.php?action=list

Lists pastes belonging to the authenticated user. Supports filtering and cursor-based pagination.

ParamDefaultDescription
limit 25 Results per page (max 100)
cursor Opaque cursor from previous response for next page
language Filter by language key e.g. python
visibility Filter: public · unlisted · private
search Filter by title (partial match)
# First page
curl -H "X-API-Key: your_api_key_here" \
     "https://zpst.net/api.php?action=list&limit=25"

# Filter by language
curl -H "X-API-Key: your_api_key_here" \
     "https://zpst.net/api.php?action=list&language=python&visibility=private"

# Next page
curl -H "X-API-Key: your_api_key_here" \
     "https://zpst.net/api.php?action=list&cursor=eyJzbHVnIjoiY..."
{
  "ok": true,
  "pastes": [
    {
      "slug": "aB3xKq",
      "title": "Hello World",
      "language": "python",
      "visibility": "unlisted",
      "views": 12,
      "image_url": "https://...",
      "created_at": "2024-01-01 12:00:00",
      "expires_at": null,
      "url": "https://zpst.net/p/aB3xKq",
      "raw_url": "https://zpst.net/raw/aB3xKq"
    }
  ],
  "total": 142,
  "limit": 25,
  "has_more": true,
  "next_cursor": "eyJzbHVnIjoiY..."
}

Cursors are stable — deleting pastes between pages does not cause duplicates or skipped results. When has_more is false you have reached the last page.

POST Login

https://zpst.net/api.php?action=login

Authenticate with username or email and password. Returns your API key. Rate limited to 10 attempts per 60s per IP. An API key is generated automatically if your account doesn't have one.

curl -X POST "https://zpst.net/api.php?action=login" \
  -H "Content-Type: application/json" \
  -d '{"ident": "alice", "password": "s3cur3pass"}'
{ "ok": true, "username": "alice", "email": "alice@example.com", "api_key": "abc123..." }

POST Register

https://zpst.net/api.php?action=register

Create an account and receive an API key. Rate limited to 5 attempts per hour per IP.

FieldRules
username3–32 characters, letters/numbers/underscores
email Valid email address, unique
passwordMinimum 8 characters
curl -X POST "https://zpst.net/api.php?action=register" \
  -H "Content-Type: application/json" \
  -d '{"username": "alice", "email": "alice@example.com", "password": "s3cur3pass"}'
{ "ok": true, "username": "alice", "email": "alice@example.com", "api_key": "abc123..." }

GET Site Info

https://zpst.net/api.php?action=info

Returns site capabilities, limits, and the full language list. No authentication required. Use this to configure client apps without hardcoding constants.

curl "https://zpst.net/api.php?action=info"
{
  "ok": true,
  "name": "ZPST",
  "version": "1.0.0",
  "url": "https://zpst.net",
  "guest_pastes": true,
  "max_paste_size_kb": 4096,
  "max_image_size_mb": 5,
  "rate_limits": { ... },
  "actions": ["create","get","raw","exists","update","delete","duplicate","history","list","login","register","info","languages"],
  "languages": ["plaintext", "python", "javascript", ...],
  "visibility_options": ["public", "unlisted", "private"],
  "expiry_options": [0, 300, 3600, 21600, 86400, 2592000]
}

GET Languages

https://zpst.net/api.php?action=languages

Returns all 37 supported syntax highlighting languages as key/label pairs. No authentication required.

curl "https://zpst.net/api.php?action=languages"
{
  "ok": true,
  "count": 37,
  "languages": [
    { "key": "plaintext",  "label": "Plain Text" },
    { "key": "python",     "label": "Python" },
    { "key": "javascript", "label": "JavaScript" }
  ]
}

Use the key as the language field when creating or updating pastes. The label is the human-readable display name for your UI.