Simple API
Convert a file with one request to the switchfile simple API (v1). Synchronous and asynchronous requests, the conversion object, batches, ZIP files and cost estimates.
The simple API converts one file per request. Send the file, a URL, an upload ID or an earlier conversion, name the target format and get the result back right away or later. All paths start with /v1.
Convert a file#
POST /v1/convert takes the file in one of four ways:
| Source | How |
|---|---|
| File in the request | multipart/form-data with the file in the field file, up to 100 MB |
| URL | JSON with url: a public http or https address. We download it |
| Upload | JSON with upload_id from POST /v1/uploads, for larger files |
| Earlier conversion | JSON with from: the ID of a conversion. Its input is converted again, no new upload |
The other fields are the same for all sources:
| Field | |
|---|---|
to | Target format, for example mp4, pdf or jpg. Required unless you send preset |
options | Settings for the target, for example {"quality": 80}. In multipart, send it as a JSON string. See Formats and options |
preset | ID or name of a preset. Sets to and options |
input_format | Input format, if the file name does not show it |
filename | File name of the input |
webhook_url | Send conversion.finished or conversion.failed to this URL. See Webhooks |
curl https://api.switchfile.io/v1/convert \
-H "Authorization: Bearer $SWITCHFILE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/report.docx", "to": "pdf"}'const res = await fetch("https://api.switchfile.io/v1/convert", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.SWITCHFILE_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ url: "https://example.com/report.docx", to: "pdf" }),
});
const conversion = await res.json();
if (!res.ok) throw new Error(`${conversion.code}: ${conversion.detail}`);
console.log(conversion.id, conversion.status);<?php
$ch = curl_init('https://api.switchfile.io/v1/convert');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(['url' => 'https://example.com/report.docx', 'to' => 'pdf']),
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('SWITCHFILE_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_RETURNTRANSFER => true,
]);
$conversion = json_decode(curl_exec($ch), true);
echo $conversion['id'], ' ', $conversion['status'], PHP_EOL;import os
import requests
res = requests.post(
"https://api.switchfile.io/v1/convert",
headers={"Authorization": f"Bearer {os.environ['SWITCHFILE_API_KEY']}"},
json={"url": "https://example.com/report.docx", "to": "pdf"},
)
res.raise_for_status()
conversion = res.json()
print(conversion["id"], conversion["status"])Wait for the result or come back later#
Without wait, the request answers right away with 202 Accepted. With ?wait=N it stays open for up to N seconds (at most 30):
| Request | Response |
|---|---|
no wait | 202 with id, status and links.self |
?wait=N, done in time | 200 with the result and download_url |
?wait=N, not done in time | 202, the same as without wait |
?wait=N and Accept: application/octet-stream, done in time | 200 with the file itself, up to 50 MB |
| the same, but the file is larger than 50 MB | 303 See Other with the download URL in Location |
| the same, but the conversion failed | an error with the code of the failure |
For a conversion that is still running:
- Ask again with GET /v1/conversions/{id}. It takes
?wait=Nas well. - Ask for many at once with GET /v1/conversions?ids=…, up to 50 IDs.
- Let us call you: set
webhook_urland handle conversion.finished.
curl "https://api.switchfile.io/v1/conversions/$CONVERSION_ID?wait=30" \
-H "Authorization: Bearer $SWITCHFILE_API_KEY"const res = await fetch(`https://api.switchfile.io/v1/conversions/${process.env.CONVERSION_ID}?wait=30`, {
headers: { Authorization: `Bearer ${process.env.SWITCHFILE_API_KEY}` },
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const conversion = await res.json();
console.log(conversion.status, conversion.progress);The conversion object#
{
"id": "0b8a7c1e-5d0f-4a51-9f39-2f7f4c1d9a10",
"object": "conversion",
"status": "finished",
"progress": 100,
"input": { "filename": "report.docx", "format": "docx", "size": 48213 },
"to": "pdf",
"options": {},
"result": {
"files": [{ "filename": "report.pdf", "size": 61544, "url": "https://api.switchfile.io/files/..." }]
},
"download_url": "https://api.switchfile.io/files/...",
"error": null,
"credits": 2,
"batch_id": null,
"created_at": "2026-11-06T18:00:00+00:00",
"finished_at": "2026-11-06T18:00:02+00:00",
"expires_at": "2026-11-07T18:00:02+00:00",
"links": { "self": "https://api.switchfile.io/v1/conversions/0b8a7c1e-5d0f-4a51-9f39-2f7f4c1d9a10" }
}| Field | |
|---|---|
status | queued, processing, finished or failed |
progress | 0 to 100 |
result.files | Usually one file. Some conversions return several, for example one image per page of a PDF |
download_url | The first result file. Signed, works without an API key until expires_at |
error | Set when status is failed: {"code": "...", "message": "..."}. See Errors |
credits | Credits used, set once the conversion is done |
expires_at | Input and result are deleted at this time, 24 hours after the conversion ends |
Cost before converting#
Add ?dry_run=1 to a convert request. Nothing is created and nothing is charged:
curl "https://api.switchfile.io/v1/convert?dry_run=1" \
-H "Authorization: Bearer $SWITCHFILE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/talk.mov", "to": "mp4"}'{
"object": "estimate",
"input_format": "mov",
"to": "mp4",
"credits": { "min": 1, "max": 5 },
"price": { "currency": "EUR", "min": 0.01, "max": 0.05 },
"free": true
}credits.min is the minimum for this pair of formats, credits.max the most it can cost: one credit per started minute of conversion time. free tells you whether the conversion still fits into today's free quota.
Convert again without uploading#
Send from with the ID of an earlier conversion. Its input is used again as long as it has not expired, for example to get a PNG and a JPG from the same file:
curl https://api.switchfile.io/v1/convert \
-H "Authorization: Bearer $SWITCHFILE_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"from\": \"$CONVERSION_ID\", \"to\": \"png\"}"Convert many files#
POST /v1/batches creates up to 50 conversions with one request. Either all of them are created or none: if one entry is wrong, the error names its index. Each entry takes the same fields as a JSON request to /v1/convert. With "archive": "zip", the batch offers every result in one ZIP file once all conversions are done.
curl "https://api.switchfile.io/v1/batches?wait=30" \
-H "Authorization: Bearer $SWITCHFILE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"archive": "zip",
"conversions": [
{"url": "https://example.com/a.png", "to": "jpg"},
{"url": "https://example.com/b.png", "to": "jpg"}
]
}'const res = await fetch("https://api.switchfile.io/v1/batches?wait=30", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.SWITCHFILE_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
archive: "zip",
conversions: ["a.png", "b.png"].map((name) => ({ url: `https://example.com/${name}`, to: "jpg" })),
}),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const batch = await res.json();
console.log(batch.status, batch.archive?.status, batch.archive?.url);archive.status is pending while conversions run, ready when the ZIP can be downloaded and empty if no conversion succeeded. Failed conversions are left out of the ZIP. GET /v1/batches/{id} takes ?wait=N, and DELETE /v1/batches/{id} deletes the batch with all its conversions.
One ZIP for several results#
POST /v1/archives puts the results of up to 50 finished conversions into one ZIP, for example after files were converted one by one:
curl https://api.switchfile.io/v1/archives \
-H "Authorization: Bearer $SWITCHFILE_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"conversions\": [\"$CONVERSION_ID\"]}"The answer has a signed url that works without an API key until expires_at. A conversion with several result files is included with all of them. If two files have the same name, the second gets a suffix, for example photo-1.jpg. A conversion that is still running or has failed returns 409 CONVERSION_NOT_FINISHED with its index. The archive is not stored and costs no credits.
Delete a conversion#
DELETE /v1/conversions/{id} deletes the input and the result right away and answers 204. A running conversion stops. Without it, files are deleted 24 hours after the conversion ends.
curl -X DELETE "https://api.switchfile.io/v1/conversions/$CONVERSION_ID" \
-H "Authorization: Bearer $SWITCHFILE_API_KEY"Formats and account#
- GET /v1/formats lists every input format with its targets, the minimum credits per file and up to three suggested targets. No API key needed.
?input=mkvreturns one format. - GET /v1/account returns your credits, today's free quota, the largest file you can send and the secret for webhook signatures.
- PATCH /v1/account sets the webhook URL for all conversions that do not send their own.