REST API

HTTPでファイルをアップロードし、コレクションを作成し、共有を管理します。すべてのレスポンスはJSONです。匿名アップロードにはAPIキーは不要です。

はじめに

storage.to の API が、CLIデスクトップアプリWeb アップローダー、そしてあなたが作りたい任意のサードパーティークライアントを支えています。

アップロードの流れは3ステップです:

  1. 初期化 — アップロードしたいファイルを伝えてください。Cloudflare R2 を指す、1つ以上の署名済みURLを返します。
  2. R2 にアップロードPUT は、バイトを直接プリサインドURL(複数可)へ送ります。バイトは当社のサーバーを経由しません。
  3. 確認 — アップロードが完了したことを伝えてください。File のレコードを作成し、共有可能なURLをお渡しします。

ベースURL

https://storage.to/api

以下のエンドポイントはすべて、このベースに対する相対です。例:POST /upload/initPOST https://storage.to/api/upload/init を意味します。

認証

ほとんどのエンドポイントは認証を必要としません。 匿名アップロードは主要な機能です。

認証は任意で、次の機能が利用できます:

  • アカウントに紐づくアップロード(/dashboard で表示)
  • プレミアム機能(永続ファイル、より大きなストレージ)
  • 訪問者トークンの一致が不要な、所有権ベースの変更(削除、パスワード設定、有効期限の変更)

Laravel Sanctum のベアラートークンを使用します。デスクトップの OAuth ハンドオフまたは Web ログインでトークンを発行し、次の形式で送信してください:

Authorization: Bearer <token>

ビジタートークン

匿名クライアントは、アカウントなしで自分のアップロードの所有権を証明する手段が必要です。そこで 訪問者トークン を使います。訪問者トークン は、クライアントが一度だけ生成して再利用するランダム文字列です。すべてのリクエストで送信してください:

X-Visitor-Token: <random-string>

Web では、トークンは visitor_token クッキーに自動的に保存されます。CLI では ~/.config/storageto/token に保存します(CLIドキュメント を参照)。

変更系エンドポイント(削除、パスワード設定、有効期限変更)では、どちらか 訪問者トークンが一致する または リクエストがファイルを作成したのと同じIPから来ている場合に所有権が確認されます。どちらも失われる可能性があります(Cookie削除、ネットワーク変更)。今後は オーナートークン を最も確実な証明として使うのがおすすめです。

オーナートークン

リソース作成系エンドポイント(/upload/init multipart、/upload/confirm/upload/reserve/collection)は、それぞれレスポンスに owner_token を返します。このトークンは、その特定のリソースに紐づいた署名付きの所有権証明であり、IPや訪問者トークンとは独立しています。

トークンをリソースIDと一緒に保存し、あらゆる変更(mutation)時に次のように送信してください:

Authorization: Owner <token>

または、認証済みセッションで既に Authorization: Bearer を使っている場合は、次のように送信してください:

X-Owner-Token: <token>

サーバーは、従来の 訪問者トークン + IP フォールバックに加えて、オーナートークンを有効な所有権証明として受け付けます。トークンを保持しているクライアントは、ネットワーク切り替えやCookie削除後も引き続き動作し、保持していないクライアントもこれまで通りそのまま動作します。

トークンはリソースが存在する限り有効で、保存しても安全で、単独で期限切れにはなりません。トークンを失うと、そのリソース(ファイル/コレクション/アップロード)の管理権限を失うことになります。ローカルのパスワードのように扱ってください。

エラー

エラーは次のような形式で返されます:

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

よくあるHTTPステータスコード:

コード意味
200OK。
201作成しました。
400不正なリクエストです(例:コレクションのサイズ上限を超過)。
401パスワードが必要、またはパスワードが正しくありません。
403認証されていません(リソースの所有者ではありません)。
404リソースが見つからないか、有効期限が切れています。
422バリデーションに失敗したか、プラン/クォータの制限があります。
429レート制限に達したか、アップロードクォータに到達しました。
500サーバーエラーです。ステータス を確認してください。

レート制限

レート制限はすべてIP単位です。429 のレスポンスには、標準の Retry-AfterX-RateLimit-LimitX-RateLimit-Remaining ヘッダーが含まれます。

スコープ上限
アップロード初期化 / 確認 / 中止60 / 分
マルチパート完了500 / 分
マルチパートのパートURL120 / 分
一括初期化 / 確認500 / 分
ステータスのポーリング(ファイル & コレクション)120 / 分
設定(パスワード、有効期限、最大ダウンロード数)30 / 分
パスワードの確認10 / 分
コレクション作成30 / 分
管理(準備完了、削除)60 / 分
サムネイルのアップロード120 / 分
ShareXアップロード20 / 日
アプリの分析 / エラー1分あたり120回と60回

アップロードクォータ: 匿名クライアントには並行して2つの上限があります — 1 訪問者トークン あたり 100 GB / 24時間IPあたり 500 GB / 24時間(IP上限はトークンなしの通信や共有ネットワークを検知します)。どちらかを超えると、詳細付きの 429 が返ります。これは アップロード クォータのみです — ダウンロードは無制限で、制限もありません(R2の署名付きURLから直接配信されます)。

アップロード

5GB超のファイルを含む、あらゆるファイルのための3ステップのアップロード手順(自動的にマルチパート)。スクリーンショットのような手軽なアップロードだけ必要なら、代わりに ShareX を見てください。

POST /upload/init 60/min

アップロードを開始します。50MBを超えるファイルの場合、レスポンスはマルチパートアップロードになります(type: "multipart" フィールド)。それ以外は、単一のプリサインド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をリクエストします。/init が返したURLが、用意しているパート数より少ない(または期限切れになった)場合に使用します。

リクエストボディ

項目種類説明
upload_idstring · required/init からの upload_id
part_numbersarray<int> · requiredURLを取得するパート番号。
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

すべてのパートがアップロードされたら、R2上でマルチパートアップロードを完了します。

リクエストボディ

項目種類説明
upload_idstring · required/init からの upload_id
partsarray · required各エントリ:R2のレスポンスからの { partNumber, etag }
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

マルチパートアップロードをキャンセルし、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/init からの r2_key
collection_idstring · optionalコレクションに追加します。
crc32integer · optional整合性確認のためのCRC32チェックサム。
file_idstring(9) · optional以前の 予約済み ファイル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

バイトが準備できる の時点で、ファイルIDと共有可能なURLを予約します。先にリンクを渡してから、後でアップロードを完了させたいときに便利です。所有権は「訪問者トークン + IP」に紐づきます。アップロードは後で /upload/init + /upload/confirm で完了し、確認時に file_id を渡してください。

リクエストボディ

項目種類説明
filenamestring · optionalプレースホルダーのファイル名。デフォルトは "Pending" です。
content_typestring · optionalプレースホルダーの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

/upload/init のバッチ相当で、Webアップローダー向けに最適化されています。1回の往復で最大250ファイルを開始します。

Webアップローダー内部で使用します。ほとんどのクライアントは、単一ファイルの /upload/init を優先してください。

POST /upload/confirm-batch 500/min

/upload/confirm のバッチ相当です。1回の往復で多数のファイルを確認します。

コレクション

コレクションは、単一の共有URL(/c/{id})配下に複数のファイルをまとめます。最大10,000ファイル、合計25GBまで。

POST /collection 30/min

新しいコレクションを作成します。後から /upload/confirmcollection_id を渡してファイルを追加します。

リクエストボディ

項目種類説明
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(プレミアムのみ)。
POST /collection/{id}/max-downloads Owner only 30/min

ダウンロード上限を設定します(burn-after-N-downloads)。上限に達するとコレクションは自動削除されます。

リクエストボディ

項目種類説明
max_downloadsinteger · optional1〜1000。現在のダウンロード数を上回る必要があります。上限を外すには null

ファイル

ファイル単位のすべての設定(パスワード、有効期限、最大ダウンロード数)は、コレクションのエンドポイントと同じです。所有者のみ。

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

ファイルのアップロードがまだ完了していないか確認します。

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

ファイルをすぐに削除します。

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

動画または画像ファイルのサムネイル画像をアップロードします(ダウンロードページで使用)。最大2MB。

リクエストボディ

項目種類説明
thumbnailimage · requiredマルチパートアップロード。最大2MB。
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(プレミアムのみ)。
POST /file/{id}/max-downloads Owner only 30/min

ファイルの総ダウンロード数に上限を設定します。上限に達すると自動削除されます。

ShareXアップロード

ワンショットのアップロードエンドポイント—マルチパートファイルを送ると、共有可能なURLが返ってきます。init/confirm の手順は不要です。スクリーンショットツールに最適です。セットアップ手順の詳細は /ja/docs/sharex

POST /sharex/upload 20/day

画像またはファイルを直接アップロードします(multipart form、file フィールド)。最大25MB。

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" }

デスクトップ認証

認証済みクライアント(例:デスクトップアプリ)で、Sanctumトークンを保持している場合。

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

現在のアクセストークンを無効化します。

その他

GET /health

ヘルスチェック。データベース、R2ストレージ、Redisキャッシュに接続確認(ping)します。すべて正常なら 200、そうでなければ 503 を返します。

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

ホームページのグローブ用のライブアクティビティストリーム。Cloudflareのエッジでキャッシュされます。

GET /bandwidth/status 60/min

呼び出し元の現在のアップロードクォータ使用量 — CLIとデスクトップアプリが残り容量を表示するために使用します。認証済みユーザーではレスポンス形式が異なります。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またはデスクトップアプリから利用イベントを送信します。

リクエストボディ

項目種類説明
appstring · requireddesktopcli、または web
versionstring · optionalクライアントのバージョン。
eventstring · requiredイベント名(例: upload_complete)。
contextobject · optional追加メタデータ。
POST /app-errors 60/min

CLIまたはデスクトップアプリからエラーレポートを送信します。サーバー側で重複を除外します。同じエラーは1時間あたり最大10件です。

リクエストボディ

項目種類説明
appstring · requireddesktopcli、または web
typestring · requiredエラーのクラス/タイプ。
messagestring · requiredエラーメッセージ。
stackstring · optionalスタックトレース。
version, os, os_version, arch, contextvarious · optional診断用メタデータ。