API Documentation
Complete reference for the FileConvertLab REST API.
Authentication
All API requests require authentication using an API key. Include your key in theX-API-Key header with every request.
X-API-Key: sk_live_your_api_key_hereInclude your API key in the X-API-Key header with every request.
Base URL
https://fileconvertlab.com/api/v1Create Conversion Job
Submit a file for conversion. The API uses an asynchronous job-based workflow.
POST/v1/jobsRequest
Content-Type: multipart/form-data
| Parameter | Type | Required | Description |
|---|---|---|---|
file | binary | Yes | The file to convert |
operation | string | Yes | Conversion operation (see Operations) |
options | JSON | No | Operation-specific options |
webhook_url | string | No | URL to receive completion notification |
Response (202 Accepted)
{
"id": "job_abc123xyz",
"status": "processing",
"operation": "pdf-to-word",
"created_at": "2025-12-07T10:30:00Z",
"estimated_seconds": 15,
"file": {
"name": "document.pdf",
"size": 1048576,
"mime_type": "application/pdf"
}
}Get Job Status
Check the status of a conversion job. Poll this endpoint until status iscompleted,failed orrejected.
GET/v1/jobs/{job_id}Response
{
"id": "job_abc123xyz",
"status": "completed",
"operation": "pdf-to-word",
"progress": 100,
"created_at": "2025-12-07T10:30:00Z",
"completed_at": "2025-12-07T10:30:12Z",
"result": {
"download_url": "/v1/jobs/job_abc123xyz/download",
"expires_at": "2025-12-07T11:30:12Z",
"file_name": "document.docx",
"file_size": 524288
}
}Status Values
| Status | Description |
|---|---|
pending | Job is queued for processing |
processing | Conversion is in progress |
completed | Conversion finished successfully |
failed | Conversion failed (see error field) |
rejected | Input file failed validation — no conversion ran (see error field) |
Download Result
Download the converted file. Only available after job status iscompleted.
GET/v1/jobs/{job_id}/downloadReturns the binary file with appropriate Content-Type and Content-Disposition headers.
Supported Operations
PDF Conversions
| Operation | Input | Output | Options |
|---|---|---|---|
pdf-to-word | DOCX | — | |
pdf-to-excel | XLSX | — | |
pdf-to-ppt | PPTX | — | |
pdf-to-png | PNG/ZIP | dpi: 72-600 | |
pdf-to-jpeg | JPEG/ZIP | dpi, quality |
Office to PDF
| Operation | Input | Output |
|---|---|---|
word-to-pdf | DOCX, DOC | |
excel-to-pdf | XLSX, XLS | |
ppt-to-pdf | PPTX, PPT | |
png-to-pdf | PNG | |
jpeg-to-pdf | JPEG |
OCR Operations
| Operation | Input | Output | Options |
|---|---|---|---|
ocr-pdf-to-pdf, ocr-png-to-pdf, ocr-jpeg-to-pdf | Scanned PDF, PNG, JPEG | Searchable PDF | language |
ocr-pdf-to-doc, ocr-png-to-doc, ocr-jpeg-to-doc | Scanned PDF, PNG, JPEG | DOCX | language |
ocr-pdf-to-txt, ocr-png-to-txt, ocr-jpeg-to-txt | Scanned PDF, PNG, JPEG | TXT | language |
ocr-multi-png-to-pdf, ocr-multi-png-to-doc, ocr-multi-png-to-txt | ZIP of PNG pages | PDF, DOCX, TXT | language |
ocr-multi-jpeg-to-pdf, ocr-multi-jpeg-to-doc, ocr-multi-jpeg-to-txt | ZIP of JPEG pages | PDF, DOCX, TXT | language |
ocr-*-to-text is accepted as an alias of ocr-*-to-txt. GIF analysis (POST /api/v1/gif/analyze) and PDF editing (POST /api/v1/pdf/apply-ops) are separate endpoints, not job operations.
OCR Languages
Values for the language option (auto picks the script from the page):
autoendeesfritnlplpttridvicssvdafinohuroruukelarfahijakothzhzh-twPDF Utilities
| Operation | Input | Output | Options |
|---|---|---|---|
compress-pdf | Compressed PDF | quality: 1-100 (default 75) |
Webhooks
Pass webhook_url when you create a job and we POST a JSON body to it once the job reaches a terminal state. The events arejob.completed,job.failed andjob.rejected. The URL must be public: private, loopback and link-local addresses are refused.
Payload
{
"event": "job.completed",
"job_id": "job_abc123xyz",
"status": "completed",
"operation": "pdf-to-word",
"result": {
"download_url": "https://fileconvertlab.com/api/v1/jobs/job_abc123xyz/download",
"file_name": "report_20260925_101500.docx",
"file_size": 51234,
"expires_at": "2026-09-25T11:15:00Z"
}
}Downloading the result still needs your API key, so treatdownload_url as a URL to call with theX-API-Key header, not a public link.
Retries
A delivery counts as successful on any 2xx response. Anything else, including a timeout after 30 seconds, is retried: three attempts in total, the second about 5 seconds later and the third about 30 seconds after that. Every request carriesX-FCL-Delivery-Attempt with the attempt number, so a receiver can tell a retry from a first delivery.
Signed payloads
Send webhook_secret together with the webhook URL and every delivery arrives with anX-FCL-Signature header. The header holds the Unix timestamp of the delivery and an HMAC-SHA256 digest of<timestamp>.<raw body> keyed with your secret, hex encoded: t=1800000000,v1=5f2b…
import hmac, hashlib, time
def verify(raw_body: bytes, header: str, secret: str, tolerance=300) -> bool:
parts = dict(p.split('=', 1) for p in header.split(','))
timestamp, digest = parts['t'], parts['v1']
if abs(time.time() - int(timestamp)) > tolerance:
return False
expected = hmac.new(
secret.encode(),
f"{timestamp}.".encode() + raw_body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, digest)Compare digests in constant time, reject deliveries whose timestamp is far from now, and verify against the raw request body rather than a re-serialised copy of the JSON.
Rate Limits
| Plan | Requests/min | Requests/day | Max File Size |
|---|---|---|---|
| Pro | 50 | 1,000 | 500 MB |
Rate Limit Headers
Every response includes rate limit information:
X-RateLimit-Limit: 50X-RateLimit-Remaining: 45X-RateLimit-Reset: 1701962400Error Handling
Error Response Format
{
"error": {
"code": "INVALID_FILE_TYPE",
"message": "Expected PDF file, got image/png",
"details": {
"expected": ["application/pdf"],
"received": "image/png"
}
}
}Error Codes
| HTTP | Code | Description |
|---|---|---|
| 400 | INVALID_FILE_TYPE | Wrong file format for operation |
| 400 | FILE_TOO_LARGE | File exceeds size limit |
| 400 | INVALID_OPERATION | Unknown operation type |
| 401 | UNAUTHORIZED | Missing or invalid API key |
| 404 | JOB_NOT_FOUND | Job ID does not exist |
| 410 | JOB_EXPIRED | Result file was deleted |
| 429 | RATE_LIMITED | Too many requests |
| 500 | INTERNAL_ERROR | Server error |
| 502 | CONVERSION_FAILED | Conversion engine error |
Code Examples
cURL
Create conversion jobcurl -X POST https://fileconvertlab.com/api/v1/jobs \ -H "X-API-Key: sk_live_abc123" \ -F "file=@document.pdf" \ -F "operation=pdf-to-word" Check job statuscurl https://fileconvertlab.com/api/v1/jobs/job_abc123 \ -H "X-API-Key: sk_live_abc123" Download resultcurl -O https://fileconvertlab.com/api/v1/jobs/job_abc123/download \ -H "X-API-Key: sk_live_abc123"JavaScript
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('operation', 'pdf-to-word');
// Create job
const response = await fetch('https://fileconvertlab.com/api/v1/jobs', {
method: 'POST',
headers: { 'X-API-Key': 'sk_live_abc123' },
body: formData
});
let job = await response.json();
// Poll until complete
while (job.status === 'processing') {
await new Promise(r => setTimeout(r, 1000));
const res = await fetch(`https://fileconvertlab.com/api/v1/jobs/${job.id}`, {
headers: { 'X-API-Key': 'sk_live_abc123' }
});
job = await res.json();
}
// Download result
if (job.status === 'completed') {
window.location.href =
`https://fileconvertlab.com${job.result.download_url}`;
}Python
import requests
import time
API_KEY = 'sk_live_abc123'
BASE_URL = 'https://fileconvertlab.com/api/v1'
headers = {'X-API-Key': API_KEY}
# Create job
with open('document.pdf', 'rb') as f:
response = requests.post(
f'{BASE_URL}/jobs',
headers=headers,
files={'file': f},
data={'operation': 'pdf-to-word'}
)
job = response.json()
# Poll until complete
while job['status'] == 'processing':
time.sleep(1)
response = requests.get(
f"{BASE_URL}/jobs/{job['id']}",
headers=headers
)
job = response.json()
# Download result
if job['status'] == 'completed':
response = requests.get(
f"{BASE_URL}{job['result']['download_url']}",
headers=headers
)
with open('document.docx', 'wb') as f:
f.write(response.content)Need help integrating?
Check out the CLI tool for quick command-line conversions, or browse the examples above.
CLI Tool Documentation