Public API

Convert files programmatically using an API key.

Getting started

  1. Go to Organization → Settings → API Keys and create a key. The full value is shown once — copy it now.
  2. Submit a job:
curl -X POST https://convertere.io/v1/jobs \
  -H "Authorization: Bearer cvio_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "tasks": {
      "import-1": { "operation": "import/upload" },
      "convert-1": {
        "operation": "convert",
        "input": "import-1",
        "input_format": "png",
        "output_format": "webp"
      },
      "export-1": { "operation": "export/url", "input": "convert-1" }
    }
  }'
  1. Upload your file to the uploadUrl returned on the import-1 task, using a PUT request with the file as the body.
  2. Poll GET /v1/jobs/{id} until status is finished, or register a webhook and wait for the notification.
  3. Download the result from the url on the export-1 task.

Authentication

Every request (except GET /v1/openapi.json) needs a header:

Authorization: Bearer cvio_live_...

or equivalently X-API-Key: cvio_live_.... Keys come in two modes:

  • live — real conversions, debits your organization's credit balance.
  • test — the full flow runs (status transitions, webhooks, a downloadable result), but no worker is ever contacted and nothing is charged. Use this to build and rehearse your integration before switching to a live key.

A missing, revoked, or expired key returns 401 with no distinguishing signal between those cases.

The task DAG

Requests use a minimal task graph — one import/upload, one convert, one export/url, chained by name:

{
	"tasks": {
		"import-1": { "operation": "import/upload" },
		"convert-1": {
			"operation": "convert",
			"input": "import-1",
			"input_format": "png",
			"output_format": "webp",
			"options": { "quality": 80 }
		},
		"export-1": { "operation": "export/url", "input": "convert-1" }
	},
	"tag": "optional-client-reference"
}

tag is an optional string echoed back on every status response and webhook payload, for correlating a job with your own records. options passes through unchanged to the target format's conversion options.

This is deliberately not a byte-identical clone of any other provider's API — response fields, status values, and errors follow this platform's own conventions.

Job status

Credits are measured in reference conversion-minutes: 1 credit = 1 reference conversion-minute. A completed job costs max(1, ceil(billable_ms × speed_factor / 60000) × weight) credits. The speed factor is 1 by default. Weights are 2 for video transcodes and iWork, 4 for PDF-to-Office, and 1 for other conversions. Failed or cancelled jobs cost 0 credits. credits_estimated is the reserved estimate; credits_used is the final charge.

GET /v1/jobs/{id} returns:

{
	"id": "...",
	"status": "processing",
	"mode": "single",
	"testMode": false,
	"credits_estimated": 3,
	"tasks": [
		{ "name": "import-1", "operation": "import/upload", "status": "finished" },
		{ "name": "convert-1", "operation": "convert", "status": "processing" },
		{ "name": "export-1", "operation": "export/url", "status": "waiting" }
	],
	"createdAt": "2026-07-31T12:00:00Z"
}

status is one of waiting, processing, finished, error. Once finished, credits_used and the export-1 task's result.url are present. Polling this endpoint also opportunistically advances the job the moment your upload is detected — you don't need to call anything else after uploading.

Capability catalog

GET /v1/operations lists every currently supported conversion with an estimated cost:

{
	"operations": [
		{
			"input_format": "png",
			"output_format": "webp",
			"group": "image",
			"credits_estimated": 1
		}
	]
}

Webhooks

Register an endpoint from the dashboard to receive job.finished / job.failed notifications instead of polling:

{
	"event": "job.finished",
	"job": { "...": "same shape as GET /v1/jobs/{id}" },
	"sentAt": "2026-07-31T12:05:00Z"
}

Each delivery is signed:

X-Webhook-Signature: sha256=<hex HMAC-SHA256 of the raw body, keyed by your endpoint's secret>

Verify it (Node example):

import { createHmac, timingSafeEqual } from "node:crypto";

function isValid(rawBody, header, secret) {
	const expected = `sha256=${createHmac("sha256", secret).update(rawBody).digest("hex")}`;
	return timingSafeEqual(Buffer.from(header), Buffer.from(expected));
}

Failed deliveries retry up to 5 times over roughly 80 minutes. Your job's true status via GET /v1/jobs/{id} is always authoritative, independent of whether a webhook ever successfully delivered.

Errors

{
	"error": {
		"code": "insufficient_credits",
		"message": "...",
		"details": { "credits_required": 4, "credits_available": 2 }
	}
}
StatusMeaning
400Malformed request, or the source/target format pair isn't supported
401Missing, invalid, revoked, or expired API key
402insufficient_credits: balance can't cover the estimate; details includes credits_required and credits_available
404Job not found, or not owned by this specific key
409Idempotency-Key reused with a different request body
429Rate limit or per-key concurrency cap exceeded — see Retry-After

Rate limits

60 requests/minute per API key, and at most 3 jobs in progress per key at once. Both scope to the individual key, not the organization — an org with multiple keys effectively gets a multiple of each limit.

Idempotency

Pass an Idempotency-Key header on POST /v1/jobs to safely retry after a network failure. The same key with the same request body returns the original job instead of creating a new one; the same key with a different body returns 409.

On this page