Skip to main content
On this page

API documentation

Create pastes from a script

Send raw text, get a link, a raw URL, and a one-time delete token back. Pastes created over the API get the same expiry, burn, and password options as the web editor — and the same 1 MB limit and per-client rate limits.
  • 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/pastes

A successful create returns 201:

json
{
  "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

It is shown only in this response and stored only as a hash — it is the only way to delete the paste before it expires.

Optional headers

HeaderValuesNotes
NP-Expiry10m | 1h | 1d | 1w | neverDefault: 1d
NP-Languageauto | <language id>Default: auto-detect
NP-Burn0 | 1Burn after first confirmed read
NP-Password<password>Argon2id access control (not E2E)
bash
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/pastes

Read 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 409 from /raw — the server holds only ciphertext and never presents it as source code.
  • Password-protected pastes need NP-Password: <password> (or Authorization: Bearer <password>), otherwise 401.
  • Burn-after-read pastes need NP-Confirm-Burn: 1, otherwise 409 — reading one destroys it, so a stray curl can never consume it by accident. A consumed paste returns 410 with {"error":"burned"}.
bash
# 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/AbC123xYz012

The JSON endpoint responds with:

json
{
  "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>

bash
curl -X DELETE \
  -H "NP-Delete-Token: 9f2kQ7wLpX4mR8sT2v" \
  https://nullpaste.org/api/v1/pastes/AbC123xYz012

Returns 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

StatusMeaning
200Read or delete succeeded.
201Paste created — JSON with id, url, raw_url, delete_token.
400Empty body, invalid NP-Expiry or NP-Burn, password over 256 characters, missing NP-Delete-Token, or content rejected by the spam/content filter.
401Password-protected paste: send NP-Password (or Authorization: Bearer).
403Cross-origin browser write, wrong delete token, or a content-policy ban.
404No paste with that id.
409Burn-after-read paste needs NP-Confirm-Burn: 1; /raw of an end-to-end encrypted paste.
410Expired or burned paste, or a paste without a delete code.
413Body over the 1 MB (1,048,576 character) limit.
415JSON body — send the raw text with Content-Type: text/plain.
429Rate 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.

bash
# 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 stdin

Rate 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.