Skip to main content

REST API

MSK Paste exposes a full JSON REST API. All features available in the web UI are also available programmatically — useful for CLI uploads, scripts, CI pipelines, and editor integrations.

Base URL: https://paste.msk-scripts.de/api (or your self-hosted domain).


Authentication

There is no authentication. All endpoints are public. Write operations (create / delete) are protected by rate limiting and, in the case of deletion, a one-time random delete token.


Error envelope

All errors share the same shape:

{
"error": "Human-readable message",
"details": {
"fieldName": ["validation error 1", "validation error 2"]
}
}

details is only present on 400 Bad Request (validation failures).

StatusMeaning
400Validation failed — see details
401Wrong password (on /verify)
403Password required (on /api/pastes/:id)
404Paste does not exist, has expired, or was burned
409Custom ID is already in use
413Content exceeds 1 MB
429Rate limit exceeded — see Retry-After header

POST /api/pastes — create a paste

Request

POST /api/pastes HTTP/1.1
Content-Type: application/json

{
"content": "console.log('hello world')",
"title": "My snippet",
"language": "javascript",
"expiresIn": "1w",
"password": "optional",
"burnAfterRead": false,
"customId": "my-snippet"
}

Field reference

FieldTypeRequiredNotes
contentstringyes1 character to 1 MB
titlestringnoMax 100 characters
languagestringnoDefaults to "plaintext". Must be in the supported list.
expiresInstringyes"10min", "1h", "1d", "1w", "1mo", "1y"
passwordstringno1–128 characters
burnAfterReadbooleannoDefaults to false
customIdstringno4–32 chars, [a-zA-Z0-9_-]

Response (201 Created)

{
"pasteId": "X7q9bA2k",
"url": "https://paste.msk-scripts.de/X7q9bA2k",
"rawUrl": "https://paste.msk-scripts.de/raw/X7q9bA2k",
"deleteToken": "dk_a7c4f2e1b9d8...",
"expiresAt": "2026-05-20T16:00:00.000Z",
"hasPassword": false,
"burnAfterRead": false
}
warning

The deleteToken is the only way to delete the paste later. Save it. It is not stored anywhere you can retrieve it.

Example

curl -X POST https://paste.msk-scripts.de/api/pastes \
-H "Content-Type: application/json" \
-d '{
"content": "print(\"hello\")",
"language": "python",
"expiresIn": "1d"
}'

GET /api/pastes/:id — fetch a paste

Behaviour

  • If the paste is password-protected, returns 403 with { "passwordRequired": true }.
  • If the paste has expired or been burned, returns 404.
  • Otherwise returns the content, increments view_count, and (for burn-after-read pastes) deletes the paste in the same transaction.

Response

{
"pasteId": "X7q9bA2k",
"title": "My snippet",
"content": "console.log('hello world')",
"language": "javascript",
"createdAt": "2026-05-13T16:00:00.000Z",
"expiresAt": "2026-05-20T16:00:00.000Z",
"viewCount": 1,
"burnAfterRead": false,
"sizeBytes": 27
}

Example

curl https://paste.msk-scripts.de/api/pastes/X7q9bA2k

POST /api/pastes/:id/verify — unlock a password-protected paste

Request

POST /api/pastes/X7q9bA2k/verify HTTP/1.1
Content-Type: application/json

{ "password": "secret" }

Response

200 OK returns the same payload as GET /api/pastes/:id plus a highlightedHtml field for direct rendering.

401 Unauthorized is returned on wrong password. Failed attempts do not count against the view counter and do not trigger burn-after-read.

Example

curl -X POST https://paste.msk-scripts.de/api/pastes/X7q9bA2k/verify \
-H "Content-Type: application/json" \
-d '{"password":"hunter2"}'

DELETE /api/pastes/:id — delete a paste

Requires a valid delete token as a query parameter.

Request

DELETE /api/pastes/X7q9bA2k?token=dk_a7c4f2e1b9d8... HTTP/1.1

Response

204 No Content on success. 404 if the paste or token is invalid (the API does not differentiate, to avoid token probing).

Example

curl -X DELETE "https://paste.msk-scripts.de/api/pastes/X7q9bA2k?token=dk_a7c4f2e1b9d8..."

GET /api/stats — global statistics

Returns anonymous aggregate numbers shown on the /stats page.

Response

{
"totalPastes": 1234,
"pastesToday": 42,
"pastesThisWeek": 187,
"topLanguages": [
{ "language": "javascript", "count": 320 },
{ "language": "lua", "count": 211 },
{ "language": "python", "count": 198 },
{ "language": "plaintext", "count": 156 },
{ "language": "json", "count": 99 }
]
}

Rate limiting

The create endpoint is limited to 10 requests per hour per IP hash by default. When exceeded:

HTTP/1.1 429 Too Many Requests
Retry-After: 1742
Content-Type: application/json

{ "error": "Rate limit exceeded. Please try again later." }

Retry-After is in seconds. Self-hosted instances can change the limit via RATE_LIMIT_CREATE_PER_HOUR.


Example: command-line uploader

A minimal Bash function to upload a file:

mskpaste() {
local file="$1"
local lang="${2:-plaintext}"
curl -sS -X POST https://paste.msk-scripts.de/api/pastes \
-H "Content-Type: application/json" \
-d "$(jq -n \
--arg c "$(cat "$file")" \
--arg l "$lang" \
'{content: $c, language: $l, expiresIn: "1w"}')" \
| jq -r '.url'
}

# Usage:
mskpaste script.lua lua
mskpaste server.log

Example: Node.js

const res = await fetch('https://paste.msk-scripts.de/api/pastes', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
content: 'console.log("hi")',
language: 'javascript',
expiresIn: '1d',
}),
})
const paste = await res.json()
console.log(paste.url)

Example: Python

import requests

r = requests.post(
"https://paste.msk-scripts.de/api/pastes",
json={
"content": "print('hi')",
"language": "python",
"expiresIn": "1h",
},
)
print(r.json()["url"])