REST API

Envie arquivos, crie coleções e gerencie compartilhamentos via HTTP. Todas as respostas são JSON; não é necessária chave de API para uploads anônimos.

Introdução

A API do storage.to alimenta nosso CLI, app desktop, envio web e qualquer cliente de terceiros que você queira criar.

O fluxo de upload tem três etapas:

  1. Iniciar — diga que quer enviar um arquivo. Retornamos uma ou mais URLs pré-assinadas apontando para o Cloudflare R2.
  2. Enviar para o R2PUT seus bytes diretamente para a(s) URL(s) pré-assinada(s). Os bytes não passam pelos nossos servidores.
  3. Confirmar — diga que o upload terminou. Criamos um registro File e fornecemos uma URL para compartilhar.

URL base

https://storage.to/api

Todos os endpoints abaixo são relativos a essa base. Exemplo: POST /upload/init significa POST https://storage.to/api/upload/init.

Autenticação

A maioria dos endpoints não requer autenticação. Uploads anônimos são um recurso principal.

A autenticação é opcional e desbloqueia:

  • Uploads vinculados à sua conta (visíveis em /dashboard)
  • Recursos premium (arquivos permanentes, armazenamento maior)
  • Alterações baseadas na propriedade (excluir, definir senha, alterar expiração) sem precisar da correspondência do token do visitante

Usamos tokens bearer Laravel Sanctum. Gere um token via OAuth no desktop ou login web, depois envie assim:

Authorization: Bearer <token>

Token do visitante

Clientes anônimos precisam provar a propriedade dos próprios uploads sem conta. Usamos um token do visitante — uma string aleatória que o cliente gera uma vez e reutiliza. Envie em toda requisição:

X-Visitor-Token: <random-string>

Na web, o token é armazenado automaticamente no cookie visitor_token. O CLI o guarda em ~/.config/storageto/token (veja Docs do CLI).

Para endpoints de alteração (excluir, definir senha, alterar expiração), a propriedade é confirmada se ou o token do visitante corresponder ou a requisição vier do mesmo IP que criou o arquivo.

Erros

Erros seguem um formato consistente:

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

Códigos HTTP comuns:

CódigoSignificado
200OK.
201Criado.
400Requisição inválida (ex: limite de tamanho da coleção excedido).
401Senha requerida ou incorreta.
403Não autorizado (não é o dono do recurso).
404Recurso não encontrado ou expirado.
422Validação falhou, ou restrição de plano/cota.
429Limite de taxa ou cota de upload atingida.
500Erro no servidor. Verifique status.

Limites de taxa

Todos os limites de taxa são por IP. Uma resposta 429 inclui os cabeçalhos padrão Retry-After, X-RateLimit-Limit e X-RateLimit-Remaining.

EscopoLimite
Iniciar / confirmar / abortar upload60 / minuto
Conclusão multipart500 / minuto
URLs das partes multipart120 / minuto
Iniciar / confirmar lote500 / minuto
Consultas de status (arquivo e coleção)120 / minuto
Configurações (senha, expiração, downloads máximos)30 / minuto
Verificação de senha10 / minuto
Criar coleção30 / minuto
Gerenciar (pronto, excluir)60 / minuto
Upload de miniatura120 / minuto
Upload via ShareX20 / dia
Análises / erros do app120 e 60 / minuto

Cota de upload: Clientes anônimos têm dois limites paralelos — 100 GB / 24 h por token do visitante e 500 GB / 24 h por IP (o limite de IP captura tráfego sem token e redes compartilhadas). Quando qualquer um é excedido, você recebe um 429 com detalhes. Isso é apenas uma cota upload — downloads são ilimitados e sem restrições (servidos diretamente por URLs assinadas do R2).

Enviar

O fluxo de upload em três etapas para qualquer arquivo, incluindo arquivos maiores que 5 GB (automaticamente multipart). Se você precisa apenas de um upload rápido no estilo screenshot, veja ShareX.

POST /upload/init 60/min

Inicie um upload. Para arquivos >50 MB a resposta inclui part_urls para um upload multipart; caso contrário, uma única url.

Corpo da requisição

CampoTipoDescrição
filenamestring · requiredNome original do arquivo. Máximo 255 caracteres.
content_typestring · requiredTipo MIME.
sizeinteger · requiredTamanho do arquivo em bytes. Mínimo 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

Solicite URLs adicionais para partes de um upload multipart em andamento. Usado quando /init retornou menos URLs do que suas partes (ou elas expiraram).

Corpo da requisição

CampoTipoDescrição
upload_idstring · requiredO upload_id de /init.
part_numbersarray<int> · requiredNúmeros das partes para obter URLs.
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

Finalize um upload multipart no R2 assim que todas as partes forem enviadas.

Corpo da requisição

CampoTipoDescrição
upload_idstring · requiredO upload_id de /init.
partsarray · requiredCada entrada: { partNumber, etag } da resposta do 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 60/min

Cancele um upload multipart e limpe quaisquer dados parciais no R2.

Corpo da requisição

CampoTipoDescrição
upload_idstring · requiredO upload a ser abortado.
Request
curl -X POST https://storage.to/api/upload/abort \ -H "Content-Type: application/json" \ -d '{ "upload_id": "01HXYZ..." }'
POST /upload/confirm 60/min

Confirme que o upload está completo. É quando criamos o registro File e retornamos a URL compartilhável.

Corpo da requisição

CampoTipoDescrição
filenamestring · requiredNome original do arquivo.
sizeinteger · requiredTamanho do arquivo em bytes.
content_typestring · requiredTipo MIME.
r2_keystring · requiredO r2_key de /init.
collection_idstring · optionalAnexar a uma coleção.
crc32integer · optionalChecksum CRC32 para verificação de integridade.
file_idstring(9) · optionalCompletar um ID de arquivo reservado previamente reservado.
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

Reserve um ID de arquivo e uma URL compartilhável antes que os bytes estejam prontos. Útil quando você precisa distribuir um link primeiro e completar o upload depois. A propriedade está vinculada ao seu token de visitante + IP. Finalize o upload depois com /upload/init + /upload/confirm, passando file_id para confirmar.

Corpo da requisição

CampoTipoDescrição
filenamestring · optionalNome de arquivo placeholder. Padrão para "Pending".
content_typestring · optionalTipo MIME placeholder.
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

Equivalente em lote de /upload/init, otimizado para o uploader web. Inicia até 250 arquivos em uma única viagem.

Usado internamente pelo uploader web. A maioria dos clientes deve preferir /upload/init para arquivo único.

POST /upload/confirm-batch 500/min

Equivalente em lote de /upload/confirm. Confirma vários arquivos em uma única viagem.

Coleções

Uma coleção agrupa vários arquivos sob uma única URL de compartilhamento (/c/{id}). Até 10.000 arquivos e 25 GB no total.

POST /collection 30/min

Crie uma nova coleção. Anexe arquivos depois enviando collection_id em /upload/confirm.

Corpo da requisição

CampoTipoDescrição
expected_file_countinteger · optionalDica para marcar automaticamente a coleção como pronta assim que todos os arquivos esperados forem confirmados.
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

Verifica o estado de uma coleção. Também marca automaticamente a coleção como pronta se todos os arquivos esperados forem confirmados.

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

Marcar a coleção como pronta para download. Normalmente não é necessário — coleções ficam prontas automaticamente quando expected_file_count é atingido.

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

Excluir uma coleção e todos os seus arquivos.

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

Definir uma senha para a coleção. Requer 4 a 100 caracteres.

Corpo da requisição

CampoTipoDescrição
passwordstring · required4 a 100 caracteres.
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

Remover a senha de uma coleção.

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

Verificar uma senha. Retorna 200 em caso de sucesso, 401 se a senha estiver incorreta.

Corpo da requisição

CampoTipoDescrição
passwordstring · required
POST /collection/{id}/expiry Owner only 30/min

Alterar a expiração de uma coleção.

Corpo da requisição

CampoTipoDescrição
daysinteger · optionalDe 1 a 7 dias a partir de agora. Omitir ou usar null para permanente (somente premium).
POST /collection/{id}/max-downloads Owner only 30/min

Definir um limite de downloads (apaga após N downloads). A coleção é excluída automaticamente quando atingido.

Corpo da requisição

CampoTipoDescrição
max_downloadsinteger · optionalDe 1 a 1000. Deve ser maior que a contagem atual de downloads. Use null para remover o limite.

Arquivos

Todas as configurações no nível do arquivo (senha, expiração, downloads máximos) refletem os endpoints da coleção. Somente para o proprietário.

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

Verificar se um arquivo ainda está pendente de upload.

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

Excluir um arquivo imediatamente.

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

Enviar uma imagem em miniatura para um vídeo ou arquivo de imagem (usado na página de download). Máximo 2 MB.

Corpo da requisição

CampoTipoDescrição
thumbnailimage · requiredUpload multipart. Máximo 2 MB.
Response
{ "success": true, "thumbnail_url": "https://..." }
POST /file/{id}/password Owner only 30/min

Definir uma senha para um arquivo. Requer 4 a 100 caracteres.

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

Remover a senha de um arquivo.

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

Verificar a senha de um arquivo.

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

Alterar a expiração de um arquivo.

Corpo da requisição

CampoTipoDescrição
daysinteger · optionalDe 1 a 7 dias a partir de agora. Omitir ou usar null para permanente (somente premium).
POST /file/{id}/max-downloads Owner only 30/min

Limitar o total de downloads de um arquivo. Exclui automaticamente quando atingido.

Upload via ShareX

Endpoint de upload único — envie um arquivo multipart, receba uma URL compartilhável. Sem etapas de iniciar/confirmar. Ideal para ferramentas de captura de tela. Guia completo de configuração em /pt/docs/sharex.

POST /sharex/upload 20/day

Envie uma imagem ou arquivo diretamente (formulário multipart, campo file). Máximo 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" }

Autenticação desktop

Para clientes autenticados (ex: app desktop) com token Sanctum.

GET /user Bearer token

Retorna o usuário autenticado.

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

Revogar o token de acesso atual.

Diversos

GET /health

Verificação de saúde. Testa o banco de dados, armazenamento R2 e cache Redis. Retorna 200 se tudo estiver ok, 503 caso contrário.

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

Fluxo de atividade ao vivo para o globo da página inicial. Cacheado na borda da Cloudflare.

GET /bandwidth/status 60/min

Uso atual da cota de upload para o solicitante — usado pelo CLI e app desktop para mostrar a capacidade restante. O formato da resposta difere para usuários autenticados. Apesar do nome da URL, isso rastreia apenas upload bytes; downloads não são contabilizados.

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

Enviar um evento de uso pelo CLI ou app desktop.

Corpo da requisição

CampoTipoDescrição
appstring · requireddesktop, cli ou web.
versionstring · optionalVersão do cliente.
eventstring · requiredNome do evento, ex: upload_complete.
contextobject · optionalMetadados extras.
POST /app-errors 60/min

Enviar um relatório de erro pelo CLI ou app desktop. Duplicatas são removidas no servidor — máximo 10 do mesmo erro por hora.

Corpo da requisição

CampoTipoDescrição
appstring · requireddesktop, cli ou web.
typestring · requiredClasse/tipo de erro.
messagestring · requiredMensagem de erro.
stackstring · optionalRastreamento de pilha.
version, os, os_version, arch, contextvarious · optionalMetadados diagnósticos.