Convert & compress files from your code.
One REST endpoint, one header. Send a file or a URL, poll for a signed download link. The same engines that power filewhirl.com, no JWT, no SDK required.
x-api-key auth
No JWT, no Authorization header: call it from any backend.
Private by default
Files deleted after 1 hour; signed URLs only.
Usage-metered
$29/mo plan + pay-as-you-go credits. Failed jobs are refunded.
Authentication
Create a key on your account page and send it on every request:
x-api-key: ff_live_your_key_hereKeys start with ff_live_ and are shown only once. Keep them server-side: anyone with a key can spend your quota.
Create a job
POST https://fkgvpgpdkktikfzspnib.supabase.co/functions/v1/convert-api accepts a file as multipart, or send a public URL as JSON. The to field is the target format (it must differ from the input format, same-format requests return 400). Returns 202 with an id, token and status_url.
curl -X POST https://fkgvpgpdkktikfzspnib.supabase.co/functions/v1/convert-api \
-H "x-api-key: ff_live_your_key_here" \
-F "file=@photo.png" \
-F "to=jpg"curl -X POST https://fkgvpgpdkktikfzspnib.supabase.co/functions/v1/convert-api \
-H "x-api-key: ff_live_your_key_here" \
-H "content-type: application/json" \
-d '{ "to": "mp3", "url": "https://example.com/clip.mp4" }'Poll for the result
GET https://fkgvpgpdkktikfzspnib.supabase.co/functions/v1/convert-api?id=…&token=… every ~1.5s until status is done or error. On success you get a signed output_url valid for one hour.
curl "https://fkgvpgpdkktikfzspnib.supabase.co/functions/v1/convert-api?id=JOB_ID&token=JOB_TOKEN" \
-H "x-api-key: ff_live_your_key_here"Full examples
const API = "https://fkgvpgpdkktikfzspnib.supabase.co/functions/v1/convert-api";
const KEY = process.env.FILEWHIRL_API_KEY;
// 1. Create a job (upload a file)
const form = new FormData();
form.append("file", fileBlob, "photo.png");
form.append("to", "jpg");
const res = await fetch(API, { method: "POST", headers: { "x-api-key": KEY }, body: form });
const { id, token, status_url } = await res.json();
// 2. Poll until done (every ~1.5s; outputs are ready within seconds-minutes)
let job;
do {
await new Promise((r) => setTimeout(r, 1500));
job = await (await fetch(status_url, { headers: { "x-api-key": KEY } })).json();
} while (job.status === "queued" || job.status === "processing");
if (job.status === "done") console.log("Download:", job.output_url);
else throw new Error(job.error);import os, time, requests
API = "https://fkgvpgpdkktikfzspnib.supabase.co/functions/v1/convert-api"
KEY = os.environ["FILEWHIRL_API_KEY"]
H = {"x-api-key": KEY}
# 1. Create a job (or pass JSON {"to": "...", "url": "..."} instead of a file)
with open("photo.png", "rb") as f:
r = requests.post(API, headers=H, files={"file": f}, data={"to": "jpg"})
job = r.json()
sid, token = job["id"], job["token"]
# 2. Poll until done
while True:
time.sleep(1.5)
s = requests.get(API, headers=H, params={"id": sid, "token": token}).json()
if s["status"] in ("done", "error"):
break
print(s.get("output_url") or s["error"])Responses & status codes
| Code | Meaning |
|---|---|
| 202 | Job created: returns { id, token, status, billing, status_url, notify_email }. Pass an optional notify_email to be emailed when the job finishes; notify_email echoes the accepted address (or null). |
| 200 | Poll result, { status, output_url?, output_size?, engine?, error? }. |
| 400 | Bad request: missing `to`, no file/url provided, or an invalid `notify_email`. |
| 401 | Invalid or missing x-api-key. |
| 402 | Quota exceeded: subscribe or buy credits ({ error, reason, hint }). |
| 404 | Unknown job id/token on a poll. |
| 500 | Server error (upload/metering/internal), safe to retry. |
- Up to 2 GB per file (64 MB posted inline; larger files use an upload URL).
- Output URLs are signed and expire after 1 hour.
- One usage unit per job; failed-to-queue jobs are auto-refunded.
- No JWT or Supabase header needed, just x-api-key.
Common questions
How do I get an API key?+
Subscribe to the API plan (or buy pay-as-you-go credits) on your account page, then create a key under API keys. Keys look like ff_live_… and are shown once; store it securely.
Do I need a Supabase or Authorization header?+
No. The endpoint is authenticated solely by the x-api-key header, no JWT, no Authorization, no apikey header. That's why it's safe to call from any backend.
What can I convert?+
Anything the website supports: images, video, audio, documents and archives. The full format matrix is the same one powering filewhirl.com. Note the target format must differ from the input (same-format compression isn't available via the API yet. Use the compress tools on the website).
How is usage billed?+
One unit per job. The API plan includes a monthly allowance; once it's used up, jobs draw from pay-as-you-go credits. If neither has room the API returns 402 with a reason. You're never charged for a job that fails to queue (units are refunded on insert failure). During early access there's no charge.
How big can files be and how long are outputs available?+
Up to 2 GB per file, with one thing to know about how you send it. Posting the file inline as multipart is capped at 64 MB, because the whole request body has to be held in memory; over that you get a 413 saying so, before any usage unit is spent. For anything larger, ask for an upload URL, PUT the bytes straight to it, then queue the job with the input_path it returns. A public url works at any size too. Output download URLs are signed and valid for 1 hour; both the upload and the result are deleted after that window.
Is there a rate limit?+
There's no hard HTTP rate limit today: throughput is governed by your plan's concurrency and monthly quota. Be a good citizen: poll a job's status every ~1.5s rather than in a tight loop.
Ready? Get an API key · see pricing · browse the tools · OpenAPI spec · Postman collection.