Zum Hauptinhalt springen

REST-API

MSK Paste bringt eine vollständige JSON-REST-API mit. Alles, was die Weboberfläche kann, geht auch programmatisch, praktisch für Uploads von der Kommandozeile, für Skripte, CI-Pipelines und Editor-Anbindungen.

Basis-URL: https://paste.msk-scripts.de/api, bei einer eigenen Instanz entsprechend deine Domain.


Authentifizierung

Es gibt keine. Alle Endpunkte sind öffentlich. Schreibende Aufrufe (anlegen und löschen) sind über eine Ratenbegrenzung abgesichert, das Löschen zusätzlich über einen einmaligen, zufälligen Lösch-Token.


Aufbau einer Fehlermeldung

Alle Fehler haben dieselbe Form:

{
"error": "Lesbare Meldung",
"details": {
"feldName": ["Validierungsfehler 1", "Validierungsfehler 2"]
}
}

details gibt es nur bei 400 Bad Request, also bei fehlgeschlagener Validierung.

StatusBedeutung
400Validierung fehlgeschlagen, siehe details
401Falsches Passwort (bei /verify)
403Passwort nötig (bei /api/pastes/:id)
404Paste gibt es nicht, ist abgelaufen oder wurde nach dem Lesen gelöscht
409Die eigene ID ist bereits vergeben
413Inhalt größer als 1 MB
429Ratenbegrenzung erreicht, siehe Header Retry-After

POST /api/pastes, einen Paste anlegen

Anfrage

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

{
"content": "console.log('hello world')",
"title": "Mein Schnipsel",
"language": "javascript",
"expiresIn": "1w",
"password": "optional",
"burnAfterRead": false,
"customId": "mein-schnipsel"
}

Die Felder

FeldTypPflichtHinweise
contentstringja1 Zeichen bis 1 MB
titlestringneinhöchstens 100 Zeichen
languagestringneinStandard "plaintext". Muss aus der Liste der unterstützten Sprachen stammen.
expiresInstringja"10min", "1h", "1d", "1w", "1mo", "1y"
passwordstringnein1 bis 128 Zeichen
burnAfterReadbooleanneinStandard false
customIdstringnein4 bis 32 Zeichen, [a-zA-Z0-9_-]

Antwort (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
}
warnung

Der deleteToken ist der einzige Weg, den Paste später zu löschen. Sichere ihn. Er liegt nirgends, wo du ihn abrufen könntest.

Beispiel

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, einen Paste abrufen

Verhalten

  • Ist der Paste passwortgeschützt, kommt 403 mit { "passwordRequired": true }.
  • Ist er abgelaufen oder nach dem Lesen gelöscht, kommt 404.
  • Sonst kommt der Inhalt, view_count wird erhöht, und bei „nach dem Lesen löschen" verschwindet der Paste in derselben Transaktion.

Antwort

{
"pasteId": "X7q9bA2k",
"title": "Mein Schnipsel",
"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
}

Beispiel

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

POST /api/pastes/:id/verify, einen geschützten Paste freischalten

Anfrage

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

{ "password": "geheim" }

Antwort

200 OK liefert dieselben Daten wie GET /api/pastes/:id, dazu ein Feld highlightedHtml zum direkten Anzeigen.

Bei falschem Passwort kommt 401 Unauthorized. Fehlversuche zählen nicht im Aufrufzähler mit und lösen kein Löschen nach dem Lesen aus.

Beispiel

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

DELETE /api/pastes/:id, einen Paste löschen

Braucht einen gültigen Lösch-Token als Query-Parameter.

Anfrage

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

Antwort

Bei Erfolg 204 No Content. Ist Paste oder Token ungültig, kommt 404. Die API unterscheidet das bewusst nicht, damit sich Tokens nicht durch Ausprobieren finden lassen.

Beispiel

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

GET /api/stats, globale Statistik

Liefert die anonymen Gesamtzahlen, die auch auf der Seite /stats stehen.

Antwort

{
"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 }
]
}

Ratenbegrenzung

Der Endpunkt zum Anlegen ist standardmäßig auf 10 Anfragen pro Stunde und IP-Hash begrenzt. Ist das erreicht:

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

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

Retry-After steht in Sekunden. Bei einer eigenen Instanz änderst du die Grenze über RATE_LIMIT_CREATE_PER_HOUR.


Beispiel: Upload von der Kommandozeile

Eine knappe Bash-Funktion, die eine Datei hochlädt:

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'
}

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

Beispiel: 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)

Beispiel: 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"])