Drops
A drop is one encrypted envelope stored under a random id. These endpoints create, inspect, open, delete and extend drops, report abuse, and tell a client its current limits. All paths are relative to https://paste.nullnet.sh.
The server never sees a key. Your client builds the envelope as described in envelope format and puts the key only in the link fragment. Times are Unix seconds. Every endpoint accepts an optional X-Null-Pass header with a NULL Premium pass; without one, or with an unusable one, you get the free limits and never an error. Errors are listed on the overview.
The drop model
What the server knows about a drop.
- Name
id- Type
- string
- Description
22 base64url characters (128 random bits). The
/p/<id>part of the share link.
- Name
created_at- Type
- integer
- Description
When the drop was stored.
- Name
expires_at- Type
- integer
- Description
When the drop is deleted. Never extended by reads.
- Name
burn- Type
- boolean
- Description
Burn after reading: deleted on the first open.
- Name
views_left- Type
- integer | null
- Description
Opens left before deletion.
nullwhen unlimited.
- Name
size_bytes- Type
- integer
- Description
Envelope length in bytes.
Create a drop
Uploads an envelope. The body is the raw envelope bytes; send Content-Type: application/octet-stream. No Content-Length is needed: the size cap is counted on the bytes received. The server checks only the version byte, the minimum length and, for version 2, the Argon2id parameter bounds.
The body must arrive within 15 seconds. Premium envelopes over 1 MiB + 16 KiB may take up to 10 minutes.
Query parameters
- Name
ttl- Type
- integer
- Description
Required. Lifetime in seconds, one of your
ttl_presets_secsfrom limits. Free:600,3600,86400. Premium adds604800and2592000.
- Name
burn- Type
- boolean
- Description
Optional, default
false. One open, then the drop is deleted.
- Name
views- Type
- integer
- Description
Optional. Opens before deletion, from 1 to your
max_views(free: 10). Withburn=true, only1is accepted. Omit for unlimited opens.
Request headers
- Name
X-Null-Pass- Type
- string
- Description
Optional premium pass. A drop created with a premium pass is owned by that account and counts against its quota.
- Name
X-Zkbin-Alert-To- Type
- string
- Description
Optional, premium. A mailbox on
mail.nullnet.shthat gets an email when the drop is opened. See open alerts.
Response
201 Created with the drop id, its expiry, the options as stored, and the manage token.
- Name
id- Type
- string
- Description
The drop id.
- Name
expires_at- Type
- integer
- Description
Expiry.
- Name
burn- Type
- boolean
- Description
Whether this is a burn drop.
- Name
views- Type
- integer | null
- Description
The view limit;
1for a burn drop,nullwhen unlimited.
- Name
manage_token- Type
- string
- Description
43 characters. Returned only in this response; the server keeps only its SHA-256. Needed to delete or extend the drop.
Build the share link as https://paste.nullnet.sh/p/<id>#k=<key>, adding &p=1 for a password-protected drop.
Errors
400 invalid_query, 400 invalid_options, 400 invalid_envelope, 400 body_read, 400 invalid_header, 403 tier_required, 408 body_timeout, 413 too_large, 429 quota_exceeded, 429 rate_limited, 503 storage_full.
Request
curl -X POST "https://paste.nullnet.sh/api/drops?ttl=3600&burn=true" \
-H "Content-Type: application/octet-stream" \
--data-binary @envelope.bin
Response
{
"id": "0T4iUVyd4X1YUjpQK-PZVA",
"expires_at": 1792597824,
"burn": true,
"views": 1,
"manage_token": "1sPpm5EGeOu6g0wrlzdrP3eLOy2flm3IMem0Rq0_lxY"
}
Get drop metadata
Returns what the server knows about a drop. Never consumes a view and never returns ciphertext, so it is safe to call when a page loads, for example to warn that the drop burns on open.
A drop can expire or be consumed between meta and open; open is authoritative.
Response
- Name
created_at- Type
- integer
- Description
When the drop was stored.
- Name
expires_at- Type
- integer
- Description
Expiry.
- Name
burn- Type
- boolean
- Description
Whether the next open deletes it.
- Name
views_left- Type
- integer | null
- Description
Opens left;
nullwhen unlimited.
- Name
size_bytes- Type
- integer
- Description
Envelope length in bytes.
Errors
404 not_found, 429 rate_limited.
Request
curl https://paste.nullnet.sh/api/drops/0T4iUVyd4X1YUjpQK-PZVA/meta
Response
{
"created_at": 1792597224,
"expires_at": 1792597824,
"burn": true,
"views_left": 1,
"size_bytes": 94
}
Open a drop
Returns the envelope and consumes one view. The body is the raw envelope (application/octet-stream). Large on-disk envelopes are streamed with a Content-Length.
The view is consumed when the server reads the drop, before delivery is confirmed: a dropped connection can lose a burn drop. For a password-protected drop, fetch the envelope once and retry wrong passwords locally; a retry costs no view.
Response headers
- Name
X-Zkbin-Expires-At- Type
- integer
- Description
Expiry, Unix seconds.
- Name
X-Zkbin-Views-Left- Type
- integer
- Description
Opens left after this one.
0means the drop is now deleted. Absent when unlimited.
Errors
404 not_found, 429 rate_limited, 503 unavailable.
Request
curl -X POST -D - -o envelope.bin \
https://paste.nullnet.sh/api/drops/0T4iUVyd4X1YUjpQK-PZVA/open
Response headers
HTTP/2 200
content-type: application/octet-stream
x-zkbin-expires-at: 1792597824
x-zkbin-views-left: 0
Delete a drop
Deletes a drop before it expires. Requires the drop's manage token as a bearer token: Authorization: Bearer <manage_token> (the scheme is case-sensitive).
Response
204 No Content when deleted.
Errors
401 missing_token (no well-formed bearer token), 403 wrong_token (another drop's token), 404 not_found, 429 rate_limited.
Request
curl -X DELETE https://paste.nullnet.sh/api/drops/0T4iUVyd4X1YUjpQK-PZVA \
-H "Authorization: Bearer 1sPpm5EGeOu6g0wrlzdrP3eLOy2flm3IMem0Rq0_lxY"
Extend a drop
Sets the expiry to now + ttl. Shortening is allowed too. Requires the manage token as for delete, and accepts an optional X-Null-Pass.
ttl must be one of the applicable presets, and the new expiry must be at most created_at plus the longest of them. The applicable presets are your premium ones when your pass owns the drop (it was uploaded with a pass for the same account), and the free ones otherwise. So an anonymous drop never lives longer than 24 hours after creation, and a premium drop at most 30 days.
Query parameters
- Name
ttl- Type
- integer
- Description
Required. New lifetime from now, in seconds.
Response
- Name
expires_at- Type
- integer
- Description
The new expiry.
Errors
400 invalid_query, 400 invalid_options (not a preset, or past the maximum), 401 missing_token, 403 wrong_token, 404 not_found, 429 rate_limited.
Request
curl -X POST "https://paste.nullnet.sh/api/drops/0T4iUVyd4X1YUjpQK-PZVA/extend?ttl=86400" \
-H "Authorization: Bearer 1sPpm5EGeOu6g0wrlzdrP3eLOy2flm3IMem0Rq0_lxY"
Response
{ "expires_at": 1792684224 }
Report a drop
Reports a drop for abuse. Anyone holding the link can. Reports are reviewed by the operator. You can also write to support@nullnet.sh.
The body is JSON (Content-Type: application/json, at most 8 KiB). Unknown members are rejected with 400 invalid_json. There is deliberately no member for the key: never send the #k= part of the link.
The answer is 202 Accepted with an empty body whether or not the drop still exists, so a report reveals nothing meta would not. Reports on one drop are aggregated and stay open for 30 days after the latest one. No client address is stored with a report.
Body
- Name
id- Type
- string
- Description
Required. The drop id from the link (
/p/<id>).
- Name
reason- Type
- string
- Description
Required. One of
illegal,malware,phishing,personal_data,spam,other.
- Name
note- Type
- string
- Description
Optional free text, trimmed, at most 1000 characters. No control characters other than newline and tab.
Errors
400 invalid_json, 400 invalid_report, 429 rate_limited (free: 5 per minute per IP), 503 reports_full.
Request
curl -X POST https://paste.nullnet.sh/api/reports \
-H "Content-Type: application/json" \
-d '{"id":"0T4iUVyd4X1YUjpQK-PZVA","reason":"phishing","note":"fake bank login page"}'
Response
HTTP/2 202
Get limits
What the caller may do right now. Use it instead of hard-coding caps, and send your X-Null-Pass to see premium limits. Counts against the read rate limit.
Response
- Name
tier- Type
- string
- Description
free(anonymous, including any unusable pass) orpremium.
- Name
max_envelope_bytes- Type
- integer
- Description
Upload cap on received envelope bytes. The plaintext may be up to this minus 16 KiB.
- Name
ttl_presets_secs- Type
- integer[]
- Description
The only
ttlvalues create accepts.
- Name
max_views- Type
- integer
- Description
Highest
viewsvalue.
- Name
max_files- Type
- integer
- Description
Entries per bundle. Enforced by the client, since the manifest is encrypted.
- Name
uploads_per_min- Type
- integer
- Description
Upload rate limit: per client IP when free, per account when premium.
- Name
reads_per_min- Type
- integer
- Description
Rate limit for meta, open, delete, extend and limits combined.
- Name
features- Type
- string[]
- Description
Premium features usable now:
dashboard(the vault),open_alerts.
- Name
quota- Type
- object | null
- Description
Premium only:
live_bytes,max_live_bytes,live_drops,max_live_dropsfor the account's live drops.nullwhen free.
- Name
pass_expires_at- Type
- integer | null
- Description
Premium only: when the presented pass lapses.
nullwhen free.
Request
curl https://paste.nullnet.sh/api/limits
Response
{
"tier": "free",
"max_envelope_bytes": 1064960,
"ttl_presets_secs": [600, 3600, 86400],
"max_views": 10,
"max_files": 1,
"uploads_per_min": 10,
"reads_per_min": 60,
"features": [],
"quota": null,
"pass_expires_at": null
}