Developer API v1.2.3

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.

quickstart.sh
# 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
StatusError codeMeaning
401missing_tokenNo Authorization header
401invalid_tokenToken not found or revoked
401invalid_credentialsWrong email or password on /auth/login
403account_disabledAccount 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:

POST/upload/token→ presigned PUT URL
PUT{upload_url}→ the raw file bytes
POST/upload/confirm→ registers the file in your account

Large files — S3 multipart

The file is split into parts, each one gets its own presigned PUT:

POST/upload/multipart/create
POST/upload/multipart/sign-part→ once per part
POST/upload/multipart/complete
POST/upload/confirm

Any S3-compatible SDK (aws-sdk, boto3…) speaks this part/ETag format natively — nothing upload.am-specific at this step.

Options accepted by /upload/confirm
FieldTypeDescription
passwordstringPassword required to download — same as the website's dropzone
max_downloadsint, 1–1000File is deleted once the limit is hit
link_ttl10m·1h·24h·7dLink expires sooner than the plan's retention, if that's sooner
folder_uuidstringPlace the file into an existing folder. Unknown or foreign uuid → 404 folder_not_found, the file is not silently dropped into the root
sha25664 hex charsChecksum computed on your side, verified on download

Files

GET/filesList — folder_uuid or root. q without a folder searches the whole account
GET/files/{uuid}File details
PATCH/files/{uuid}Rename, set/clear password, set/clear download limit
POST/files/{uuid}/moveMove to a folder (or the root)
POST/files/{uuid}/downloadIssue a download link
DELETE/files/{uuid}Move to trash — 204 empty body
POST/files/{uuid}/restoreRestore from trash
DELETE/files/{uuid}/forcePermanently delete a trashed file — 204
Downloading — 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

GET/foldersList — parent_uuid optional
POST/folders{"name", "parent_uuid"}
POST/folders/ensure{"path", "parent_uuid"} — create or reuse a folder path
PATCH/folders/{uuid}Rename
DELETE/folders/{uuid}Only if empty — files inside move up one level

Shares

A public link to one or more files — with a password, expiry, download limit, burn-after-download, or an email gate.

GET/shares
POST/shares
GET/shares/{uuid}
DELETE/shares/{uuid}Revoke
Example — create a share
curl -X POST https://upload.am/api/v1/shares \
  -H "Authorization: Bearer ua_live_xxx" -H "Content-Type: application/json" \
  -d '{
    "file_uuids": ["b6f1...e9", "a2c3...11"],
    "ttl": "24h",
    "max_downloads": 10,
    "password": "secret",
    "title": "Meeting materials"
  }'

Batches

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.

POST/batches{"uuids": [...]}
GET/batches/{uuid}

Errors

Validation failures (422) use Laravel's standard shape. Everything else is {"error": "code", "message": "..."}.

StatusMeaning
404{"error":"not_found"} — not found, or not yours. Upload with a bad folder_uuid returns folder_not_found
409Conflict — e.g. confirming the same upload twice
410Expired or revoked
413Over the plan's size or storage quota
429Rate limited — see Retry-After
507Storage node temporarily full

Changelog

Every change to the API lands here, dated, the day it ships.

v1.2.3

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).
v1.2.2

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.
v1.2.1

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`.
v1.2.0

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.
v1.1.0

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.
v1.0.2

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.
v1.0.1

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.
v1.0

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.