pastesite API and CLI
The pastesite API lets you create and read pastes from a terminal, script, or CI job. It is the same API the website uses. Browser paste creation is protected by Cloudflare Turnstile; for scripts, create a personal API key instead.
1. Create an API key
- Sign in and open your dashboard.
- Find the API keys section and click Create key.
- Give it a label, choose its permissions and expiry, then copy the key when it appears. It is shown only once.
Keys act as your account, so scope them narrowly. A new key defaults to create and read; add manage only if a script needs to edit or delete pastes. The dashboard offers an expiry of 30 days, 90 days, one year, or never, and the API accepts any whole number from 1 to 3,650 days. An expired key stops working and is deleted the first time it is presented.
| Permission | Allows |
|---|---|
create | POST /api/pastes |
read | Read your own pastes, revisions, folders and profile |
manage | Edit and delete your own pastes, folders and profile (also grants read) |
Do not commit keys to a repository or paste them into a public chat. Revoke a single key, or use Revoke all after a suspected leak.
2. Create a paste with curl
curl -s https://pastesite.com/api/pastes \
-H "Authorization: Bearer ps_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"content": "hello from the terminal",
"title": "Terminal test",
"language": "plaintext",
"visibility": "unlisted"
}'
The response includes the paste ID and shareable links:
{
"ok": true,
"paste": {
"id": "x7Km2Qp9aL",
"title": "Terminal test",
"language": "plaintext",
"visibility": "unlisted",
"created_at": "2026-10-17T08:30:00.000Z",
"views": 0,
"url": "https://pastesite.com/#/p/x7Km2Qp9aL",
"rawUrl": "https://pastesite.com/raw/x7Km2Qp9aL"
}
}
Extract the link with jq:
curl -s https://pastesite.com/api/pastes \
-H "Authorization: Bearer ps_your_api_key" \
-H "Content-Type: application/json" \
-d '{"content":"hello","language":"plaintext","visibility":"unlisted"}' \
| jq -r '.paste.url'
3. Pipe command output into a paste
content is a normal JSON string. Use jq -Rs to wrap stdin as a string:
./deploy.sh 2>&1 \
| jq -Rs '{content: ., title: "deploy log", language: "bash", visibility: "unlisted"}' \
| curl -s https://pastesite.com/api/pastes \
-H "Authorization: Bearer ps_your_api_key" \
-H "Content-Type: application/json" \
-d @- \
| jq -r '.paste.url'
4. Read a paste as plain text
Use the friendly raw URL for pipes and downloads:
curl -s https://pastesite.com/raw/x7Km2Qp9aL
It returns text/plain with no HTML. A password-protected paste needs the password in a header (preferred) or query string:
curl -s https://pastesite.com/raw/x7Km2Qp9aL \
-H "x-paste-password: your-password"
Burn-after-read pastes are deleted after the first successful read — whether that read came from the browser or this endpoint. Expired pastes return a 410; unknown IDs return 404.
Endpoints
| Method | Path | Auth | Purpose |
|---|---|---|---|
| POST | /api/pastes | API key or Turnstile | Create a paste |
| GET | /raw/<id> | None (password if set) | Plain-text paste |
| GET | /api/pastes/<id>/raw | None (password if set) | Same as above |
| POST | /api/pastes/<id>/view | None (password if set) | JSON view used by the site |
| GET | /api/pastes/<id> | Session or key (read) | Your own paste, unlisted included |
| PATCH | /api/pastes/<id> | Session or key (manage) | Edit your own paste |
| DELETE | /api/pastes/<id> | Session or key (manage) | Delete your own paste |
| GET | /api/pastes/<id>/revisions | Session or key (read) | Edit history of your own paste |
| GET | /api/pastes/recent | None | Recent public pastes |
| GET | /api/pastes/trending | None | Trending public pastes from the last 30 days |
| GET | /api/me/pastes | Session or key (read) | Your pastes |
| GET | /api/me/profile | Session or key (read) | Your profile, plus isAdmin |
| PUT | /api/me/profile | Session or key (manage) | Change your username or display name |
| GET/POST | /api/me/api-keys | Session | List or create keys |
| DELETE | /api/me/api-keys/<id> | Session | Revoke one key |
| DELETE | /api/me/api-keys | Session | Revoke every key |
| GET/POST | /api/folders | Key: read / manage | List or create folders |
| PATCH/DELETE | /api/folders/<id> | Key: manage | Rename or delete a folder |
| GET | /api/profiles/<username> | None | Public profile and its public pastes |
| POST | /api/reports | None (Turnstile) | Report a paste for abuse |
| GET | /api/reports | Admin (manage) | List abuse reports |
| PATCH | /api/reports/<id> | Admin (manage) | Resolve or dismiss a report |
Admin routes additionally require the caller's address to be listed in the server's ADMIN_EMAILS variable; the same allowlist decides the isAdmin flag on GET /api/me/profile. API keys cannot manage other API keys — that needs a browser session.
Fields for creating a paste
| Field | Type | Notes |
|---|---|---|
content | string | Required. Up to 100,000 characters. |
title | string | Optional, up to 200 characters. |
language | string | Optional. One of the supported language IDs; defaults to plaintext. |
visibility | string | public or unlisted; defaults to public. |
password | string | Optional. Readers must supply it. |
expiryMinutes | number | Optional. 1 to 525,600 (one year). |
burnAfterRead | boolean | Optional. Deletes on first read. |
customSlug | string | Optional. 3–30 lowercase letters, numbers, hyphens. Reserved words are refused: raw, embed, edit, report, admin, recent, trending, mine, account, verify, u, terms, api, p. |
screenshot | string | Optional base64 data URL, max 500,000 characters. Only png, jpeg, gif and webp are accepted — SVG is refused with a 400 because a data URL can carry active content. |
forkedFrom | string | Optional source paste ID. |
Rate limits and errors
Limits are per network address and apply to API-key requests too:
- 10 pastes created per 10 minutes (30 attempts per 10 minutes, so failed captchas cannot be spammed)
- 300 paste reads per 10 minutes across paste views and
/raw/<id> - 8 password attempts per paste per 15 minutes
- 5 abuse reports per hour
Repeated reads from the same network count as one view for 24 hours, so view counts and the trending list cannot be pumped from one machine. Errors are JSON for API routes and plain text for raw routes:
400— invalid input (empty content, bad or reserved slug, expiry out of range, an unsupported screenshot data URL)401— missing/invalid API key, or a missing paste password403— the key lacks the required permission, you are not the owner, or the route needs admin access404— paste not found409— that custom URL or username is already taken410— paste expired429— rate limit reached
Security notes
- API keys are stored as SHA-256 hashes — salted with
API_KEY_PEPPERwhen the server sets it — and the plaintext key is shown once. - Use one key per machine or script so you can revoke it independently.
- Do not put keys in URLs, screenshots, or public pastes.
- Password-protected pastes are access-controlled, not end-to-end encrypted.
- Raw reads count as a view and can burn a burn-after-read paste.
Frequently asked questions
How do I create an API key?
Sign in, open your dashboard, find the API keys section, and click Create key. Copy the key when it appears — it is shown only once. Revoke it from the same section if it leaks.
Can I create a paste with curl?
Yes. Send a POST request to /api/pastes with an Authorization: Bearer ps_... header and a JSON body containing content. The response includes a shareable url and rawUrl.
Does the raw endpoint work without JavaScript?
Yes. GET /raw/<id> returns text/plain from the server, so curl, wget, and shell pipes work without a browser.
Can I limit what an API key can do?
Yes. Keys carry permissions: create, read, and manage. New keys can create pastes and read your own data; add manage only when a script needs to edit or delete. Keys can expire after 30 days, 90 days, a year, or never, and you can revoke all keys at once if one leaks.
Do API keys bypass the captcha?
Yes. API keys are issued to signed-in accounts and replace the browser Turnstile challenge for scripted creation. Paste creation is still rate-limited by IP.
Why do I get a 401 from /raw/<id>?
The paste is password-protected. Send the password in an x-paste-password header (preferred) or as a ?password= query parameter.