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.
Quickstart
Sign up, grab your API key, upload a file. The whole flow takes under a minute.
- Get your free API key (300 uploads/month, no credit card).
- POST any file to
https://filepost.dev/v1/uploadwith the key in theX-API-Keyheader. - Take the
urlfrom 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);
{
"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.
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
Headers
Body (multipart/form-data)
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);
{
"file_id": "abc123def456",
"url": "https://cdn.filepost.dev/file/filepost/uploads/ab/abc123def456.jpg",
"name": "photo.jpg",
"size": 45321,
"content_type": "image/jpeg"
}
Query parameters
curl https://filepost.dev/v1/files?page=1&per_page=50 \
-H "X-API-Key: YOUR_API_KEY"
{
"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
}
Path parameters
curl https://filepost.dev/v1/files/abc123def456 \
-H "X-API-Key: YOUR_API_KEY"
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"
{ "deleted": true, "file_id": "abc123def456" }
Account
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)
curl -X POST https://filepost.dev/v1/signup \ -H "Content-Type: application/json" \ -d '{"email": "you@example.com"}'
{
"message": "Account created",
"api_key": "fh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"plan": "free",
"email": "you@example.com"
}
curl https://filepost.dev/v1/account \
-H "X-API-Key: YOUR_API_KEY"
{
"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
}
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.
Body (application/json)
["jpg","pdf"].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"]}'
{
"intake_id": "intk_...",
"token": "3L18hphjnmDB",
"upload_url": "https://filepost.dev/u/3L18hphjnmDB"
}
curl https://filepost.dev/v1/intake-links \
-H "X-API-Key: YOUR_API_KEY"
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.
| Plan | Uploads | Rate limit | Max file size | Storage |
|---|---|---|---|---|
| Free | 300 / month | 10 req/sec | 50 MB | 2 GB |
| Starter ($9/mo) | 5,000 / month | 50 req/sec | 200 MB | Unlimited |
| Pro ($29/mo) | 25,000 / month | 200 req/sec | 500 MB | Unlimited |
All plans include unlimited bandwidth, HTTPS, Cloudflare CDN delivery, and permanent URLs.
Error codes
| Status | Meaning | What to do |
|---|---|---|
400 | Bad request (missing file, invalid email) | Check request body and headers. |
401 | Missing or invalid X-API-Key | Confirm the key is set and matches an active account. |
403 | Plan limit reached (uploads or storage) | Upgrade plan or wait for monthly reset. |
404 | File not found | Verify file_id belongs to the authenticated account. |
413 | File too large for plan | Free: 50 MB, Starter: 200 MB, Pro: 500 MB. |
422 | Validation error | Response body lists the invalid field. |
429 | Rate limit exceeded | Back off and retry after a few seconds. |
500 | Server error | Retry 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.