iLoveConvert API · v1
File conversion API
Convert, compress and resize images, PDFs and documents from your own code. The same engine as the website, with the same privacy promise: files are deleted within 1 hour and never used for anything else.
Quickstart
- Create a key on your API page (Business plan).
- POST /v1/uploads/ describes the file and returns a presigned URL; PUT the file there.
- POST /v1/uploads/:id/complete/, then POST /v1/jobs/ with the settings.
- Poll GET /v1/jobs/:id/ or pass a webhookUrl, then download outputs[].url (valid 15 minutes).
# 1. Create an upload for HEIC → JPG
curl -s -X POST https://iloveconvert.eu/v1/uploads/ \
-H "Authorization: Bearer $FILEORA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"toolId":"heic-to-jpg","fileName":"photo.heic","size":2483021,"contentType":"image/heic"}'
# → {"uploadId":"…","upload":{"url":"https://…","method":"PUT","headers":{…}}}
# 2. Upload the file to the presigned URL
curl -s -X PUT "$UPLOAD_URL" -H "Content-Type: image/heic" --data-binary @photo.heic
# 3. Confirm the upload and start the job
curl -s -X POST https://iloveconvert.eu/v1/uploads/$UPLOAD_ID/complete/ -H "Authorization: Bearer $FILEORA_API_KEY"
curl -s -X POST https://iloveconvert.eu/v1/jobs/ \
-H "Authorization: Bearer $FILEORA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1042" \
-d '{"uploadId":"'$UPLOAD_ID'","settings":{"quality":85}}'
# 4. Poll until "completed", then download outputs[0].url (valid 15 minutes)
curl -s https://iloveconvert.eu/v1/jobs/$UPLOAD_ID/ -H "Authorization: Bearer $FILEORA_API_KEY"import { readFile } from "node:fs/promises";
const API = "https://iloveconvert.eu/v1";
const headers = { Authorization: `Bearer ${process.env.FILEORA_API_KEY}`, "Content-Type": "application/json" };
const call = async (path, init = {}) => {
const res = await fetch(API + path, { ...init, headers: { ...headers, ...init.headers } });
if (!res.ok) throw new Error((await res.json()).error.code);
return res.json();
};
const file = await readFile("photo.heic");
const { uploadId, upload } = await call("/uploads/", {
method: "POST",
body: JSON.stringify({ toolId: "heic-to-jpg", fileName: "photo.heic", size: file.length, contentType: "image/heic" }),
});
await fetch(upload.url, { method: "PUT", headers: upload.headers, body: file });
await call(`/uploads/${uploadId}/complete/`, { method: "POST" });
await call("/jobs/", { method: "POST", body: JSON.stringify({ uploadId, settings: { quality: 85 } }) });
let job;
do {
await new Promise((r) => setTimeout(r, 1000));
job = await call(`/jobs/${uploadId}/`);
} while (job.status === "queued" || job.status === "processing");
console.log(job.status, job.outputs?.[0]?.url);import os, time, requests
API = "https://iloveconvert.eu/v1"
H = {"Authorization": f"Bearer {os.environ['FILEORA_API_KEY']}"}
data = open("photo.heic", "rb").read()
up = requests.post(f"{API}/uploads/", headers=H, json={
"toolId": "heic-to-jpg", "fileName": "photo.heic", "size": len(data), "contentType": "image/heic",
}).json()
requests.put(up["upload"]["url"], headers=up["upload"]["headers"], data=data).raise_for_status()
requests.post(f"{API}/uploads/{up['uploadId']}/complete/", headers=H).raise_for_status()
requests.post(f"{API}/jobs/", headers=H, json={"uploadId": up["uploadId"], "settings": {"quality": 85}})
while True:
job = requests.get(f"{API}/jobs/{up['uploadId']}/", headers=H).json()
if job["status"] not in ("queued", "processing"):
break
time.sleep(1)
print(job["status"], job.get("outputs", [{}])[0].get("url"))<?php
$api = "https://iloveconvert.eu/v1";
$key = getenv("FILEORA_API_KEY");
function call($method, $url, $key, $body = null) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer $key", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body ? json_encode($body) : null,
]);
return json_decode(curl_exec($ch), true);
}
$data = file_get_contents("photo.heic");
$up = call("POST", "$api/uploads/", $key, [
"toolId" => "heic-to-jpg", "fileName" => "photo.heic", "size" => strlen($data), "contentType" => "image/heic",
]);
$ch = curl_init($up["upload"]["url"]);
curl_setopt_array($ch, [CURLOPT_CUSTOMREQUEST => "PUT", CURLOPT_POSTFIELDS => $data,
CURLOPT_HTTPHEADER => ["Content-Type: image/heic"], CURLOPT_RETURNTRANSFER => true]);
curl_exec($ch);
call("POST", "$api/uploads/{$up['uploadId']}/complete/", $key);
call("POST", "$api/jobs/", $key, ["uploadId" => $up["uploadId"], "settings" => ["quality" => 85]]);
do { sleep(1); $job = call("GET", "$api/jobs/{$up['uploadId']}/", $key); }
while (in_array($job["status"], ["queued", "processing"]));
echo $job["status"], " ", $job["outputs"][0]["url"] ?? "";Authentication
Send your key with every request as Authorization: Bearer fo_live_… (or X-API-Key). Keys are shown once when created; we store only a hash. Revoke a key on your API page and it stops working immediately. Never put keys in browser code.
Endpoints
Base URL https://iloveconvert.eu/v1. Paths end with a slash; requests without it are redirected with 308 (the method is kept).
GET /v1/tools/ lists every tool with its input and output formats and the settings it accepts.
Webhooks
Pass webhookUrl (a public https URL) when you start a job. When it finishes we POST { "event": "job.completed" | "job.failed", "jobId", "status" } with a Fileora-Signature: t=…,v1=… header. Verify it with the webhook secret from your API page and reject timestamps older than 5 minutes:
import { createHmac, timingSafeEqual } from "node:crypto";
// rawBody: the request body exactly as received (a string)
export function verifyFileoraWebhook(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest();
const given = Buffer.from(parts.v1 ?? "", "hex");
return given.length === expected.length && timingSafeEqual(given, expected);
}Errors
Errors are JSON: { "error": { "code", "message" } }. Request errors:
| Code | HTTP | Meaning |
|---|---|---|
| UNAUTHENTICATED | 401 | No API key was sent. |
| INVALID_API_KEY | 401 | The key is wrong or was revoked. |
| PLAN_REQUIRED | 403 | The account has no plan with API access. |
| ACCOUNT_SUSPENDED | 403 | The account is suspended. |
| RATE_LIMITED | 429 | More than 60 requests in a minute for this key. Retry after the Retry-After seconds. |
| API_QUOTA_EXCEEDED | 429 | The month's included credits are used up and overage is not enabled. |
| LIMIT_REACHED | 429 | The plan's daily operation limit is reached. |
| INVALID_BODY | 400 | The JSON body is missing or malformed. |
| INVALID_IDEMPOTENCY_KEY | 400 | Idempotency-Key must be 8–128 letters, digits, '-' or '_'. |
| INVALID_WEBHOOK_URL | 422 | webhookUrl must be a public https:// URL. |
| INVALID_SETTINGS | 422 | A setting is unknown or out of range (see /v1/tools/). |
| UPLOAD_INCOMPLETE | 409 | Confirm the upload before starting the job. |
| NOT_FOUND | 404 | No such upload or job for this account. |
| SERVER_BUSY | 503 | The queue is full. Retry after the Retry-After seconds. |
| MAINTENANCE | 503 | Server processing is paused for maintenance. |
A failed job reports one of these processing codes in error.code:
- UNSUPPORTED_FORMAT – This file format is not supported by this tool.
- FILE_TOO_LARGE – This file is larger than the allowed size.
- TOO_MANY_FILES – You added more files than allowed in one operation.
- EMPTY_FILE – This file is empty.
- CORRUPTED_FILE – This file seems to be damaged and could not be read.
- ENCRYPTED_FILE – This PDF is password-protected.
- MALWARE_DETECTED – This file was flagged as unsafe and was deleted.
- IMAGE_TOO_LARGE – This image has too many pixels to process safely.
- LIMIT_REACHED – You have reached your daily limit.
- PROCESSING_FAILED – Something went wrong while processing this file.
- PROCESSING_TIMEOUT – Processing took too long and was stopped.
- RESOURCE_LIMIT – This file needs more memory or processing power than allowed.
- BROWSER_UNSUPPORTED – Your browser cannot process this file. Try an up-to-date Chrome, Edge, Firefox or Safari.
- SERVER_UNAVAILABLE – Our servers cannot process this file right now. Please try again later.
- NETWORK_ERROR – Network error. Check your connection and try again.
- CANCELLED – Processing was cancelled.
Rate limits and idempotency
- 60 requests per minute per key; over that you get 429 with Retry-After.
- The plan's file size and files-per-operation limits apply, like on the website.
- Send Idempotency-Key with POSTs: a repeat within 24 hours returns the first response (header Idempotent-Replayed: true) instead of a second job.
- Uploaded files and results are deleted within 1 hour.
API pricing
The API is part of the Business plan (€19.99 per month), which includes 10,000 credits a month. Each started job uses credits by how heavy it is:
- Images: 1
- PDF: 2
- Documents: 3
- Audio and video: 5
You get an email at 80 % and 100 % of your credits. Need more? Contact us.