REST API

Lade Dateien hoch, erstelle Sammlungen und verwalte Freigaben über HTTP. Alle Antworten sind JSON; kein API-Schlüssel für anonyme Uploads erforderlich.

Einführung

Die storage.to API treibt unser CLI, Desktop-App, Web-Uploader und jeden Drittanbieter-Client an, den du bauen möchtest.

Der Upload-Ablauf besteht aus drei Schritten:

  1. Initialisieren — Sag uns, dass du eine Datei hochladen möchtest. Wir geben eine oder mehrere vorgezeichnete URLs zurück, die auf Cloudflare R2 zeigen.
  2. Upload zu R2PUT deine Bytes direkt an die vorab signierte(n) URL(s). Die Bytes laufen nicht über unsere Server.
  3. Bestätigen — Sag uns, dass der Upload abgeschlossen ist. Wir erstellen einen File Eintrag und geben dir eine teilbare URL.

Basis-URL

https://storage.to/api

Alle untenstehenden Endpunkte sind relativ zu dieser Basis. Beispiel: POST /upload/init bedeutet POST https://storage.to/api/upload/init.

Authentifizierung

Die meisten Endpunkte erfordern keine Authentifizierung. Anonyme Uploads sind eine Kernfunktion.

Authentifizierung ist optional und schaltet Folgendes frei:

  • Uploads, die deinem Konto zugeordnet sind (sichtbar unter /dashboard)
  • Premium-Funktionen (permanente Dateien, größerer Speicher)
  • Besitzbasierte Änderungen (Löschen, Passwort setzen, Ablauf ändern) ohne Übereinstimmung des Besucher-Tokens

Wir verwenden Laravel Sanctum Bearer-Tokens. Erstelle ein Token über die Desktop-OAuth-Übergabe oder das Web-Login und sende es dann als:

Authorization: Bearer <token>

Besucher-Token

Anonyme Clients benötigen eine Möglichkeit, den Besitz ihrer eigenen Uploads ohne Konto nachzuweisen. Wir verwenden ein Besucher-Token — eine zufällige Zeichenfolge, die der Client einmal generiert und wiederverwendet. Sende es mit jeder Anfrage:

X-Visitor-Token: <random-string>

Im Web wird das Token automatisch im visitor_token Cookie gespeichert. Die CLI speichert es unter ~/.config/storageto/token (siehe CLI-Dokumentation).

Für Änderungs-Endpunkte (Löschen, Passwort setzen, Ablauf ändern) wird der Besitz bestätigt, wenn entweder das Besucher-Token übereinstimmt oder die Anfrage von derselben IP kommt, die die Datei erstellt hat.

Fehler

Fehler folgen einer einheitlichen Struktur:

{
  "success": false,
  "error": "Human-readable message"
}

Übliche HTTP-Statuscodes:

CodeBedeutung
200OK.
201Erstellt.
400Fehlerhafte Anfrage (z. B. Sammlungslimit überschritten).
401Passwort erforderlich oder falsch.
403Nicht autorisiert (nicht der Besitzer der Ressource).
404Ressource nicht gefunden oder abgelaufen.
422Validierung fehlgeschlagen oder Plan-/Kontingentbeschränkung.
429Rate-Limit oder Upload-Kontingent erreicht.
500Serverfehler. Prüfe Status.

Ratenbegrenzungen

Alle Ratenbegrenzungen gelten pro IP. Eine 429-Antwort enthält die Standard-Header Retry-After, X-RateLimit-Limit und X-RateLimit-Remaining.

BereichLimit
Upload starten / bestätigen / abbrechen60 / Minute
Multipart-Abschluss500 / Minute
Multipart-Teil-URLs120 / Minute
Batch starten / bestätigen500 / Minute
Statusabfragen (Datei & Sammlung)120 / Minute
Einstellungen (Passwort, Ablauf, max. Downloads)30 / Minute
Passwortüberprüfung10 / Minute
Sammlung erstellen30 / Minute
Verwalten (bereit, löschen)60 / Minute
Thumbnail-Upload120 / Minute
ShareX-Upload20 / Tag
App-Analysen / Fehler120 und 60 / Minute

Upload-Kontingent: Anonyme Clients haben zwei parallele Limits – 100 GB / 24 Std. pro Besucher-Token und 500 GB / 24 Std. pro IP (das IP-Limit erfasst tokenlosen Traffic und geteilte Netzwerke). Wird eines überschritten, erhältst du einen 429 mit Details. Dies ist nur ein Upload Kontingent – Downloads sind unbegrenzt und ungedrosselt (werden direkt von R2 signierten URLs ausgeliefert).

Hochladen

Der dreistufige Upload-Prozess für jede Datei, auch über 5 GB (automatisch multipart). Für einen schnellen Screenshot-Upload siehe stattdessen ShareX.

POST /upload/init 60/min

Starte einen Upload. Bei Dateien >50 MB enthält die Antwort part_urls für einen Multipart-Upload; ansonsten eine einzelne url.

Request-Body

FeldTypBeschreibung
filenamestring · requiredOriginaldateiname. Maximal 255 Zeichen.
content_typestring · requiredMIME-Typ.
sizeinteger · requiredDateigröße in Bytes. Mindestens 1.
Request
curl -X POST https://storage.to/api/upload/init \ -H "Content-Type: application/json" \ -H "X-Visitor-Token: abc123" \ -d '{ "filename": "report.pdf", "content_type": "application/pdf", "size": 2202009 }'
Response · single upload
{ "success": true, "url": "https://r2.cloudflarestorage.com/...signed...", "r2_key": "uuid-abc123", "upload_id": null, "is_multipart": false }
Response · multipart
{ "success": true, "upload_id": "01HXYZ...", "r2_key": "uuid-abc123", "is_multipart": true, "part_size": 52428800, "part_urls": [ { "partNumber": 1, "url": "https://..." }, { "partNumber": 2, "url": "https://..." } ] }
POST /upload/parts 120/min

Fordere zusätzliche Teil-URLs für einen laufenden Multipart-Upload an. Wird verwendet, wenn /init weniger URLs zurückgab als Teile vorhanden sind (oder diese abgelaufen sind).

Request-Body

FeldTypBeschreibung
upload_idstring · requiredDie upload_id von /init.
part_numbersarray<int> · requiredTeilnummern, für die URLs angefordert werden.
Request
curl -X POST https://storage.to/api/upload/parts \ -H "Content-Type: application/json" \ -d '{ "upload_id": "01HXYZ...", "part_numbers": [3, 4] }'
Response
{ "success": true, "part_urls": [ { "partNumber": 3, "url": "https://..." }, { "partNumber": 4, "url": "https://..." } ] }
POST /upload/complete-multipart 500/min

Schließe einen Multipart-Upload auf R2 ab, sobald alle Teile hochgeladen sind.

Request-Body

FeldTypBeschreibung
upload_idstring · requiredDie upload_id von /init.
partsarray · requiredJeder Eintrag: { partNumber, etag } aus der R2-Antwort.
Request
curl -X POST https://storage.to/api/upload/complete-multipart \ -H "Content-Type: application/json" \ -d '{ "upload_id": "01HXYZ...", "parts": [ { "partNumber": 1, "etag": "\"abc...\"" }, { "partNumber": 2, "etag": "\"def...\"" } ] }'
Response
{ "success": true }
POST /upload/abort 60/min

Brich einen Multipart-Upload ab und bereinige alle Teil-Daten auf R2.

Request-Body

FeldTypBeschreibung
upload_idstring · requiredDer Upload, der abgebrochen werden soll.
Request
curl -X POST https://storage.to/api/upload/abort \ -H "Content-Type: application/json" \ -d '{ "upload_id": "01HXYZ..." }'
POST /upload/confirm 60/min

Bestätige, dass der Upload abgeschlossen ist. Hier erstellen wir den File-Eintrag und geben die teilbare URL zurück.

Request-Body

FeldTypBeschreibung
filenamestring · requiredOriginaldateiname.
sizeinteger · requiredDateigröße in Bytes.
content_typestring · requiredMIME-Typ.
r2_keystring · requiredDie r2_key von /init.
collection_idstring · optionalAn eine Sammlung anhängen.
crc32integer · optionalCRC32-Prüfsumme zur Integritätsprüfung.
file_idstring(9) · optionalErfülle eine zuvor reserviert Datei-ID.
Request
curl -X POST https://storage.to/api/upload/confirm \ -H "Content-Type: application/json" \ -H "X-Visitor-Token: abc123" \ -d '{ "filename": "report.pdf", "size": 2202009, "content_type": "application/pdf", "r2_key": "uuid-abc123" }'
Response
{ "success": true, "file": { "id": "FQxyz1234", "url": "https://storage.to/FQxyz1234", "raw_url": "https://storage.to/r/FQxyz1234", "filename": "report.pdf", "size": 2202009, "human_size": "2.1 MB", "expires_at": "2026-04-15T12:00:00Z" } }
POST /file/reserve 60/min

Reserviere eine Datei-ID und teilbare URL bevor die Bytes bereit sind. Nützlich, wenn du zuerst einen Link ausgeben und den Upload später abschließen möchtest. Die Eigentümerschaft ist an deinen Besuchertoken + IP gebunden. Beende den Upload später mit /upload/init + /upload/confirm, wobei file_id zur Bestätigung übergeben wird.

Request-Body

FeldTypBeschreibung
filenamestring · optionalPlatzhalter-Dateiname. Standard ist "Pending".
content_typestring · optionalPlatzhalter-MIME-Typ.
Request
curl -X POST https://storage.to/api/file/reserve \ -H "X-Visitor-Token: abc123"
Response
{ "success": true, "file": { "id": "FQxyz1234", "url": "https://storage.to/FQxyz1234", "raw_url": "https://storage.to/r/FQxyz1234", "expires_at": "2026-04-12T18:00:00Z" } }
POST /upload/init-batch 500/min

Batch-Äquivalent zu /upload/init, optimiert für den Web-Uploader. Initiiert bis zu 250 Dateien in einer einzigen Runde.

Wird intern vom Web-Uploader verwendet. Die meisten Clients sollten das Single-File-/upload/init bevorzugen.

POST /upload/confirm-batch 500/min

Batch-Äquivalent zu /upload/confirm. Bestätigt viele Dateien in einer einzigen Runde.

Sammlungen

Eine Sammlung fasst mehrere Dateien unter einer einzigen Freigabe-URL (/c/{id}) zusammen. Bis zu 10.000 Dateien und insgesamt 25 GB.

POST /collection 30/min

Erstelle eine neue Sammlung. Füge danach Dateien hinzu, indem du collection_id an /upload/confirm übergibst.

Request-Body

FeldTypBeschreibung
expected_file_countinteger · optionalHinweis zum automatischen Markieren der Sammlung als bereit, sobald alle erwarteten Dateien bestätigt wurden.
Request
curl -X POST https://storage.to/api/collection \ -H "Content-Type: application/json" \ -H "X-Visitor-Token: abc123" \ -d '{ "expected_file_count": 3 }'
Response
{ "success": true, "collection": { "id": "ABC123xyz", "url": "https://storage.to/c/ABC123xyz", "expires_at": "2026-04-15T12:00:00Z" } }
GET /collection/{id}/status 120/min

Abfrage des Status einer Sammlung. Markiert die Sammlung auch automatisch als bereit, wenn alle erwarteten Dateien bestätigt sind.

Request
curl https://storage.to/api/collection/ABC123xyz/status
Response
{ "success": true, "files": [ /* file objects: id, url, filename, size, ... */ ], "is_uploading": false, "file_count": 3, "expected_file_count": 3, "total_size": 6291456, "human_total_size": "6 MB" }
POST /collection/{id}/ready Owner only 60/min

Markiere die Sammlung als bereit zum Download. Normalerweise nicht nötig – Sammlungen werden automatisch bereit, sobald expected_file_count erreicht ist.

DELETE /collection/{id} Owner only 60/min

Lösche eine Sammlung und alle ihre Dateien.

POST /collection/{id}/password Owner only 30/min

Setze ein Passwort für die Sammlung. Erfordert 4–100 Zeichen.

Request-Body

FeldTypBeschreibung
passwordstring · required4–100 Zeichen.
Request
curl -X POST https://storage.to/api/collection/ABC123xyz/password \ -H "X-Visitor-Token: abc123" \ -d '{ "password": "hunter22" }'
DELETE /collection/{id}/password Owner only 30/min

Entferne das Passwort von einer Sammlung.

POST /collection/{id}/verify-password 10/min

Überprüfe ein Passwort. Gibt 200 bei Erfolg zurück, 401 bei falschem Passwort.

Request-Body

FeldTypBeschreibung
passwordstring · required
POST /collection/{id}/expiry Owner only 30/min

Ändere das Ablaufdatum einer Sammlung.

Request-Body

FeldTypBeschreibung
daysinteger · optional1–7 Tage ab jetzt. Weglassen oder null für dauerhaft (nur Premium).
POST /collection/{id}/max-downloads Owner only 30/min

Setze ein Download-Limit (burn-after-N-downloads). Die Sammlung wird automatisch gelöscht, wenn das Limit erreicht ist.

Request-Body

FeldTypBeschreibung
max_downloadsinteger · optional1–1000. Muss die aktuelle Download-Anzahl übersteigen. null entfernt das Limit.

Dateien

Alle dateibezogenen Einstellungen (Passwort, Ablauf, max. Downloads) spiegeln die Sammlung-Endpunkte wider. Nur für Besitzer.

GET /file/{id}/status 120/min

Prüfe, ob eine Datei noch auf den Upload wartet.

Response
{ "pending": false }
DELETE /file/{id} Owner only 60/min

Lösche eine Datei sofort.

POST /file/{id}/thumbnail Owner only 120/min

Lade ein Vorschaubild für eine Video- oder Bilddatei hoch (wird auf der Download-Seite verwendet). Max. 2 MB.

Request-Body

FeldTypBeschreibung
thumbnailimage · requiredMultipart-Upload. Max. 2 MB.
Response
{ "success": true, "thumbnail_url": "https://..." }
POST /file/{id}/password Owner only 30/min

Setze ein Passwort für eine Datei. Erfordert 4–100 Zeichen.

DELETE /file/{id}/password Owner only 30/min

Entferne das Passwort einer Datei.

POST /file/{id}/verify-password 10/min

Überprüfe das Passwort einer Datei.

POST /file/{id}/expiry Owner only 30/min

Ändere das Ablaufdatum einer Datei.

Request-Body

FeldTypBeschreibung
daysinteger · optional1–7 Tage ab jetzt. Weglassen oder null für dauerhaft (nur Premium).
POST /file/{id}/max-downloads Owner only 30/min

Begrenze die Gesamtzahl der Downloads einer Datei. Löscht die Datei automatisch, wenn erreicht.

ShareX-Upload

One-Shot-Upload-Endpunkt — sende eine Multipart-Datei und erhalte eine teilbare URL zurück. Kein Init-/Bestätigungsprozess. Ideal für Screenshot-Tools. Vollständige Anleitung unter /de/docs/sharex.

POST /sharex/upload 20/day

Lade ein Bild oder eine Datei direkt hoch (Multipart-Form, file Feld). Max. 25 MB.

Request
curl -X POST https://storage.to/api/sharex/upload \ -F "[email protected]"
Response
{ "success": true, "url": "https://storage.to/FQxyz1234", "raw_url": "https://storage.to/r/FQxyz1234", "filename": "screenshot.png", "expires_at": "2026-04-15T12:00:00Z" }

Desktop-Authentifizierung

Für authentifizierte Clients (z. B. die Desktop-App) mit einem Sanctum-Token.

GET /user Bearer token

Gibt den authentifizierten Benutzer zurück.

Request
curl https://storage.to/api/user \ -H "Authorization: Bearer <token>"
Response
{ "id": 42, "name": "Ada", "email": "[email protected]", "is_premium": true }
POST /auth/logout Bearer token

Widerrufe das aktuelle Zugriffstoken.

Verschiedenes

GET /health

Health-Check. Prüft Datenbank, R2-Speicher und Redis-Cache. Gibt 200 zurück, wenn alles in Ordnung ist, sonst 503.

Response
{ "status": "healthy", "checks": { "database": "ok", "storage": "ok", "cache": "ok" }, "timestamp": "2026-04-12T12:00:00Z" }
GET /activity

Live-Aktivitätsstream für die Startseiten-Weltkugel. Im Cloudflare-Edge zwischengespeichert.

GET /bandwidth/status 60/min

Aktuelle Upload-Kontingentnutzung des Aufrufers – wird von CLI und Desktop-App genutzt, um die verbleibende Kapazität anzuzeigen. Antwortformat unterscheidet sich bei authentifizierten Nutzern. Trotz des URL-Namens werden nur Upload Bytes erfasst; Downloads werden nicht gezählt.

Response · anonymous
{ "success": true, "authenticated": false, "has_token": true, "limit_bytes": 107374182400, "limit_gb": 100, "used_bytes": 12345678, "used_gb": 0.01, "remaining_bytes": 107361836722, "remaining_gb": 99.99, "window_hours": 24 }
Response · authenticated
{ "success": true, "authenticated": true, "plan": "premium" }
POST /app-analytics 120/min

Sende ein Nutzungsereignis von der CLI oder Desktop-App.

Request-Body

FeldTypBeschreibung
appstring · requireddesktop, cli oder web.
versionstring · optionalClient-Version.
eventstring · requiredEreignisname, z. B. upload_complete.
contextobject · optionalZusätzliche Metadaten.
POST /app-errors 60/min

Sende einen Fehlerbericht von der CLI oder Desktop-App. Serverseitig dedupliziert – max. 10 gleiche Fehler pro Stunde.

Request-Body

FeldTypBeschreibung
appstring · requireddesktop, cli oder web.
typestring · requiredFehlerklasse/-typ.
messagestring · requiredFehlermeldung.
stackstring · optionalStack-Trace.
version, os, os_version, arch, contextvarious · optionalDiagnostische Metadaten.