upload.am API
Upload, store, and share files from your own scripts, servers, and apps — the same speed, storage, and link controls the website gives your users, over a plain REST API.
Prefer plain text? Full reference as .md — handy for scripts and AI agents.
# 1. Get a presigned upload URL
curl -X POST https://upload.am/api/v1/upload/token \
-H "Authorization: Bearer ua_live_xxx" -H "Content-Type: application/json" \
-d '{"file_name": "report.pdf", "file_size": 204800}'
# 2. Put the bytes straight on storage
curl -X PUT "$upload_url" --data-binary @report.pdf
# 3. Confirm — the file now exists in your account
curl -X POST https://upload.am/api/v1/upload/confirm \
-H "Authorization: Bearer ua_live_xxx" -H "Content-Type: application/json" \
-d '{"object_key": "...", "public_uuid": "...", "node_id": 1,
"original_name": "report.pdf", "size_bytes": 204800}'
Authentication
Official app Upload.am for Windows signs in with email and password:
curl -X POST https://upload.am/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"you@example.com","password":"your-password"}'
That call needs no Bearer header. It returns a token plus the same
object as GET /me. POST /auth/logout revokes that token.
You can still create tokens by hand in
Account → API.
A leaked Bearer still cannot mint new tokens — only the account password can.
Every other request needs:
Authorization: Bearer ua_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
| Status | Error code | Meaning |
|---|---|---|
| 401 | missing_token | No Authorization header |
| 401 | invalid_token | Token not found or revoked |
| 401 | invalid_credentials | Wrong email or password on /auth/login |
| 403 | account_disabled | Account suspended |
Rate limits
Every endpoint has its own independent limit, counted per token —
hitting the ceiling on one endpoint never blocks another. Going over
returns 429 with a Retry-After header.
Storage quota and the max size of a single file follow your account's
plan (see GET /me below) — download speed itself is unmetered on every plan, same as on the website.
Uploading files
Files go straight into object storage over a presigned URL — the upload.am servers see the request headers, never the file bytes in full. It's the same protocol behind the website's own dropzone.
Small files (up to 8 MB)
A single PUT, three calls total — this is the flow shown in the quickstart above:
/upload/token→ presigned PUT URL{upload_url}→ the raw file bytes/upload/confirm→ registers the file in your accountLarge files — S3 multipart
The file is split into parts, each one gets its own presigned PUT:
/upload/multipart/create/upload/multipart/sign-part→ once per part/upload/multipart/complete/upload/confirmAny S3-compatible SDK (aws-sdk, boto3…) speaks this part/ETag format natively — nothing upload.am-specific at this step.
Options accepted by /upload/confirm
| Field | Type | Description |
|---|---|---|
| password | string | Password required to download — same as the website's dropzone |
| max_downloads | int, 1–1000 | File is deleted once the limit is hit |
| link_ttl | 10m·1h·24h·7d | Link expires sooner than the plan's retention, if that's sooner |
| folder_uuid | string | Place the file into an existing folder. Unknown or foreign uuid → 404 folder_not_found, the file is not silently dropped into the root |
| sha256 | 64 hex chars | Checksum computed on your side, verified on download |
Files
/filesList — folder_uuid or root. q without a folder searches the whole account/files/{uuid}File details/files/{uuid}Rename, set/clear password, set/clear download limit/files/{uuid}/moveMove to a folder (or the root)/files/{uuid}/downloadIssue a download link/files/{uuid}Move to trash — 204 empty body/files/{uuid}/restoreRestore from trash/files/{uuid}/forcePermanently delete a trashed file — 204Downloading — the Free-plan wait, without cookies
POST /files/{uuid}/download doesn't ask for the file's own password —
that password protects the public /d/ page from strangers; your API token already
proved this is your file. This call does not increment download_count
and does not consume max_downloads — those apply to public /d/ downloads only.
On paid plans the link comes back immediately. On Free, the same
10-second anti-bot wait the website shows applies — call the endpoint twice:
curl -X POST https://upload.am/api/v1/files/b6f1.../download -H "Authorization: Bearer ua_live_xxx"
→ 202 { "status": "waiting", "wait_seconds": 10 }
sleep 10
curl -X POST https://upload.am/api/v1/files/b6f1.../download -H "Authorization: Bearer ua_live_xxx"
→ 200 { "status": "ready", "download_url": "https://files.upload.am/...", "expires_in": 600 }
Folders
/foldersList — parent_uuid optional/folders{"name", "parent_uuid"}/folders/ensure{"path", "parent_uuid"} — create or reuse a folder path/folders/{uuid}Rename/folders/{uuid}Only if empty — files inside move up one levelBatches
One link for several just-uploaded files, no password/expiry setup needed — each file keeps its own download page inside. Files that already have a batch_uuid cannot join another group (422 files_not_found). There is no DELETE /batches in v1.
/batches{"uuids": [...]}/batches/{uuid}Errors
Validation failures (422) use Laravel's standard shape. Everything else is {"error": "code", "message": "..."}.
| Status | Meaning |
|---|---|
| 404 | {"error":"not_found"} — not found, or not yours. Upload with a bad folder_uuid returns folder_not_found |
| 409 | Conflict — e.g. confirming the same upload twice |
| 410 | Expired or revoked |
| 413 | Over the plan's size or storage quota |
| 429 | Rate limited — see Retry-After |
| 507 | Storage node temporarily full |
Changelog
Every change to the API lands here, dated, the day it ships.
Profile avatar
- `GET /me` and `PATCH /me` now include `avatar_url`.
- `POST /me/avatar/token` + `POST /me/avatar/confirm` — upload or replace your profile picture (2MB limit, same presigned flow as regular files).
Account management
- `PATCH /me` to change your name/email, `PATCH /me/password` to change your password (requires current password) — by request from desktop client users who don't want to open the website just to update their profile.
Account-wide file listing
- `GET /files?all=1` lists every file in your account in one flat, paginated feed (up to 200 per page) — useful for client-side tools like duplicate detection by `sha256`.
Trash, file versions, Upload Inbox
- `GET /files?trashed=1` lists deleted files; `GET /files?inbox_pending=1` lists files awaiting review from your Upload Inbox.
- `GET /files/{uuid}/versions`, `.../versions/{v}/restore`, `.../versions/{v}/download` — file version history.
- `POST /files/{uuid}/accept` and `.../decline` for Upload Inbox submissions.
- `GET/PATCH /inbox`, `POST /inbox/regenerate-token` — manage your Upload Inbox link and rules.
Login, logout, folder paths
- `POST /auth/login` — email and password return a Bearer token. No token needed for this call.
- `POST /auth/logout` revokes the current token.
- `POST /folders/ensure` creates a folder path (`Contracts/2024`) or returns the existing one.
File search and response fields
- `GET /files?q=` without `folder_uuid` searches all files in the account, not only the root.
- `download_count` is always an integer (`0` if none).
- `POST /batches` includes `batch_uuid` on each file in the same response.
Confirm response, folders, download counter
- `POST /upload/confirm` returns the standard file object (`data`), same as `GET /files/{uuid}`.
- New field `batch_uuid` on file objects.
- Invalid `folder_uuid` on upload returns `404`.
- `POST /files/{uuid}/download` no longer counts against `max_downloads`.
- Consistent JSON for `404` responses.
Initial public release
- Bearer token authentication, managed from the dashboard (Account → API).
- File uploads via presigned S3 URLs — single PUT for small files, multipart for large ones. Same protocol the website itself uses.
- Files: list, get, rename, set password, set download limit, move, trash, restore, permanently delete, get a download link.
- Folders: list, create, rename, delete.
- Shares: create password-protected / time-limited / download-limited public links, list, revoke.
- Batches: group already-uploaded files under one shareable link.
- `GET /me` for account, plan, and quota information.