API Reference

FilePost API Documentation

Upload any file, get a permanent public CDN URL. Three endpoints do 95% of what you need. Curl, Python, and Node.js examples for every call.

API v1.0 Base URL: https://filepost.dev Auth: X-API-Key header

Quickstart

Sign up, grab your API key, upload a file. The whole flow takes under a minute.

  1. Get your free API key (300 uploads/month, no credit card).
  2. POST any file to https://filepost.dev/v1/upload with the key in the X-API-Key header.
  3. Take the url from the response. It lives forever on CDN.
curl -X POST https://filepost.dev/v1/upload \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "file=@photo.jpg"
import requests

r = requests.post(
    "https://filepost.dev/v1/upload",
    headers={"X-API-Key": "YOUR_API_KEY"},
    files={"file": open("photo.jpg", "rb")},
)
print(r.json()["url"])
import fs from "node:fs";

const form = new FormData();
form.append("file", new Blob([fs.readFileSync("photo.jpg")]), "photo.jpg");

const r = await fetch("https://filepost.dev/v1/upload", {
  method: "POST",
  headers: { "X-API-Key": "YOUR_API_KEY" },
  body: form,
});
console.log((await r.json()).url);
200 OK
{
  "file_id": "abc123def456",
  "url": "https://cdn.filepost.dev/file/filepost/uploads/ab/abc123def456.jpg",
  "name": "photo.jpg",
  "size": 45321,
  "content_type": "image/jpeg"
}

Authentication

Every request except POST /v1/signup requires your API key in the X-API-Key request header. Keys are created on signup and can be rotated from your dashboard or via the rotate endpoint.

Keep your key server-side. An API key grants full access to uploads, listings, and deletes on your account. Never commit it to a public repo or ship it in a frontend bundle. If exposed, rotate immediately.

Base URL

All endpoints live under:

https://filepost.dev

Uploaded files are served from the CDN at https://cdn.filepost.dev. Both are HTTPS-only with HSTS preload.

Files

POST /v1/upload Upload a file and get a public URL

Headers

X-API-Key requiredstringYour API key.

Body (multipart/form-data)

file requiredfileThe file to upload. Free plan: 50 MB max. Starter: 200 MB. Pro: 500 MB.
curl -X POST https://filepost.dev/v1/upload \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "file=@photo.jpg"
import requests

r = requests.post(
    "https://filepost.dev/v1/upload",
    headers={"X-API-Key": "YOUR_API_KEY"},
    files={"file": open("photo.jpg", "rb")},
)
r.raise_for_status()
print(r.json()["url"])
import fs from "node:fs";

const form = new FormData();
form.append("file", new Blob([fs.readFileSync("photo.jpg")]), "photo.jpg");

const r = await fetch("https://filepost.dev/v1/upload", {
  method: "POST",
  headers: { "X-API-Key": "YOUR_API_KEY" },
  body: form,
});
if (!r.ok) throw new Error(await r.text());
console.log((await r.json()).url);
200 OK
{
  "file_id": "abc123def456",
  "url": "https://cdn.filepost.dev/file/filepost/uploads/ab/abc123def456.jpg",
  "name": "photo.jpg",
  "size": 45321,
  "content_type": "image/jpeg"
}
GET /v1/files List your uploaded files

Query parameters

pageintegerPage number, defaults to 1.
per_pageintegerItems per page, defaults to 50, max 200.
curl https://filepost.dev/v1/files?page=1&per_page=50 \
  -H "X-API-Key: YOUR_API_KEY"
200 OK
{
  "files": [
    {
      "file_id": "abc123def456",
      "url": "https://cdn.filepost.dev/file/filepost/uploads/ab/abc123def456.jpg",
      "name": "photo.jpg",
      "size": 45321,
      "content_type": "image/jpeg",
      "created_at": "2026-04-16T18:22:03Z"
    }
  ],
  "page": 1,
  "per_page": 50,
  "total": 1
}
GET /v1/files/{file_id} Get file details

Path parameters

file_id requiredstringThe file ID returned by the upload endpoint.
curl https://filepost.dev/v1/files/abc123def456 \
  -H "X-API-Key: YOUR_API_KEY"
DELETE /v1/files/{file_id} Delete a file permanently

Removes the file from storage and the CDN URL stops resolving. Cannot be undone.

curl -X DELETE https://filepost.dev/v1/files/abc123def456 \
  -H "X-API-Key: YOUR_API_KEY"
200 OK
{ "deleted": true, "file_id": "abc123def456" }

Account

POST /v1/signup Create an account and get an API key

No auth required. If an account already exists for the email, a magic login link is sent instead of returning a key.

Body (application/json)

email requiredstringA real, deliverable email address.
curl -X POST https://filepost.dev/v1/signup \
  -H "Content-Type: application/json" \
  -d '{"email": "you@example.com"}'
200 OK
{
  "message": "Account created",
  "api_key": "fh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "plan": "free",
  "email": "you@example.com"
}
GET /v1/account Get account info and current usage
curl https://filepost.dev/v1/account \
  -H "X-API-Key: YOUR_API_KEY"
200 OK
{
  "email": "you@example.com",
  "plan": "free",
  "uploads_this_month": 42,
  "upload_limit": 300,
  "storage_used_mb": 18,
  "storage_limit_mb": 2048,
  "max_file_size_mb": 50
}
POST /v1/rotate-key Generate a new API key and invalidate the old one

Use when a key may be compromised. The old key stops working as soon as the response returns the new one.

curl -X POST https://filepost.dev/v1/rotate-key \
  -H "X-API-Key: YOUR_CURRENT_KEY"

Intake links

Intake links let third parties upload files to your account through a public URL, without an API key. Useful for client onboarding, contractor deliverables, or form submissions.

POST /v1/intake-links Create a public upload link

Body (application/json)

labelstringHuman-readable name shown to uploaders, e.g. "Client photos".
max_filesintegerCap on uploads through this link. Optional.
max_file_size_mbintegerPer-file size cap in MB.
allowed_typesarrayFile extensions to allow, e.g. ["jpg","pdf"].
expires_in_daysintegerAuto-deactivate after N days. Defaults to 30.
curl -X POST https://filepost.dev/v1/intake-links \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"label":"Client photos","max_files":10,"allowed_types":["jpg","png","heic"]}'
200 OK
{
  "intake_id": "intk_...",
  "token": "3L18hphjnmDB",
  "upload_url": "https://filepost.dev/u/3L18hphjnmDB"
}
GET /v1/intake-links List your active intake links
curl https://filepost.dev/v1/intake-links \
  -H "X-API-Key: YOUR_API_KEY"
POST /v1/intake/{token}/upload Upload a file via an intake link (public, no auth)

This endpoint is called by whoever you share the intake URL with. No API key is required, only the token from the link.

curl -X POST https://filepost.dev/v1/intake/3L18hphjnmDB/upload \
  -F "file=@deliverable.pdf"

Rate limits

Rate limits are enforced per API key. Hitting a limit returns 429 Too Many Requests.

PlanUploadsRate limitMax file sizeStorage
Free300 / month10 req/sec50 MB2 GB
Starter ($9/mo)5,000 / month50 req/sec200 MBUnlimited
Pro ($29/mo)25,000 / month200 req/sec500 MBUnlimited

All plans include unlimited bandwidth, HTTPS, Cloudflare CDN delivery, and permanent URLs.

Error codes

StatusMeaningWhat to do
400Bad request (missing file, invalid email)Check request body and headers.
401Missing or invalid X-API-KeyConfirm the key is set and matches an active account.
403Plan limit reached (uploads or storage)Upgrade plan or wait for monthly reset.
404File not foundVerify file_id belongs to the authenticated account.
413File too large for planFree: 50 MB, Starter: 200 MB, Pro: 500 MB.
422Validation errorResponse body lists the invalid field.
429Rate limit exceededBack off and retry after a few seconds.
500Server errorRetry with exponential backoff. Email support@filepost.dev if it persists.

All error responses use the same JSON shape:

{ "detail": "Human-readable error message" }

OpenAPI spec

The raw OpenAPI 3.1 schema is available at /openapi.json. Point your codegen (openapi-generator, Orval, Kiota) at it to generate a typed client in any language.

If you prefer an interactive Swagger UI for testing endpoints manually, we still host it at /swagger.

Ready to ship?

Grab a free API key and make your first upload in under a minute.