REST API

Wysyłaj pliki, twórz kolekcje i zarządzaj udostępnieniami przez HTTP. Wszystkie odpowiedzi są w formacie JSON; do anonimowych wysyłek nie jest wymagany klucz API.

Wprowadzenie

API storage.to napędza nasze CLI, aplikacja desktopowa, aplikacja do wysyłania w przeglądarce oraz każdy klient zewnętrzny, który chcesz zbudować.

Proces wysyłania składa się z trzech kroków:

  1. Inicjuj — powiedz nam, że chcesz wysłać plik. Zwracamy jeden lub więcej presigned URLi wskazujących na Cloudflare R2.
  2. Wyślij do R2 — :wgraj swoje bajty bezpośrednio na presigned URL(e). Bajty nie przechodzą przez nasze serwery.
  3. Potwierdź — daj nam znać, że wysyłka się zakończyła. Tworzymy rekord File i przekazujemy Ci URL do udostępnienia.

Adres bazowy (Base URL)

https://storage.to/api

Wszystkie endpointy poniżej są względne względem tej bazy. Przykład: POST /upload/init oznacza POST https://storage.to/api/upload/init.

Uwierzytelnianie

Większość endpointów nie wymaga uwierzytelniania. Anonimowe wysyłki to kluczowa funkcja.

Uwierzytelnianie jest opcjonalne i odblokowuje:

  • Wysyłki przypisane do Twojego konta (widoczne w /dashboard)
  • Funkcje premium (pliki na stałe, większa przestrzeń)
  • Zmiany oparte na własności (usuń, ustaw hasło, zmień datę ważności) bez konieczności dopasowania visitor-token

Używamy tokenów typu Laravel Sanctum bearer. Wygeneruj token przez przekazanie OAuth w aplikacji desktopowej lub logowanie w przeglądarce, a następnie wyślij go jako:

Authorization: Bearer <token>

Token gościa

Anonimowi klienci muszą mieć sposób, aby udowodnić, że są właścicielem własnych wysyłek, bez konta. Używamy visitor token — losowego ciągu, który klient generuje raz i ponownie wykorzystuje. Wyślij go wraz z każdą prośbą:

X-Visitor-Token: <random-string>

W wersji web token jest automatycznie zapisywany w ciasteczku visitor_token. CLI zapisuje go w ~/.config/storageto/token (zobacz Dokumentacja CLI).

Dla endpointów modyfikacji (delete, set password, change expiry) własność jest potwierdzana, jeśli albo token odwiedzającego pasuje do lub żądanie pochodzi z tego samego adresu IP, który utworzył plik. Oba mogą zostać utracone (wyczyszczone ciasteczka, zmiany sieci). W przyszłości preferowanym dowodem jest token właściciela.

Token właściciela

Każdy endpoint tworzący zasób (/upload/init multipart, /upload/confirm, /upload/reserve, /collection) zwraca w odpowiedzi owner_token. Token jest podpisanym dowodem własności przypisanym do konkretnego zasobu, niezależnym od Twojego IP ani tokenu odwiedzającego.

Zapisz token razem z ID zasobu i wysyłaj go przy każdej modyfikacji jako:

Authorization: Owner <token>

Albo, jeśli już używasz Authorization: Bearer do uwierzytelnionej sesji, wyślij go jako:

X-Owner-Token: <token>

Serwer akceptuje token właściciela jako ważny dowód własności obok starszego mechanizmu awaryjnego visitor token + IP — klienci, którzy mają token, nadal będą działać po przełączeniu sieci lub wyczyszczeniu ciasteczek, a klienci, którzy go nie mają, będą działać dokładnie jak wcześniej.

Tokeny działają tak długo, jak działa zasób, są bezpieczne do przechowywania i nie wygasają niezależnie. Utracony token oznacza utratę kontroli nad tym zasobem (plik/kolekcja/upload) — traktuj je jak lokalne hasła.

Błędy

Błędy mają spójny format:

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

Najczęstsze kody statusu HTTP:

KodZnaczenie
200OK.
201Utworzono.
400Nieprawidłowe żądanie (np. przekroczono limit rozmiaru kolekcji).
401Wymagane hasło lub hasło jest nieprawidłowe.
403Brak uprawnień (nie jesteś właścicielem zasobu).
404Nie znaleziono zasobu lub wygasł.
422Walidacja nie powiodła się lub obowiązuje ograniczenie planu/limitu.
429Przekroczono limit szybkości lub limit uploadu.
500Błąd serwera. Sprawdź status.

Limity zapytań

Wszystkie limity szybkości są liczone na podstawie IP. Odpowiedź 429 zawiera standardowe nagłówki Retry-After, X-RateLimit-Limit i X-RateLimit-Remaining.

ZakresLimit
Inicjowanie / potwierdzanie / anulowanie uploadu60 / minuta
Zakończenie multipart500 / minuta
Adresy URL części multipart120 / minuta
Inicjowanie / potwierdzanie wsadowe500 / minuta
Pobieranie statusu (plik i kolekcja)120 / minuta
Ustawienia (hasło, wygaśnięcie, maks. liczba pobrań)30 / minuta
Weryfikacja hasła10 / minuta
Tworzenie kolekcji30 / minuta
Zarządzanie (ready, delete)60 / minuta
Upload miniatury120 / minuta
Upload ShareX20 / dzień
Analityka aplikacji / błędy120 i 60 / minuta

Limit uploadu: anonimowi klienci mają dwa limity działające równolegle — 100 GB / 24 h na visitor token i 500 GB / 24 h na IP (limit IP łapie ruch bez tokena oraz sieci współdzielone). Gdy którykolwiek zostanie przekroczony, dostaniesz 429 z szczegółami. To jest wyłącznie limit upload — pobieranie jest nielimitowane i bez ograniczeń (obsługiwane bezpośrednio z podpisanych URL-i R2).

Upload

Trzystopniowy proces uploadu dla każdego pliku, w tym plików większych niż 5 GB (automatycznie multipart). Jeśli potrzebujesz tylko szybkiego uploadu w stylu zrzutu ekranu, zamiast tego zobacz ShareX.

POST /upload/init 60/min

Rozpocznij upload. Dla plików >50 MB odpowiedź to upload multipart (pole type: "multipart"); w przeciwnym razie pojedynczy presigned PUT.

Treść żądania

PoleTypOpis
filenamestring · requiredOryginalna nazwa pliku. Maks. 255 znaków.
content_typestring · requiredTyp MIME.
sizeinteger · requiredRozmiar pliku w bajtach. 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

Zażądaj dodatkowych adresów URL części dla trwającego przesyłania multipart. Używane, gdy /init zwróciło mniej adresów URL niż masz części (albo wygasły).

Treść żądania

PoleTypOpis
upload_idstring · requiredIdentyfikator upload_id z /init.
part_numbersarray<int> · requiredNumery części, dla których pobrać adresy 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

Zakończ przesyłanie multipart na R2, gdy wszystkie części zostaną przesłane.

Treść żądania

PoleTypOpis
upload_idstring · requiredIdentyfikator upload_id z /init.
partsarray · requiredKażdy wpis: { partNumber, etag } z odpowiedzi 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

Anuluj przesyłanie multipart i posprzątaj wszelkie częściowe dane na R2.

Treść żądania

PoleTypOpis
upload_idstring · requiredPrzesyłanie do przerwania.
Request
curl -X POST https://storage.to/api/upload/abort \ -H "Content-Type: application/json" \ -d '{ "upload_id": "01HXYZ..." }'
POST /upload/confirm 60/min

Potwierdź, że przesyłanie jest ukończone. W tym momencie tworzymy rekord File i zwracamy udostępniany adres URL.

Treść żądania

PoleTypOpis
filenamestring · requiredOryginalna nazwa pliku.
sizeinteger · requiredRozmiar pliku w bajtach.
content_typestring · requiredTyp MIME.
r2_keystring · requiredIdentyfikator r2_key z /init.
collection_idstring · optionalDodaj do kolekcji.
crc32integer · optionalSuma kontrolna CRC32 do weryfikacji integralności.
file_idstring(9) · optionalZrealizuj wcześniej utworzony identyfikator pliku zarezerwowane.
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

Zarezerwuj identyfikator pliku i udostępniany adres URL przed, gdy bajty będą gotowe. Przydatne, gdy musisz najpierw udostępnić link, a dopiero potem dokończyć przesyłanie. Własność jest powiązana z tokenem odwiedzającego + IP. Dokończ przesyłanie później za pomocą /upload/init + /upload/confirm, przekazując file_id do potwierdzenia.

Treść żądania

PoleTypOpis
filenamestring · optionalNazwa pliku zastępcza. Domyślnie "Pending".
content_typestring · optionalZastępczy typ MIME.
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

Odpowiednik wsadowy /upload/init, zoptymalizowany pod przeglądarkowy uploader. Inicjuje do 250 plików w jednej rundzie żądań.

Używane wewnętrznie przez przeglądarkowy uploader. Większość klientów powinna preferować pojedynczy /upload/init dla pliku.

POST /upload/confirm-batch 500/min

Odpowiednik wsadowy /upload/confirm. Potwierdza wiele plików w jednej rundzie żądań.

Kolekcje

Kolekcja grupuje wiele plików pod jednym udostępnianym adresem URL (/c/{id}). Do 10 000 plików i łącznie 25 GB.

POST /collection 30/min

Utwórz nową kolekcję. Dodaj pliki później, przekazując collection_id na /upload/confirm.

Treść żądania

PoleTypOpis
expected_file_countinteger · optionalWskazówka do automatycznego oznaczania kolekcji jako gotowej, gdy wszystkie oczekiwane pliki zostaną potwierdzone.
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

Sprawdzaj stan kolekcji. Dodatkowo automatycznie oznacza kolekcję jako gotową, jeśli wszystkie oczekiwane pliki zostały potwierdzone.

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

Oznacz kolekcję jako gotową do pobrania. Zwykle nie jest to potrzebne — kolekcje same stają się gotowe po osiągnięciu expected_file_count.

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

Usuń kolekcję i wszystkie jej pliki.

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

Ustaw hasło dla kolekcji. Wymaga 4–100 znaków.

Treść żądania

PoleTypOpis
passwordstring · required4–100 znaków.
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

Usuń hasło z kolekcji.

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

Sprawdź hasło. Zwraca 200 w razie powodzenia, 401 w przypadku niepoprawnego hasła.

Treść żądania

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

Zmień datę ważności kolekcji.

Treść żądania

PoleTypOpis
daysinteger · optionalOd teraz 1–7 dni. Pomij lub null dla trwałego (tylko premium).
POST /collection/{id}/max-downloads Owner only 30/min

Ustaw limit pobrań (burn-after-N-downloads). Kolekcja usuwa się automatycznie po osiągnięciu limitu.

Treść żądania

PoleTypOpis
max_downloadsinteger · optional1–1000. Musi przekraczać bieżącą liczbę pobrań. null, aby usunąć limit.

Pliki

Wszystkie ustawienia na poziomie pliku (hasło, wygaśnięcie, max-downloads) odzwierciedlają endpointy kolekcji. Tylko właściciel.

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

Sprawdź, czy plik nadal oczekuje na przesłanie.

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

Natychmiast usuń plik.

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

Prześlij miniaturę dla pliku wideo lub obrazu (używane na stronie pobierania). Maks. 2 MB.

Treść żądania

PoleTypOpis
thumbnailimage · requiredPrzesyłanie multipart. Maks. 2 MB.
Response
{ "success": true, "thumbnail_url": "https://..." }
POST /file/{id}/password Owner only 30/min

Ustaw hasło dla pliku. Wymaga 4–100 znaków.

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

Usuń hasło pliku.

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

Zweryfikuj hasło pliku.

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

Zmień datę ważności pliku.

Treść żądania

PoleTypOpis
daysinteger · optionalOd teraz 1–7 dni. Pomij lub null dla trwałego (tylko premium).
POST /file/{id}/max-downloads Owner only 30/min

Ogranicz łączną liczbę pobrań pliku. Usuwa się automatycznie po osiągnięciu limitu.

Upload ShareX

Endpoint do jednorazowego przesyłania — wyślij plik multipart, a w odpowiedzi dostaniesz udostępniany adres URL. Bez zabawy w init/confirm. Idealne dla narzędzi do zrzutów ekranu. Pełna instrukcja konfiguracji na /pl/docs/sharex.

POST /sharex/upload 20/day

Prześlij obraz lub plik bezpośrednio (formularz multipart, pole file). Maks. 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" }

Uwierzytelnianie na komputerze

Dla uwierzytelnionych klientów (np. aplikacji desktopowej) posiadających token Sanctum.

GET /user Bearer token

Zwróć uwierzytelnionego użytkownika.

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

Unieważnij bieżący token dostępu.

Różne

GET /health

Test kondycji. Wysyła ping do bazy danych, magazynu R2 i cache Redis. Zwraca 200, jeśli wszystko działa (zielone), w przeciwnym razie 503.

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

Na żywo: strumień aktywności dla globu na stronie głównej. Buforowane na krawędzi Cloudflare.

GET /bandwidth/status 60/min

Bieżące wykorzystanie limitu uploadu dla wywołującego — używane przez CLI i aplikację desktopową do pokazania pozostałej pojemności. Format odpowiedzi różni się dla użytkowników uwierzytelnionych. Mimo nazwy URL, to śledzi tylko upload bajty; pobrania nie są liczone.

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

Wyślij zdarzenie użycia z poziomu CLI lub aplikacji desktopowej.

Treść żądania

PoleTypOpis
appstring · requireddesktop, cli lub web.
versionstring · optionalWersja klienta.
eventstring · requiredNazwa zdarzenia, np. upload_complete.
contextobject · optionalDodatkowe metadane.
POST /app-errors 60/min

Wyślij raport błędu z poziomu CLI lub aplikacji desktopowej. Usuwanie duplikatów po stronie serwera — maks. 10 takich samych błędów na godzinę.

Treść żądania

PoleTypOpis
appstring · requireddesktop, cli lub web.
typestring · requiredKlasa/typ błędu.
messagestring · requiredKomunikat błędu.
stackstring · optionalŚlad stosu (stack trace).
version, os, os_version, arch, contextvarious · optionalMetadane diagnostyczne.