📡 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.
| Action | Method | Auth | Description |
|---|---|---|---|
create | POST | Optional | Create a paste (JSON or multipart with image) |
get | GET | Optional | Get paste content and metadata |
raw | GET | Optional | Get raw paste text as text/plain |
exists | GET | Optional | Lightweight existence check |
update | POST | Required | Update fields on an existing paste |
delete | GET | Key or token | Delete a paste |
duplicate | POST | Optional | Fork a paste (copies content, language, password) |
history | GET | Required | List edit revisions for a paste |
list | GET | Required | List your pastes with filtering and cursor pagination |
login | POST | None | Authenticate and retrieve API key |
register | POST | None | Create an account |
info | GET | None | Site capabilities and configuration |
languages | GET | None | Full list of supported syntax languages |
CLI Client — zpst
A bash script wrapping the API. Requires curl and python3.
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:
| Method | Example |
|---|---|
X-API-Key header Preferred | X-API-Key: your_api_key_here |
Authorization header | Authorization: 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:
| Header | Description |
|---|---|
X-RateLimit-Limit | Max requests in the window |
X-RateLimit-Remaining | Requests remaining |
X-RateLimit-Reset | Unix timestamp when the window resets |
Retry-After | Seconds to wait — only present on 429 responses |
| Actions | Limit | Window |
|---|---|---|
create, update, duplicate | 5/IP | 60s |
get, raw, exists | 300/IP | 30s |
login | 10/IP | 60s |
register | 5/IP | 3600s |
| Paste password attempts | 7/IP | 60s |
Errors
| Code | Meaning |
|---|---|
400 | Bad request — missing or invalid parameter |
401 | Authentication required, or wrong paste password |
403 | Forbidden — banned, private paste, or not the owner |
404 | Paste not found |
409 | Conflict — username/email taken, or duplicate content |
410 | Paste has expired |
413 | Content or image exceeds the maximum size |
422 | Rejected — too many URLs detected in content |
429 | Rate limit exceeded — check Retry-After |
500 | Server 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.
| Field | Type | Required | Description |
|---|---|---|---|
content | string | ✅ | Paste text. Max 4096 KB. |
title | string | Paste title. Max 255 chars. | |
language | string | Syntax language key (default: plaintext). See languages. | |
visibility | string | public · unlisted (default) · private (auth required) | |
expiry | int | Seconds until expiry: 0 never · 300 · 3600 · 21600 · 86400 · 2592000 | |
password | string | Password-protect the paste | |
burn | bool | Destroy on first view. Cannot be combined with expiry. | |
source | string | Client 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>
| Param | Description |
|---|---|
slug | Paste slug ✅ |
password | Plaintext 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.
| Field | Type | Description |
|---|---|---|
content | string | New paste text |
title | string | New title ("" to clear) |
language | string | New syntax language |
visibility | string | public · unlisted · private |
expiry | int | Seconds from now (0 removes expiry) |
password | string | New 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).
| Param | Default | Description |
|---|---|---|
slug | — | Paste slug ✅ |
limit | 20 | Max revisions to return (max 50) |
offset | 0 | Pagination 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.
| Param | Default | Description |
|---|---|---|
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.
| Field | Rules |
|---|---|
username | 3–32 characters, letters/numbers/underscores |
email | Valid email address, unique |
password | Minimum 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.