On this page
API documentation
Create pastes from a script
- No API keyNo account or token needed
- Plain text inBody is the paste itself
- Same limits1 MB per paste, per-client rate limits
Create a paste
POST/api/v1/pastes
The request body is the paste text itself, sent with Content-Type: text/plain — JSON bodies are rejected with 415. Browser requests from other sites are refused, so call it from a terminal, script or server.
curl --data-binary @file.txt \
-H "Content-Type: text/plain" \
https://nullpaste.org/api/v1/pastesA successful create returns 201:
{
"id": "AbC123xYz012",
"url": "https://nullpaste.org/AbC123xYz012",
"raw_url": "https://nullpaste.org/raw/AbC123xYz012",
"delete_token": "9f2kQ7wLpX4mR8sT2v",
"expires_at": "2026-08-31T06:00:00.000Z",
"burn": false,
"encrypted": false
}Save delete_token
Optional headers
| Header | Values | Notes |
|---|---|---|
| NP-Expiry | 10m | 1h | 1d | 1w | never | Default: 1d |
| NP-Language | auto | <language id> | Default: auto-detect |
| NP-Burn | 0 | 1 | Burn after first confirmed read |
| NP-Password | <password> | Argon2id access control (not E2E) |
curl --data-binary @secrets.env \
-H "Content-Type: text/plain" \
-H "NP-Expiry: 10m" \
-H "NP-Burn: 1" \
-H "NP-Password: hunter2" \
https://nullpaste.org/api/v1/pastesRead a paste
GET/raw/<id>
GET/api/v1/pastes/<id>
/raw/<id> returns the paste as text/plain; /api/v1/pastes/<id> returns JSON with content plus metadata. Both follow the same rules:
- Plaintext only: an end-to-end encrypted paste returns
409from/raw— the server holds only ciphertext and never presents it as source code. - Password-protected pastes need
NP-Password: <password>(orAuthorization: Bearer <password>), otherwise401. - Burn-after-read pastes need
NP-Confirm-Burn: 1, otherwise409— reading one destroys it, so a stray curl can never consume it by accident. A consumed paste returns410with{"error":"burned"}.
# plain paste
curl https://nullpaste.org/raw/AbC123xYz012
# password-protected paste
curl -H "NP-Password: hunter2" https://nullpaste.org/raw/AbC123xYz012
# burn-after-read paste — reading destroys it, so confirmation is explicit
curl -H "NP-Confirm-Burn: 1" https://nullpaste.org/raw/AbC123xYz012The JSON endpoint responds with:
{
"id": "AbC123xYz012",
"content": "hello world",
"language": "text",
"created_at": "2026-08-30T06:00:00.000Z",
"expires_at": "2026-08-31T06:00:00.000Z",
"burn": false,
"encrypted": false,
"views": 1
}Delete a paste
DELETE/api/v1/pastes/<id>
curl -X DELETE \
-H "NP-Delete-Token: 9f2kQ7wLpX4mR8sT2v" \
https://nullpaste.org/api/v1/pastes/AbC123xYz012Returns 200 on success. The token is single-use by construction: deletion removes the paste and its token hash, so it cannot be replayed.
Response codes
| Status | Meaning |
|---|---|
| 200 | Read or delete succeeded. |
| 201 | Paste created — JSON with id, url, raw_url, delete_token. |
| 400 | Empty body, invalid NP-Expiry or NP-Burn, password over 256 characters, missing NP-Delete-Token, or content rejected by the spam/content filter. |
| 401 | Password-protected paste: send NP-Password (or Authorization: Bearer). |
| 403 | Cross-origin browser write, wrong delete token, or a content-policy ban. |
| 404 | No paste with that id. |
| 409 | Burn-after-read paste needs NP-Confirm-Burn: 1; /raw of an end-to-end encrypted paste. |
| 410 | Expired or burned paste, or a paste without a delete code. |
| 413 | Body over the 1 MB (1,048,576 character) limit. |
| 415 | JSON body — send the raw text with Content-Type: text/plain. |
| 429 | Rate limited — wait for the retry-after header. |
Error responses are JSON with an error code and, where useful, a detail message.
Encrypt client-side
API-created pastes are plaintext on the server unless your client encrypts them first. To make a paste the server cannot read, encrypt to the same versioned format the web editor uses — AES-256-GCM, fresh 12-byte IV, enc1:<b64url iv>:<b64url ciphertext> — and put the key in the URL fragment yourself. The fragment never reaches the server. Full spec on the security page.
// Node 20+: E2E-encrypt before upload (same scheme the browser uses)
export const { webcrypto: crypto } = require("node:crypto");
const b64url = (buf) => Buffer.from(buf).toString("base64url");
async function encryptAndPaste(plaintext) {
const key = await crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 }, true, ["encrypt"]);
const iv = crypto.getRandomValues(new Uint8Array(12));
const ct = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv }, key, new TextEncoder().encode(plaintext));
const body = `enc1:${b64url(iv)}:${b64url(ct)}`;
const res = await fetch("https://nullpaste.org/api/v1/pastes", {
method: "POST",
headers: { "Content-Type": "text/plain" },
body,
});
const paste = await res.json();
const keyB64 = b64url(await crypto.subtle.exportKey("raw", key));
return `${paste.url}#key=${keyB64}`; // full share link — key never uploaded
}Shell helper
Add an npaste function to your shell to paste files or command output.
# Add to ~/.bashrc or ~/.zshrc
npaste() {
curl -s --data-binary @"${1:--}" \
-H "Content-Type: text/plain" \
https://nullpaste.org/api/v1/pastes
}
# Usage:
# npaste file.txt — paste a file
# some-command | npaste — paste from stdinRate limits & abuse
Creation, reads, and deletes are rate-limited per client. The identifier is an HMAC-SHA256 hash derived from your IP — the raw IP is never stored or logged. Exceeding a limit returns 429 with a retry-after header. Report abusive pastes via the Report button on any paste page or the contact page.