REST API

Загружайте файлы, создавайте коллекции и управляйте доступами по HTTP. Все ответы — в формате JSON; для анонимных загрузок ключ API не требуется.

Введение

API storage.to работает для нашего CLI, приложение для рабочего стола, веб-загрузчик и любых сторонних клиентов, которые вы захотите создать.

Процесс загрузки состоит из трёх шагов:

  1. Инициализация — сообщите, что хотите загрузить файл. Мы вернём один или несколько предподписанных URL-адресов, указывающих на Cloudflare R2.
  2. Загрузка в R2 — :передайте ваши байты напрямую на presigned URL(ы). Байты не проходят через наши серверы.
  3. Подтвердить — сообщите, что загрузка завершена. Мы создадим запись File и дадим вам URL-адрес для общего доступа.

Базовый URL

https://storage.to/api

Все конечные точки ниже указаны относительно этого базового адреса. Пример: POST /upload/init означает POST https://storage.to/api/upload/init.

Аутентификация

Большинство endpoint’ов не требуют аутентификации. Анонимные загрузки — ключевая функция.

Аутентификация необязательна и даёт доступ к:

  • Загрузки, привязанные к вашей учётной записи (видны в /dashboard)
  • Премиум-функции (постоянные файлы, больше места)
  • Изменения на основе владения (удаление, установка пароля, изменение срока действия) без необходимости совпадения visitor-token

Мы используем токены bearer Laravel Sanctum. Получите токен через передачу OAuth в desktop или вход на сайте, затем отправляйте его как:

Authorization: Bearer <token>

Токен посетителя

Анонимным клиентам нужен способ подтверждать владение собственными загрузками без учётной записи. Мы используем visitor token — случайную строку, которую клиент генерирует один раз и затем повторно использует. Отправляйте её с каждым запросом:

X-Visitor-Token: <random-string>

В веб-версии токен автоматически сохраняется в cookie visitor_token. В CLI он хранится по адресу ~/.config/storageto/token (см. Документация для CLI).

Для мутационных endpoint’ов (удаление, установка пароля, изменение срока действия) владение подтверждается, если либо токен посетителя совпадает или запрос приходит с того же IP, с которого был создан файл. Оба варианта могут быть потеряны (очищенные cookies, смена сети). В дальнейшем предпочтительным доказательством является owner token.

Токен владельца

Каждый endpoint, создающий ресурс (/upload/init multipart, /upload/confirm, /upload/reserve, /collection), возвращает в ответе owner_token. Токен — это подписанное доказательство владения, привязанное к конкретному ресурсу, и не зависит от вашего IP или токена посетителя.

Сохраните токен вместе с ID ресурса и отправляйте его в любом запросе на изменение как:

Authorization: Owner <token>

Или, если вы уже используете Authorization: Bearer для аутентифицированной сессии, отправляйте его так:

X-Owner-Token: <token>

Сервер принимает owner token как действительное доказательство владения вместе с устаревшим запасным вариантом visitor token + IP — клиенты, у которых есть токен, продолжают работать после смены сети или очистки cookies, а клиенты без токена работают ровно как раньше.

Токены живут столько же, сколько и ресурс: их безопасно сохранять, и они не истекают независимо. Потеря токена означает потерю контроля над этим ресурсом (файл/коллекция/загрузка) — относитесь к ним как к локальным паролям.

Ошибки

Ошибки имеют единый формат:

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

Частые HTTP-коды состояния:

КодЗначение
200ОК.
201Создано.
400Неверный запрос (например, превышен лимит размера коллекции).
401Требуется пароль или он неверный.
403Нет прав (вы не владелец ресурса).
404Ресурс не найден или срок действия истёк.
422Ошибка валидации или ограничение тарифа/квоты.
429Превышен лимит частоты или квота на загрузки.
500Ошибка сервера. Проверьте статус.

Ограничения по частоте запросов

Все лимиты частоты считаются по IP. В ответе 429 присутствуют стандартные заголовки Retry-After, X-RateLimit-Limit и X-RateLimit-Remaining.

ОбластьЛимит
Инициализация / подтверждение / отмена загрузки60 / минута
Завершение multipart500 / минута
URL частей multipart120 / минута
Пакетная инициализация / подтверждение500 / минута
Проверки статуса (файл и коллекция)120 / минута
Настройки (пароль, срок действия, максимум скачиваний)30 / минута
Проверка пароля10 / минута
Создание коллекции30 / минута
Управление (готово, удалить)60 / минута
Загрузка превью120 / минута
Загрузка из ShareX20 / день
Аналитика приложения / ошибки120 и 60 / мин

Квота на загрузки: для анонимных клиентов действуют два лимита, работающих параллельно — 100 ГБ / 24 ч на visitor token и 500 ГБ / 24 ч на IP (лимит по IP ловит трафик без токена и общие сети). Если превысить любой из них, вы получите 429 с подробностями. Это только загрузка лимит на отправку — скачивания неограничены и без ограничений по скорости (отдаются напрямую с R2 signed URLs).

Загрузить

Трёхшаговый процесс загрузки для любых файлов, включая файлы больше 5 ГБ (автоматически multipart). Если вам нужна только быстрая загрузка в стиле скриншота — вместо этого смотрите ShareX.

POST /upload/init 60/min

Инициируйте загрузку. Для файлов >50 МБ ответ будет multipart-загрузкой (поле type: "multipart"); иначе — один presigned PUT.

Тело запроса

ПолеТипОписание
filenamestring · requiredОригинальное имя файла. Макс. 255 символов.
content_typestring · requiredMIME-тип.
sizeinteger · requiredРазмер файла в байтах. Мин. 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

Запросить дополнительные URL-адреса частей для выполняющейся multipart-загрузки. Используется, когда /init вернул меньше URL, чем у вас частей (или они истекли).

Тело запроса

ПолеТипОписание
upload_idstring · requiredЗначение upload_id из /init.
part_numbersarray<int> · requiredНомера частей, для которых нужно получить 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

Завершить multipart-загрузку на R2 после загрузки всех частей.

Тело запроса

ПолеТипОписание
upload_idstring · requiredЗначение upload_id из /init.
partsarray · requiredКаждая запись: { partNumber, etag } из ответа 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

Отменить multipart-загрузку и очистить любые частичные данные на R2.

Тело запроса

ПолеТипОписание
upload_idstring · requiredЗагрузка, которую нужно прервать.
Request
curl -X POST https://storage.to/api/upload/abort \ -H "Content-Type: application/json" \ -d '{ "upload_id": "01HXYZ..." }'
POST /upload/confirm 60/min

Подтвердить, что загрузка завершена. Именно тогда мы создаём запись File и возвращаем общий URL.

Тело запроса

ПолеТипОписание
filenamestring · requiredОригинальное имя файла.
sizeinteger · requiredРазмер файла в байтах.
content_typestring · requiredMIME-тип.
r2_keystring · requiredЗначение r2_key из /init.
collection_idstring · optionalДобавить в коллекцию.
crc32integer · optionalКонтрольная сумма CRC32 для проверки целостности.
file_idstring(9) · optionalВыполнить (fulfil) ранее выданный reserved file 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" }, "owner_token": "owner_v1_..." }
POST /file/reserve 60/min

Зарезервируйте file ID и общий URL до того, как будут готовы байты. Удобно, когда нужно сначала выдать ссылку, а затем выполнить загрузку. Владение привязано к вашему visitor token + IP. Завершите загрузку позже с /upload/init + /upload/confirm, передав file_id для подтверждения.

Тело запроса

ПолеТипОписание
filenamestring · optionalИмя файла-заглушка. По умолчанию: "Pending".
content_typestring · optionalMIME-тип-заглушка.
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

Пакетный аналог /upload/init, оптимизированный для веб-загрузчика. Запускает до 250 файлов за один обмен.

Используется внутри веб-загрузчиком. Большинству клиентов лучше использовать одиночный /upload/init.

POST /upload/confirm-batch 500/min

Пакетный аналог /upload/confirm. Подтверждает много файлов за один обмен.

Коллекции

Коллекция объединяет несколько файлов под одним общим URL (/c/{id}). До 10 000 файлов и 25 ГБ суммарно.

POST /collection 30/min

Создать новую коллекцию. Затем добавьте файлы, передав collection_id на /upload/confirm.

Тело запроса

ПолеТипОписание
expected_file_countinteger · optionalПодсказка для автоматической отметки коллекции как готовой, когда подтвердятся все ожидаемые файлы.
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

Проверять состояние коллекции. Также автоматически помечает коллекцию как готовую, если подтвердятся все ожидаемые файлы.

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

Отметить коллекцию как готовую к скачиванию. Обычно не нужно — коллекции автоматически становятся готовыми, когда достигнут expected_file_count.

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

Удалить коллекцию и все её файлы.

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

Установить пароль для коллекции. Требуется 4–100 символов.

Тело запроса

ПолеТипОписание
passwordstring · required4–100 символов.
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

Удалить пароль из коллекции.

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

Проверить пароль. Возвращает 200 при успехе и 401 при неверном пароле.

Тело запроса

ПолеТипОписание
passwordstring · required
POST /collection/{id}/expiry Owner only 30/min

Изменить срок действия коллекции.

Тело запроса

ПолеТипОписание
daysinteger · optionalВ течение 1–7 дней с текущего момента. Пропустите или укажите null для постоянного (только premium).
POST /collection/{id}/max-downloads Owner only 30/min

Установить лимит скачиваний (burn-after-N-downloads). Коллекция автоматически удалится, когда лимит будет достигнут.

Тело запроса

ПолеТипОписание
max_downloadsinteger · optional1–1000. Должно быть больше текущего количества скачиваний. null — чтобы убрать лимит.

Файлы

Все настройки на уровне файла (пароль, срок действия, max-downloads) повторяют параметры коллекции. Только владелец.

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

Проверить, ожидает ли файл завершения загрузки.

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

Немедленно удалить файл.

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

Загрузить миниатюру для видео или изображения (используется на странице скачивания). Макс. 2 МБ.

Тело запроса

ПолеТипОписание
thumbnailimage · requiredMultipart-загрузка. Макс. 2 МБ.
Response
{ "success": true, "thumbnail_url": "https://..." }
POST /file/{id}/password Owner only 30/min

Установить пароль для файла. Требуется 4–100 символов.

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

Убрать пароль у файла.

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

Проверить пароль файла.

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

Изменить срок действия файла.

Тело запроса

ПолеТипОписание
daysinteger · optionalВ течение 1–7 дней с текущего момента. Пропустите или укажите null для постоянного (только premium).
POST /file/{id}/max-downloads Owner only 30/min

Ограничить общее число скачиваний файла. Автоматически удаляется, когда лимит достигнут.

Загрузка из ShareX

Единая точка загрузки — отправьте multipart-файл и получите обратно общий URL. Без танцев с init/confirm. Идеально для инструментов скриншотов. Полная инструкция по настройке на /ru/docs/sharex.

POST /sharex/upload 20/day

Загрузить изображение или файл напрямую (multipart form, поле file). Макс. 25 МБ.

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-приложения), у которых есть Sanctum token.

GET /user Bearer token

Верните аутентифицированного пользователя.

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

Отзовите текущий access token.

Разное

GET /health

Проверка работоспособности. Пингует базу данных, хранилище R2 и кэш Redis. Возвращает 200, если всё в порядке, и 503 — в противном случае.

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

Живой поток активности для «глобуса» на главной странице. Кэшируется на edge у Cloudflare.

GET /bandwidth/status 60/min

Текущее использование лимита на загрузку для вызывающего — используется CLI и desktop-приложением, чтобы показывать оставшуюся ёмкость. Формат ответа отличается для аутентифицированных пользователей. Несмотря на название URL, это отслеживает только загрузка байты; скачивания не учитываются.

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

Отправьте событие использования из CLI или desktop-приложения.

Тело запроса

ПолеТипОписание
appstring · requireddesktop, cli или web.
versionstring · optionalВерсия клиента.
eventstring · requiredИмя события, например upload_complete.
contextobject · optionalДополнительные метаданные.
POST /app-errors 60/min

Отправьте отчёт об ошибке из CLI или desktop-приложения. Дедупликация на сервере — максимум 10 одинаковых ошибок в час.

Тело запроса

ПолеТипОписание
appstring · requireddesktop, cli или web.
typestring · requiredКласс/тип ошибки.
messagestring · requiredСообщение об ошибке.
stackstring · optionalСтек вызовов.
version, os, os_version, arch, contextvarious · optionalДиагностические метаданные.