Public API

Uploads

Give a job its file with a single upload, a multipart upload for large files, or an HTTPS URL.

A job's first task decides how its file arrives:

Import taskUse it when
import/uploadYou have the file. PUT it to the presigned upload URL (up to 100 MB in one request, or in parts above that).
import/urlThe file is already online at an HTTPS URL. The platform fetches it for you.

The current maximum input size is 1 GB. A larger file is refused with file_too_large (see Errors).

Single upload

Create the job with an import/upload task, then PUT the bytes to the uploadUrl in tasks[0].result.uploadUrl. The upload URL is valid for one hour and is presigned, so the request does not carry your API key.

curl -X PUT --upload-file input.png "$UPLOAD_URL"
import { readFile } from "node:fs/promises";
await fetch(uploadUrl, { method: "PUT", body: await readFile("input.png") });
with open("input.png", "rb") as source:
    response = requests.put(upload_url, data=source)
response.raise_for_status()

You do not need to tell the API the upload is done. The job notices the file the next time it is fetched, polled with ?wait=, or checked by the platform, and starts converting.

Multipart upload (over 100 MB)

For large files, upload in parts. All four calls need the job.write scope and apply to a job whose import task is import/upload.

1. Start. Declare the exact file size. The size is checked against your plan limit before anything is uploaded.

POST /jobs/{id}/uploads/multipart
Content-Type: application/json

{"size_bytes": 250000000}
{ "upload_id": "2~abc123", "part_size": 26214400, "parts_expected": 10 }

part_size is the size of every part except the last (25 MB today); parts_expected is how many parts that makes. Starting twice returns the same upload (status 200 instead of 201).

2. Get part URLs. Ask for up to 100 part numbers at a time (numbering starts at 1). Each URL is valid for 15 minutes; ask again if one expires.

POST /jobs/{id}/uploads/multipart/parts
Content-Type: application/json

{"part_numbers": [1, 2]}
{
	"parts": [
		{
			"part_number": 1,
			"url": "https://…",
			"expires_at": "2026-10-10T12:15:00.000Z"
		},
		{
			"part_number": 2,
			"url": "https://…",
			"expires_at": "2026-10-10T12:15:00.000Z"
		}
	]
}

3. Upload each part. PUT the part's bytes to its url (no API key). Keep the ETag response header of every part, exactly as returned.

4. Complete. Send every part number with its ETag.

POST /jobs/{id}/uploads/multipart/complete
Content-Type: application/json

{"parts": [{"part_number": 1, "etag": "\"9b2cf5…\""}, {"part_number": 2, "etag": "\"4f1a07…\""}]}
{ "size_bytes": 250000000 }

The job starts converting by itself; poll it or wait for a webhook. If the assembled file turns out larger than your limit, the call returns 413 file_too_large and the job ends as error with nothing charged.

To discard an unfinished upload, call POST /jobs/{id}/uploads/multipart/abort (204). You can then start again or use the single-upload URL.

import { open } from "node:fs/promises";

// `api(path, body)` is your small helper that POSTs JSON with the Authorization header.
const { upload_id, part_size, parts_expected } = await api(
	`/jobs/${id}/uploads/multipart`,
	{ size_bytes: size },
);
const file = await open("big.mov");
const parts = [];
for (let first = 1; first <= parts_expected; first += 100) {
	const numbers = Array.from(
		{ length: Math.min(100, parts_expected - first + 1) },
		(_, i) => first + i,
	);
	const { parts: urls } = await api(`/jobs/${id}/uploads/multipart/parts`, {
		part_numbers: numbers,
	});
	for (const { part_number, url } of urls) {
		const { buffer, bytesRead } = await file.read(
			Buffer.alloc(part_size),
			0,
			part_size,
			(part_number - 1) * part_size,
		);
		const put = await fetch(url, {
			method: "PUT",
			body: buffer.subarray(0, bytesRead),
		});
		parts.push({ part_number, etag: put.headers.get("etag") });
	}
}
await api(`/jobs/${id}/uploads/multipart/complete`, { parts });

Import from a URL

Use an import/url task instead of import/upload. Nothing needs to be uploaded; the job starts fetching right after it is created and the import task shows processing while it runs.

{
	"tasks": {
		"import-1": {
			"operation": "import/url",
			"url": "https://files.example.com/image.png",
			"headers": { "Authorization": "Bearer source-token" }
		},
		"convert-1": {
			"operation": "convert",
			"input": "import-1",
			"input_format": "png",
			"output_format": "webp"
		},
		"export-1": { "operation": "export/url", "input": "convert-1" }
	}
}

Rules:

  • The URL must be https:// and at most 2048 characters, with no embedded credentials. Plain http:// is refused with url_import_failed (details.reason is not_https).
  • The optional headers (up to 10) are sent to the remote host, for example for an authenticated download. They are stored encrypted, and dropped if the host redirects to a different origin.
  • Up to 3 redirects are followed. Every hop must be https, and every address the host resolves to must be public: private, loopback, link-local and cloud-metadata addresses are blocked, also after a redirect or a DNS change.
  • The file must fit your plan's size limit.

A URL that cannot be fetched fails the job: status becomes error and error.code is url_import_failed with error.details.reason one of blocked_address, not_https, too_many_redirects, http_status or network. A file that is too large fails with file_too_large. Nothing is charged for a failed import.

curl -X POST "$BASE/jobs" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -d '{"tasks":{"import-1":{"operation":"import/url","url":"https://files.example.com/image.png"},"convert-1":{"operation":"convert","input":"import-1","input_format":"png","output_format":"webp"},"export-1":{"operation":"export/url","input":"convert-1"}}}'
await fetch("https://api.convertere.io/v1/jobs", {
	method: "POST",
	headers: {
		Authorization: `Bearer ${key}`,
		"Content-Type": "application/json",
	},
	body: JSON.stringify({
		tasks: {
			"import-1": {
				operation: "import/url",
				url: "https://files.example.com/image.png",
			},
			"convert-1": {
				operation: "convert",
				input: "import-1",
				input_format: "png",
				output_format: "webp",
			},
			"export-1": { operation: "export/url", input: "convert-1" },
		},
	}),
});
requests.post("https://api.convertere.io/v1/jobs", headers={"Authorization": f"Bearer {key}"}, json={"tasks": {
    "import-1": {"operation": "import/url", "url": "https://files.example.com/image.png"},
    "convert-1": {"operation": "convert", "input": "import-1", "input_format": "png", "output_format": "webp"},
    "export-1": {"operation": "export/url", "input": "convert-1"}
}})

On this page