API Reference
The ConvertFly REST API lets you convert, optimize, and transform files programmatically. Build conversion pipelines with jobs and tasks - import a file, process it, and export the result in a single request.
https://convertfly.io/api/v1All endpoints are relative to this base URL. Requests and responses use JSON. All timestamps are UTC ISO 8601.
Authentication
Every request must include an API key in the Authorization header. Keys are prefixed with cvfly_sk_ and can be created in your dashboard.
Authorization: Bearer cvfly_sk_live_abc123def456...Security tip: Never expose API keys in client-side code. Store them in environment variables and make API calls from your server.
Key Scopes
API keys can be scoped to limit what operations they allow:
| Scope | Access |
|---|---|
| jobs:write | Create and cancel jobs |
| jobs:read | List and retrieve jobs/tasks |
| webhooks:manage | Create, list, and delete webhooks |
| * | Full access (default) |
Quick Start
Convert a PDF to DOCX in three steps: create a job, wait for it to finish, then download the result.
Create a conversion job
curl -X POST https://convertfly.io/api/v1/jobs \
-H "Authorization: Bearer cvfly_sk_live_abc123..." \
-H "Content-Type: application/json" \
-d '{
"tasks": {
"import-file": {
"operation": "import/url",
"url": "https://example.com/report.pdf"
},
"convert": {
"operation": "convert",
"input": ["import-file"],
"output_format": "docx"
},
"export": {
"operation": "export/url",
"input": ["convert"]
}
}
}'const res = await fetch("https://convertfly.io/api/v1/jobs", {
method: "POST",
headers: {
"Authorization": "Bearer cvfly_sk_live_abc123...",
"Content-Type": "application/json",
},
body: JSON.stringify({
tasks: {
"import-file": {
operation: "import/url",
url: "https://example.com/report.pdf",
},
convert: {
operation: "convert",
input: ["import-file"],
output_format: "docx",
},
export: {
operation: "export/url",
input: ["convert"],
},
},
}),
});
const { data: job } = await res.json();
console.log(job.id); // "job_7kX9mP2qR..."
console.log(job.status); // "PROCESSING"import requests
resp = requests.post(
"https://convertfly.io/api/v1/jobs",
headers={"Authorization": "Bearer cvfly_sk_live_abc123..."},
json={
"tasks": {
"import-file": {
"operation": "import/url",
"url": "https://example.com/report.pdf",
},
"convert": {
"operation": "convert",
"input": ["import-file"],
"output_format": "docx",
},
"export": {
"operation": "export/url",
"input": ["convert"],
},
}
},
)
job = resp.json()["data"]
print(job["id"]) # "job_7kX9mP2qR..."
print(job["status"]) # "PROCESSING"Wait for completion
Poll the job status or use webhooks for real-time notifications.
curl https://convertfly.io/api/v1/jobs/job_7kX9mP2qR \
-H "Authorization: Bearer cvfly_sk_live_abc123..."{
"data": {
"id": "job_7kX9mP2qR",
"status": "FINISHED",
"created_at": "2026-04-03T10:30:00Z",
"tasks": {
"export": {
"status": "FINISHED",
"result": {
"url": "https://storage.convertfly.io/dl/abc123..."
}
}
}
}
}Download the result
Use the URL from the export task result. Download links expire after 24 hours.
curl -o result.docx "https://storage.convertfly.io/dl/abc123..."Jobs
A Job is a container for one or more Tasks that form a conversion pipeline. Tasks within a job are executed in dependency order - each task can reference the output of a previous task via the input field.
Pipeline concept
A typical pipeline looks like: import/url -> convert -> export/url. You can also chain multiple operations: import -> convert -> optimize -> watermark -> export.
Job statuses
| Status | Description |
|---|---|
| WAITING | Job created, queued for processing |
| PROCESSING | At least one task is currently running |
| FINISHED | All tasks completed successfully |
| ERROR | One or more tasks failed |
Create a job
/api/v1/jobstagstringOptional label to organize jobs (e.g. batch-2026-04).
tasksobjectrequiredA map of task names to task definitions. Task names must be unique within the job.
webhook_urlstringURL to receive a job.finished or job.failed event when the job completes.
curl -X POST https://convertfly.io/api/v1/jobs \
-H "Authorization: Bearer cvfly_sk_..." \
-H "Content-Type: application/json" \
-d '{
"tag": "invoice-batch",
"tasks": {
"import-file": {
"operation": "import/url",
"url": "https://example.com/invoice.pdf"
},
"to-png": {
"operation": "convert",
"input": ["import-file"],
"output_format": "png",
"options": { "density": 300 }
},
"export": {
"operation": "export/url",
"input": ["to-png"]
}
}
}'{
"data": {
"id": "job_7kX9mP2qR",
"tag": "invoice-batch",
"status": "PROCESSING",
"credits_used": 1,
"created_at": "2026-04-03T10:30:00Z",
"tasks": {
"import-file": { "status": "FINISHED" },
"to-png": { "status": "PROCESSING" },
"export": { "status": "WAITING" }
}
}
}Get a job
/api/v1/jobs/:idRetrieve job details and the status of all its tasks.
curl https://convertfly.io/api/v1/jobs/job_7kX9mP2qR \
-H "Authorization: Bearer cvfly_sk_..."List jobs
/api/v1/jobspageintegerPage number, starting at 1. Default: 1.
limitintegerResults per page, 1-100. Default: 20.
statusstringFilter by status: WAITING, PROCESSING, FINISHED, ERROR.
tagstringFilter by tag.
curl "https://convertfly.io/api/v1/jobs?status=FINISHED&limit=5" \
-H "Authorization: Bearer cvfly_sk_..."{
"data": [ ... ],
"meta": {
"page": 1,
"limit": 5,
"total": 42
}
}Delete a job
/api/v1/jobs/:idDelete a job and all its associated files. Running tasks will be cancelled. This action is irreversible.
curl -X DELETE https://convertfly.io/api/v1/jobs/job_7kX9mP2qR \
-H "Authorization: Bearer cvfly_sk_..."Wait for completion
/api/v1/jobs/:id/waitLong-poll endpoint that blocks until the job reaches a terminal state (FINISHED or ERROR). Times out after 300 seconds with a 202 Accepted if the job is still processing.
curl "https://convertfly.io/api/v1/jobs/job_7kX9mP2qR/wait?timeout=120" \
-H "Authorization: Bearer cvfly_sk_..."Tasks
Tasks are the individual steps within a job. Each task has a name (the key in the tasks object), an operation, and optionally an input array referencing previous task names.
Task fields
| Field | Type | Description |
|---|---|---|
| operation | string | The operation to perform (see Operations) |
| input | string[] | Names of tasks whose output to use as input |
| output_format | string | Target format for convert operations |
| options | object | Operation-specific settings (quality, density, etc.) |
List tasks for a job
/api/v1/jobs/:id/tasks{
"data": [
{
"name": "import-file",
"operation": "import/url",
"status": "FINISHED",
"result": { "file_id": "file_Xk9m..." }
},
{
"name": "convert",
"operation": "convert",
"status": "FINISHED",
"result": { "file_id": "file_Pm3n..." }
}
]
}Get a task
/api/v1/jobs/:job_id/tasks/:task_nameCancel a task
/api/v1/jobs/:job_id/tasks/:task_name/cancelCancel a running or waiting task. Downstream tasks that depend on it will also be cancelled.
Retry a failed task
/api/v1/jobs/:job_id/tasks/:task_name/retryRetry a task that has status ERROR. This consumes credits again if the task is billable.
File Upload
There are three ways to get files into ConvertFly. Choose the one that best fits your use case.
Import from URL
The simplest method. ConvertFly downloads the file from a publicly accessible URL.
{
"import-file": {
"operation": "import/url",
"url": "https://example.com/document.pdf",
"filename": "document.pdf",
"headers": {
"Authorization": "Bearer your-external-token"
}
}
}Direct upload (presigned URL)
For files on your local machine. This is a two-step process: first create the task to get a presigned upload URL, then PUT your file to that URL.
{
"upload-file": {
"operation": "import/upload",
"filename": "photo.jpg"
}
}# The task result will contain the presigned URL
curl -X PUT "https://storage.convertfly.io/upload/abc123..." \
-H "Content-Type: image/jpeg" \
--data-binary @photo.jpgImport from Base64
For small files (under 10 MB). Embed the file content directly in the request.
{
"import-file": {
"operation": "import/base64",
"file": "JVBERi0xLjQKJcOkw7zD...",
"filename": "document.pdf"
}
}| Method | Max size | Best for |
|---|---|---|
| import/url | 5 GB | Files already hosted online |
| import/upload | 5 GB | Local files, large files |
| import/base64 | 10 MB | Small files, inline in request |
Conversion Options
Each operation supports specific options. Here are all available operations and the format pairs for conversions.
Available operations
| Operation | Description | Credits |
|---|---|---|
| import/upload | Upload a file via presigned URL | 0 |
| import/url | Import a file from a URL | 0 |
| import/base64 | Import a file from base64 data | 0 |
| convert | Convert between formats | 1-4 |
| optimize | Compress/optimize a file | 1 |
| thumbnail | Generate a thumbnail preview | 1 |
| merge | Merge multiple files into one | 1 |
| watermark | Add text or image watermark | 1 |
| metadata | Extract file metadata | 1 |
| export/url | Get a presigned download URL | 0 |
| export/s3 | Export directly to your S3 bucket | 0 |
Supported format pairs
Popular conversions. The format is auto-detected from the file, but you can also set input_format explicitly.
| Category | Input formats | Output formats |
|---|---|---|
| Documents | pdf, docx, doc, odt, rtf, txt, html | pdf, docx, odt, rtf, txt, html |
| Spreadsheets | xlsx, xls, ods, csv | xlsx, ods, csv, pdf |
| Presentations | pptx, ppt, odp | pptx, odp, pdf |
| Images | jpg, png, gif, bmp, tiff, webp, svg, heic | jpg, png, gif, bmp, tiff, webp |
| Video | mp4, avi, mov, mkv, webm | mp4, avi, mov, mkv, webm, gif |
| Audio | mp3, wav, ogg, flac, aac, m4a | mp3, wav, ogg, flac, aac |
Quality and processing options
Pass these in the options object on a convert task.
qualityintegerOutput quality for lossy formats (1-100). Default: 85.
densityintegerDPI for document-to-image conversion. Default: 150.
widthintegerResize to this width in pixels. Maintains aspect ratio if height is omitted.
heightintegerResize to this height in pixels. Maintains aspect ratio if width is omitted.
pagesstringPage range for document operations. E.g. 1-5, 1,3,5, all.
{
"convert-hq": {
"operation": "convert",
"input": ["import-file"],
"output_format": "png",
"options": {
"density": 300,
"quality": 95
}
}
}Webhooks
Register webhook URLs to receive real-time notifications when jobs or tasks change status. All payloads are signed with HMAC-SHA256 so you can verify they came from ConvertFly.
Create a webhook
/api/v1/webhooksurlstringrequiredThe HTTPS URL to receive webhook events.
eventsstring[]requiredEvent types to subscribe to.
curl -X POST https://convertfly.io/api/v1/webhooks \
-H "Authorization: Bearer cvfly_sk_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/convertfly",
"events": ["job.finished", "job.failed"]
}'{
"data": {
"id": "wh_Abc123...",
"url": "https://your-app.com/webhooks/convertfly",
"events": ["job.finished", "job.failed"],
"signing_secret": "whsec_9f8e7d6c5b4a...",
"created_at": "2026-04-03T10:30:00Z"
}
}List webhooks
/api/v1/webhooksDelete a webhook
/api/v1/webhooks/:idEvent types
| Event | Trigger |
|---|---|
| job.finished | All tasks in the job completed successfully |
| job.failed | One or more tasks failed, job is in ERROR state |
| task.finished | An individual task completed |
| task.failed | An individual task failed |
Webhook payload
{
"event": "job.finished",
"timestamp": "2026-04-03T10:31:15Z",
"data": {
"id": "job_7kX9mP2qR",
"status": "FINISHED",
"tasks": { ... }
}
}Verifying signatures
Each webhook request includes two headers: X-Webhook-Signature and X-Webhook-Timestamp. Verify them to ensure the payload was sent by ConvertFly and was not tampered with.
import crypto from "crypto";
function verifyWebhook(rawBody, signature, timestamp, secret) {
const signedContent = timestamp + "." + rawBody;
const expected = "v1=" + crypto
.createHmac("sha256", secret)
.update(signedContent)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}import hmac, hashlib
def verify_webhook(raw_body: bytes, signature: str, timestamp: str, secret: str) -> bool:
signed_content = f"{timestamp}.{raw_body.decode()}"
expected = "v1=" + hmac.new(
secret.encode(), signed_content.encode(), hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)API Keys
Manage API keys programmatically. You can also manage keys from the dashboard.
Create an API key
/api/v1/api-keysnamestringrequiredA descriptive name for the key.
scopesstring[]Permissions to grant. Default: ["*"] (full access).
{
"data": {
"id": "key_Xk9m...",
"name": "production-server",
"key": "cvfly_sk_live_abc123def456...",
"scopes": ["*"],
"created_at": "2026-04-03T10:30:00Z"
}
}Important: The full API key is only shown once at creation time. Store it securely - you will not be able to retrieve it again.
List API keys
/api/v1/api-keysRevoke an API key
/api/v1/api-keys/:idImmediately invalidates the key. Any requests using it will return 401.
Rate Limiting
API requests are rate-limited per API key to ensure fair usage. When you exceed the limit, requests return 429 Too Many Requests.
Default limits
| Plan | Requests / minute | Concurrent jobs |
|---|---|---|
| Free | 10 | 2 |
| Pro | 60 | 10 |
| Business | 200 | 50 |
| Enterprise | Custom | Custom |
Response headers
Every API response includes these headers:
X-RateLimit-LimitheaderMaximum requests allowed in the current window.
X-RateLimit-RemainingheaderRequests remaining in the current window.
X-RateLimit-ResetheaderUnix timestamp when the window resets.
Retry-AfterheaderSeconds to wait before retrying (only on 429 responses).
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1743681600
Retry-After: 45
{
"code": "TOO_MANY_REQUESTS",
"message": "Rate limit exceeded. Retry after 45 seconds."
}Errors
ConvertFly uses conventional HTTP status codes. All error responses follow a consistent format with a machine-readable code and a human-readable message.
Error response format
{
"code": "INVALID_DATA",
"message": "Missing required field: output_format",
"details": {
"field": "output_format",
"reason": "required"
}
}Error codes
| Code | HTTP | Description |
|---|---|---|
| INVALID_DATA | 400 | Invalid request body or parameters |
| UNAUTHENTICATED | 401 | Missing or invalid API key |
| FORBIDDEN | 403 | API key lacks the required scope for this operation |
| NOT_FOUND | 404 | Resource does not exist or has been deleted |
| CONFLICT | 409 | Resource already exists or operation conflicts with current state |
| PAYLOAD_TOO_LARGE | 413 | Request body exceeds maximum allowed size |
| UNSUPPORTED_FORMAT | 422 | Requested conversion is not supported |
| TOO_MANY_REQUESTS | 429 | Rate limit exceeded - see Rate Limiting |
| INSUFFICIENT_CREDITS | 402 | Not enough credits to complete the operation |
| SERVER_ERROR | 500 | Internal server error - retry or contact support |
| UNAVAILABLE | 503 | Service temporarily unavailable - try again in a moment |
Handling errors
const res = await fetch("https://convertfly.io/api/v1/jobs", {
method: "POST",
headers: {
"Authorization": "Bearer cvfly_sk_...",
"Content-Type": "application/json",
},
body: JSON.stringify({ tasks: { /* ... */ } }),
});
if (!res.ok) {
const error = await res.json();
console.error(`[${error.code}] ${error.message}`);
// "[INVALID_DATA] Missing required field: output_format"
return;
}
const { data: job } = await res.json();Credits
Each operation costs a certain number of credits. Import and export operations are free. Your remaining balance is included in every API response via the X-Credits-Remaining header.
Credit costs by conversion type
| Operation | Cost | Examples |
|---|---|---|
| Import / Export | Free | import/url, import/upload, export/url, export/s3 |
| Image conversion | 1 credit | jpg to png, webp to gif, heic to jpg |
| Audio conversion | 1 credit | mp3 to wav, flac to aac |
| Document to PDF | 1 credit | txt to pdf, html to pdf, md to pdf |
| Office to PDF | 2 credits | docx to pdf, pptx to pdf, xlsx to pdf |
| Video conversion | 2 credits | mp4 to webm, mov to mp4 |
| PDF to Office | 4 credits | pdf to docx, pdf to pptx |
| Optimize | 1 credit | Compress images, PDFs |
| Thumbnail | 1 credit | Generate preview image |
| Merge | 1 credit | Combine multiple PDFs |
| Watermark | 1 credit | Add text/image overlay |
| Metadata | 1 credit | Extract file info |
Checking your balance
Every API response includes the X-Credits-Remaining header with your current balance.
HTTP/1.1 201 Created
Content-Type: application/json
X-Credits-Remaining: 9847
X-Credits-Used: 2When your balance is too low to complete a job, the API returns 402 with the INSUFFICIENT_CREDITS error code. Top up credits in your billing dashboard.
Ready to start building?
Create your free account and get 50 credits to try the API.