REST API

Carica file, crea collezioni e gestisci le condivisioni via HTTP. Tutte le risposte sono in formato JSON; per i caricamenti anonimi non è richiesta alcuna chiave API.

Introduzione

La API di storage.to alimenta CLI, app desktop, caricatore web e qualsiasi client di terze parti che vuoi creare.

Il flusso di caricamento è in tre passaggi:

  1. Inizializza — ci dici che vuoi caricare un file. Ti restituiamo uno o più URL presignati che puntano a Cloudflare R2.
  2. Carica su R2 — :inserisci i tuoi byte direttamente nell’URL(i) presigned. I byte non passano dai nostri server.
  3. Conferma — ci dici che il caricamento è terminato. Creiamo un record File e ti forniamo un URL condivisibile.

URL di base

https://storage.to/api

Tutti gli endpoint qui sotto sono relativi a questa base. Esempio: POST /upload/init significa POST https://storage.to/api/upload/init.

Autenticazione

La maggior parte degli endpoint non richiede autenticazione. I caricamenti anonimi sono una funzionalità fondamentale.

L’autenticazione è facoltativa e sblocca:

  • Caricamenti associati al tuo account (visibili su /dashboard)
  • Funzionalità premium (file permanenti, spazio maggiore)
  • Modifiche basate sulla proprietà (elimina, imposta password, cambia scadenza) senza dover corrispondere al visitor-token

Usiamo token bearer Laravel Sanctum. Genera un token tramite il passaggio OAuth del desktop o l’accesso web, poi invialo come:

Authorization: Bearer <token>

Token visitatore

I client anonimi hanno bisogno di un modo per dimostrare la proprietà dei propri caricamenti senza un account. Usiamo un visitor token — una stringa casuale che il client genera una volta e riutilizza. Inviatela con ogni richiesta:

X-Visitor-Token: <random-string>

Sul web, il token viene salvato automaticamente nel cookie visitor_token. La CLI lo salva in ~/.config/storageto/token (vedi Documentazione CLI).

Per gli endpoint di mutazione (elimina, imposta password, cambia scadenza), la proprietà è confermata se oppure il token del visitatore corrisponde o la richiesta proviene dallo stesso IP che ha creato il file. Entrambi possono andare persi (cookie cancellati, cambi di rete). Da ora in poi, la prova preferita è token dell’owner.

Token proprietario

Ogni endpoint che crea risorse (/upload/init multipart, /upload/confirm, /upload/reserve, /collection) restituisce un owner_token nella risposta. Il token è una prova firmata di proprietà legata a quella specifica risorsa, indipendente dal tuo IP o dal token del visitatore.

Salva il token insieme all’ID della risorsa e invialo in qualsiasi mutazione come:

Authorization: Owner <token>

Oppure, se stai già usando Authorization: Bearer per una sessione autenticata, invialo come:

X-Owner-Token: <token>

Il server accetta il token dell’owner come prova valida di proprietà insieme al fallback legacy visitor token + IP — i client che hanno il token continuano a funzionare anche dopo aver cambiato rete o cancellato i cookie, mentre i client che non lo hanno funzionano esattamente come prima.

I token restano validi finché esiste la risorsa, sono sicuri da conservare e non scadono in modo indipendente. Un token perso significa perdere il controllo di quella risorsa (file/collezione/upload) — trattali come password locali.

Errori

Gli errori seguono una struttura coerente:

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

Codici di stato HTTP comuni:

CodiceSignificato
200OK.
201Creato.
400Richiesta non valida (es. limite di dimensione della collezione superato).
401Password richiesta o non corretta.
403Non autorizzato (non sei il proprietario della risorsa).
404Risorsa non trovata o scaduta.
422Validazione non riuscita, oppure restrizione di piano/quota.
429Limite di rate o quota di upload superata.
500Errore del server. Controlla stato.

Limiti di velocità

Tutti i limiti di rate sono per IP. Una risposta 429 include gli header standard Retry-After, X-RateLimit-Limit e X-RateLimit-Remaining.

AmbitoLimite
Avvio / conferma / annullamento upload60 / minuto
Completamento multipart500 / minuto
URL delle parti multipart120 / minuto
Avvio / conferma batch500 / minuto
Verifiche dello stato (file e collezione)120 / minuto
Impostazioni (password, scadenza, max-download)30 / minuto
Verifica password10 / minuto
Creazione collezione30 / minuto
Gestione (pronto, elimina)60 / minuto
Caricamento thumbnail120 / minuto
Upload con ShareX20 / giorno
Analytics dell’app / errori120 e 60 / minuto

Quota di upload: i client anonimi hanno due limiti che girano in parallelo — 100 GB / 24 h per visitor token e 500 GB / 24 h per IP (il limite IP intercetta il traffico senza token e le reti condivise). Quando uno dei due viene superato riceverai un 429 con i dettagli. È una quota solo upload — i download sono illimitati e senza limitazioni (serviti direttamente da URL firmati R2).

Carica

Il flusso di upload in tre passaggi per qualsiasi file, inclusi quelli oltre 5 GB (multipart in automatico). Se ti serve solo un upload rapido in stile screenshot, guarda invece ShareX.

POST /upload/init 60/min

Avvia un upload. Per i file >50 MB la risposta è un upload multipart (campo type: "multipart"); altrimenti un singolo PUT presigned.

Corpo della richiesta

CampoTipoDescrizione
filenamestring · requiredNome file originale. Max 255 caratteri.
content_typestring · requiredTipo MIME.
sizeinteger · requiredDimensione del file in byte. Min 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, "type": "single", "upload_url": "https://r2.cloudflarestorage.com/...signed...", "headers": { "Host": ["..."] }, "r2_key": "uuid-abc123" }
Response · multipart
{ "success": true, "type": "multipart", "upload_id": "01HXYZ...", "r2_key": "uuid-abc123", "part_size": 33554432, "total_parts": 4, "initial_urls": { "1": "https://...", "2": "https://..." }, "owner_token": "owner_v1_..." }
POST /upload/parts Owner only 120/min

Richiedi URL aggiuntivi per le parti di un upload multipart in corso. Usato quando /init ha restituito meno URL di quante parti hai (oppure sono scaduti).

Corpo della richiesta

CampoTipoDescrizione
upload_idstring · requiredL'upload_id da /init.
part_numbersarray<int> · requiredNumeri delle parti per cui ottenere gli URL.
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 Owner only 500/min

Completa un upload multipart su R2 una volta caricate tutte le parti.

Corpo della richiesta

CampoTipoDescrizione
upload_idstring · requiredL'upload_id da /init.
partsarray · requiredOgni voce: { partNumber, etag } dalla risposta di R2.
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 Owner only 60/min

Annulla un upload multipart e ripulisci eventuali dati parziali su R2.

Corpo della richiesta

CampoTipoDescrizione
upload_idstring · requiredL'upload da annullare.
Request
curl -X POST https://storage.to/api/upload/abort \ -H "Content-Type: application/json" \ -d '{ "upload_id": "01HXYZ..." }'
POST /upload/confirm 60/min

Conferma che l'upload è completato. È qui che creiamo il record File e restituiamo l'URL condivisibile.

Corpo della richiesta

CampoTipoDescrizione
filenamestring · requiredNome file originale.
sizeinteger · requiredDimensione del file in byte.
content_typestring · requiredTipo MIME.
r2_keystring · requiredL'r2_key da /init.
collection_idstring · optionalAggiungi a una raccolta.
crc32integer · optionalChecksum CRC32 per la verifica dell'integrità.
file_idstring(9) · optionalCompleta un ID file precedentemente riservato.
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" }, "owner_token": "owner_v1_..." }
POST /file/reserve 60/min

Riserva un ID file e un URL condivisibile prima che i byte siano pronti. Utile quando devi consegnare prima un link e completare l'upload dopo. La proprietà è legata al tuo token visitatore + IP. Completa l'upload più tardi con /upload/init + /upload/confirm, passando file_id per la conferma.

Corpo della richiesta

CampoTipoDescrizione
filenamestring · optionalNome file segnaposto. Impostazione predefinita: "Pending".
content_typestring · optionalTipo MIME segnaposto.
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" }, "owner_token": "owner_v1_..." }
POST /upload/init-batch 500/min

Equivalente batch di /upload/init, ottimizzato per l'uploader web. Avvia fino a 250 file in un solo round-trip.

Usato internamente dall'uploader web. La maggior parte dei client dovrebbe preferire /upload/init per singolo file.

POST /upload/confirm-batch 500/min

Equivalente batch di /upload/confirm. Conferma molti file in un solo round-trip.

Raccolte

Una raccolta raggruppa più file sotto un unico URL di condivisione (/c/{id}). Fino a 10.000 file e 25 GB totali.

POST /collection 30/min

Crea una nuova raccolta. Aggiungi i file in seguito passando collection_id su /upload/confirm.

Corpo della richiesta

CampoTipoDescrizione
expected_file_countinteger · optionalSuggerimento per contrassegnare automaticamente la raccolta come pronta una volta che tutti i file previsti sono stati confermati.
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" }, "owner_token": "owner_v1_..." }
GET /collection/{id}/status 120/min

Controlla lo stato di una raccolta. Contrassegna automaticamente la raccolta come pronta anche se tutti i file previsti sono stati confermati.

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

Segna la raccolta come pronta per il download. Di solito non serve: le raccolte diventano automaticamente pronte quando viene raggiunto expected_file_count.

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

Elimina una raccolta e tutti i suoi file.

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

Imposta una password sulla raccolta. Richiede 4–100 caratteri.

Corpo della richiesta

CampoTipoDescrizione
passwordstring · required4–100 caratteri.
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

Rimuovi la password da una raccolta.

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

Verifica una password. Restituisce 200 in caso di successo, 401 se la password è errata.

Corpo della richiesta

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

Cambia la scadenza di una raccolta.

Corpo della richiesta

CampoTipoDescrizione
daysinteger · optionalDa 1 a 7 giorni da oggi. Ommetti o null per la durata permanente (solo premium).
POST /collection/{id}/max-downloads Owner only 30/min

Imposta un limite di download (burn-after-N-downloads). La raccolta viene eliminata automaticamente quando viene raggiunto il limite.

Corpo della richiesta

CampoTipoDescrizione
max_downloadsinteger · optional1–1000. Deve superare il numero di download attuale. null per rimuovere il limite.

File

Tutte le impostazioni a livello file (password, scadenza, max-downloads) rispecchiano gli endpoint della raccolta. Solo il proprietario.

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

Controlla se un file è ancora in attesa del caricamento.

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

Elimina un file immediatamente.

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

Carica un'immagine thumbnail per un file video o immagine (usata nella pagina di download). Max 2 MB.

Corpo della richiesta

CampoTipoDescrizione
thumbnailimage · requiredUpload multipart. Max 2 MB.
Response
{ "success": true, "thumbnail_url": "https://..." }
POST /file/{id}/password Owner only 30/min

Imposta una password su un file. Richiede 4–100 caratteri.

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

Rimuovi la password di un file.

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

Verifica la password di un file.

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

Cambia la scadenza di un file.

Corpo della richiesta

CampoTipoDescrizione
daysinteger · optionalDa 1 a 7 giorni da oggi. Ommetti o null per la durata permanente (solo premium).
POST /file/{id}/max-downloads Owner only 30/min

Limita il numero totale di download di un file. Si elimina automaticamente quando viene raggiunto il limite.

Upload con ShareX

Endpoint di upload one-shot — invia un file multipart e ricevi indietro un URL condivisibile. Niente passaggi init/confirm. Ideale per strumenti di screenshot. Guida completa alla configurazione su /it/docs/sharex.

POST /sharex/upload 20/day

Carica direttamente un'immagine o un file (form multipart, campo file). 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" }

Autenticazione desktop

Per client autenticati (ad es. l'app desktop) che hanno un token Sanctum.

GET /user Bearer token

Restituisci l’utente autenticato.

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

Revoca il token di accesso corrente.

Varie

GET /health

Controllo di salute. Fa ping al database, allo storage R2 e alla cache Redis. Restituisce 200 se tutto è verde, 503 in caso contrario.

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

Flusso live delle attività per il globo della home. Memorizzato nella cache ai margini di Cloudflare.

GET /bandwidth/status 60/min

Utilizzo attuale della quota di upload per il chiamante — usato da CLI e app desktop per mostrare la capacità residua. La struttura della risposta cambia per gli utenti autenticati. Nonostante il nome dell’URL, tiene traccia solo di upload byte; i download non vengono conteggiati.

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

Invia un evento di utilizzo dalla CLI o dall’app desktop.

Corpo della richiesta

CampoTipoDescrizione
appstring · requireddesktop, cli o web.
versionstring · optionalVersione del client.
eventstring · requiredNome dell’evento, ad es. upload_complete.
contextobject · optionalMetadati extra.
POST /app-errors 60/min

Invia una segnalazione di errore dalla CLI o dall’app desktop. Deduplicata lato server — massimo 10 dello stesso errore per ora.

Corpo della richiesta

CampoTipoDescrizione
appstring · requireddesktop, cli o web.
typestring · requiredClasse/tipo di errore.
messagestring · requiredMessaggio di errore.
stackstring · optionalStack trace.
version, os, os_version, arch, contextvarious · optionalMetadati diagnostici.