bulk-convert

API reference · v1

Bulk Convert HTTP API

Convert files programmatically with a single POST. Send a URL to the source file plus the target format; receive a signed download URL. No SDK required.

Quickstart

The API has one endpoint: POST /api/v1/convert. Authenticate with a bearer token, send a JSON body with two fields, and receive a signed URL pointing at the converted file. A typical conversion finishes in 2–10 seconds; the request blocks until the file is ready (up to 3 minutes), so you don't need to poll anything yourself.

bash
curl -X POST https://bulkconvert.io/api/v1/convert \
  -H "Authorization: Bearer bc_live_xxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "source_url": "https://example.com/report.pdf",
    "to": "docx"
  }'
Use a real, publicly fetchable URL for source_url. CloudConvert pulls the file server-side, so local paths, localhost, and credentials-required URLs won't work. Signed S3 / R2 / Vercel Blob URLs are ideal.

Authentication

Every request needs an Authorization: Bearer <api_key> header. API keys look like bc_live_ followed by 26 random characters.

Keys are emailed to you the moment your API-tier Stripe subscription activates. Cancelling the subscription revokes the key within seconds— every request re-checks the key against our KV store, so there's no grace period and no caching. Treat the key like a password: store it as an environment variable, never commit it.

Pro and Bulk plans also produce bc_live_ keys, but those work only on the web converter. Calling /api/v1/convert with a Pro or Bulk key returns 403 — API access requires the API plan.

Base URL & versioning

https://bulkconvert.io/api/v1

The /v1 in the path is the API contract version. We version on breaking changes only. Any new optional field — or a new conversion pair — is added without bumping the version. When we do ship a v2, v1 stays live for at least 12 months and you get an email 90 days before sunset.

POST /api/v1/convert

Synchronous conversion. Blocks until the file is ready.

Request body

FieldTypeRequiredDescription
source_urlstringyesPublicly fetchable URL of the source file. Must end in a file extension (or include one in the path) so we can detect the source format.
tostringyesTarget format, lowercase, no dot. e.g. "docx", "mp3", "webp". See Supported conversions.

Success response — 200

json
{
  "ok": true,
  "download_url": "https://eu-central.storage.cloudconvert.com/tasks/.../report.docx?...",
  "filename": "report.docx",
  "size": 184320,
  "job_id": "b4d8ded1-4b10-4956-a5c3-83732c29459d",
  "tier": "api",
  "usage": {
    "used": 385,
    "quota": 10000,
    "remaining": 9615,
    "reset_at": "2026-06-01T00:00:00.000Z"
  }
}
FieldDescription
download_urlSigned URL to the converted file. Expires after 24 hours.
filenameSuggested filename for the output.
sizeOutput file size in bytes.
job_idInternal job ID — quote this if you open a support ticket.
tierAlways "api" — confirms the key tier that processed this request.

Error response

json
{
  "error": "conversion pdf → xyz not supported"
}

Errors always include an error string. Some also include detail with the upstream message, or hints like upgrade_url. The HTTP status is meaningful — see the table.

StatuserrorWhen this happens
400invalid json bodyRequest body didn't parse as JSON.
400source_url and `to` are requiredMissing one of the required fields.
400couldn't detect source format from URLThe URL doesn't end in a file extension. Append `.pdf`, `.mp4` etc. or use a `?ext=pdf` style query string the server can strip.
400conversion <from> → <to> not supportedThe {from, to} pair isn't in the catalog. See Supported conversions below.
401missing Authorization: Bearer <api_key>No bearer token in the request.
401invalid or revoked api keyThe key was never valid, or the subscription was cancelled.
402monthly quota exhaustedYou've used your tier's monthly conversion allowance. Response body contains `used`, `quota`, and `reset_at`. Counter resets at 00:00 UTC on the 1st.
403API access requires the API planYour key is valid but on a Pro/Bulk tier. Upgrade at /pricing#api.
502conversion failedCloudConvert returned an error mid-job. The `detail` field has the upstream message; retrying with the same source usually works. We don't bill failures — the usage counter rolls back.
503conversion backend not yet configuredOnly happens during a brief outage if our CloudConvert key is missing or revoked. Retry in a minute.

Examples

cURL

bash
curl -X POST https://bulkconvert.io/api/v1/convert \
  -H "Authorization: Bearer bc_live_xxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "source_url": "https://example.com/report.pdf",
    "to": "docx"
  }'

Node.js

js
// Node 18+ (built-in fetch)
const res = await fetch("https://bulkconvert.io/api/v1/convert", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BULK_CONVERT_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    source_url: "https://example.com/report.pdf",
    to: "docx",
  }),
});

if (!res.ok) {
  const err = await res.json();
  throw new Error(`${res.status}: ${err.error}`);
}

const { download_url, filename, size, job_id } = await res.json();
console.log(`Converted ${filename} (${size} bytes) — job ${job_id}`);
console.log("Download from:", download_url);

Python

py
import os
import requests

resp = requests.post(
    "https://bulkconvert.io/api/v1/convert",
    headers={"Authorization": f"Bearer {os.environ['BULK_CONVERT_KEY']}"},
    json={
        "source_url": "https://example.com/report.pdf",
        "to": "docx",
    },
    timeout=300,  # conversions can take up to 3 minutes
)
resp.raise_for_status()
data = resp.json()
print(f"Converted {data['filename']} ({data['size']} bytes)")
print("Download:", data["download_url"])

GET /api/v1/usage

Read the calling key's current-month conversion count without performing a conversion. Free to hit as often as you want — no rate limit, no CloudConvert call.

bash
curl https://bulkconvert.io/api/v1/usage \
  -H "Authorization: Bearer bc_live_xxxxxxxxxxxxxxxxxxxxxxxxxx"

Response

json
{
  "tier": "api",
  "used": 384,
  "quota": 10000,
  "remaining": 9616,
  "reset_at": "2026-06-01T00:00:00.000Z",
  "key_prefix": "bc_live_xxxxxx"
}
FieldDescription
tierYour subscription tier — pro, bulk, or api.
usedConversions consumed this UTC month.
quotaMonthly cap, or null when the tier is unlimited (Bulk).
remainingmax(0, quota − used), or null when quota is null.
reset_atISO timestamp of the next month boundary (UTC). Counter zeros at that moment.
key_prefixFirst 16 chars of your key — handy for support tickets without leaking the secret.
Every successful POST /api/v1/convert response also includes the same usageobject inline, so most clients won't need to call /usage at all — only hit it for dashboards or pre-flight checks.

Supported conversions

33 conversion pairs across six categories. Pass the to column as the to field in your request; the source format is detected from the source_url extension.

document

fromtoDescription
pdfdocxPDF → Word
docxpdfWord → PDF
pdfhtmlPDF → HTML
htmlpdfHTML → PDF
pdfmdPDF → Markdown
mdhtmlMarkdown → HTML
mdpdfMarkdown → PDF
htmlmdHTML → Markdown
csvxlsxCSV → Excel
xlsxcsvExcel → CSV
csvjsonCSV → JSON

audio

fromtoDescription
mp3wavMP3 → WAV
wavmp3WAV → MP3
mp3m4aMP3 → M4A
mp3flacMP3 → FLAC
flacmp3FLAC → MP3
mp4mp3MP4 (audio) → MP3

video

fromtoDescription
mp4movMP4 → MOV
movmp4MOV → MP4
mp4webmMP4 → WebM
webmmp4WebM → MP4
mp4gifMP4 → GIF

image

fromtoDescription
pngjpgPNG → JPG
jpgpngJPG → PNG
pngwebpPNG → WebP
webppngWebP → PNG
heicjpgHEIC → JPG
pngsvgPNG → SVG

archive

fromtoDescription
ziptarZIP → TAR
tarzipTAR → ZIP
7zzip7Z → ZIP

ebook

fromtoDescription
epubpdfEPUB → PDF
epubmobiEPUB → MOBI
Need a pair that's not listed? Email support@bulkconvert.io — most CloudConvert-supported pairs can be added in under an hour.

Limits & quotas

Per request

  • Max file size: 5 GB on the API tier
  • Max conversion time: 3 minutes. After that the request returns 502 and you can retry.
  • Download URL lifetime: 24 hours from when the job finishes. Mirror to your own storage if you need it longer.

Concurrency

No per-account concurrency cap on the API tier — we forward to CloudConvert and they queue. In practice expect ~10 simultaneous jobs to all start processing immediately.

Per tier — all plans

TierPriceConversions / dayMax file sizeAPI access
FreeFree310 MBNo
Pro€9/mo100100 MBNo
Bulk€19/moUnlimited1 GBNo
API€49/moUnlimited5 GBYes

Best practices

Hosting the source file

The fastest pattern is to upload to S3 / R2 / Vercel Blob with a short-lived signed GET URL (5–15 minutes is plenty), then send that URL as source_url. CloudConvert pulls it once, within the request, so the signed URL only needs to outlive that single fetch.

If the source URL doesn't have a file extension at the end of the path, the request will fail with a 400. The cleanest fix is to include the extension in your signed-URL filename — e.g. https://your-bucket/abc123.pdf?X-Amz-.... Any query string after the extension is fine; we strip it before detection.

Idempotency & retries

Conversions aren't idempotent — each call creates a new CloudConvert job and costs credits. If a request fails with a 5xx, retry safely; CloudConvert deduplicates upload-only failures on their side, so worst case you'll get a fresh job_id for the second attempt.

For long-tail flakiness — usually transient CloudConvert engine errors — a single retry with the same body fixes the problem 95% of the time. We recommend at most 3 retries with exponential backoff (1s, 4s, 16s).

Downloading the result

The download_urlpoints at CloudConvert's object storage, not at us. It's a regular signed HTTPS URL — no auth header needed, just stream it down with the HTTP client of your choice. If you need to store it long-term, copy it to your own bucket before the 24 h expiry.

Webhooks (not yet available)

We don't support webhook callbacks yet. The endpoint is synchronous — the response only returns once the file is ready. For very large files (1 GB+), set your HTTP client's read timeout to at least 300 seconds.

Help & support

Email support@bulkconvert.io with your job_id for the failing request. Replies usually come within one business day. Status page and changelog are coming soon.