Conversions

Synchronous conversion

POST /v1/convert — submit a file, get the converted file back inline.

POST /v1/convert is the simplest way to convert a file. Submit a multipart/form-data upload; the server processes the conversion and returns the converted bytes in the response body.

When to use it #

  • Files under 100 MB
  • You can wait synchronously for the result (typical: 50ms–60s depending on route)
  • You don't need a webhook callback

For files larger than 100 MB, or for predictable async behaviour, use POST /v1/jobs.

Request #

POST /v1/convert HTTP/1.1
Host: changethisfile.com
Authorization: Bearer ctf_sk_...your_key_here...
Content-Type: multipart/form-data; boundary=…

--…
Content-Disposition: form-data; name="file"; filename="photo.png"
Content-Type: application/octet-stream

<binary>
--…
Content-Disposition: form-data; name="target"

jpg
--…
Content-Disposition: form-data; name="quality"

85
--…--
FieldTypeRequiredDescription
filebinaryyesThe input file.
targetstringyesTarget format extension (case-insensitive). The only field most callers need.
sourcestringnoSource format extension. Auto-detected from the filename and (if needed) magic bytes. Pass it explicitly only for renamed uploads.
qualityinteger 1–100noOptional quality preset for lossy image / video / audio routes.

Optional headers #

HeaderDescription
Idempotency-KeyStripe-style idempotency key (≤128 chars). See Idempotency.
AcceptDefault is application/octet-stream (binary). Set Accept: application/json to receive a JSON envelope with a signed download URL instead.

Response #

200 OK — converted file inline #

HTTP/1.1 200 OK
Content-Type: image/jpeg
Content-Disposition: attachment; filename="photo.jpg"
X-Conversion-Time: 412ms
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 9
X-RateLimit-Reset: 1735689600

<binary JPEG>
HeaderDescription
Content-DispositionSuggested filename for the converted file.
X-Conversion-TimeEnd-to-end conversion duration.
X-RateLimit-Limit / X-RateLimit-Remaining / X-RateLimit-ResetPer-minute rate-limit window state.

202 Accepted — too large, queued #

If the file exceeds the inline-processing threshold (~100 MB), the server queues a job and returns:

{
  "job_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "status": "queued",
  "status_url": "https://changethisfile.com/v1/jobs/f47ac10b-58cc-4372-a567-0e02b2c3d479"
}

Poll GET /v1/jobs/{job_id} until status is completed.

Quality presets #

Route familyquality interpretation
Lossy image (JPG, WebP, AVIF)Encoder quality 1-100 (passed straight to libvips / sharp).
Lossy video (MP4, WebM)Mapped to FFmpeg -crf curve (1=highest CRF/lowest quality, 100=lowest CRF).
Lossy audio (MP3, AAC, OGG)Mapped to bitrate presets (e.g. 32 kb/s @ q=10, 320 kb/s @ q=100).
Document, ebook, archive routesIgnored.

Code samples #

No SDK to install — just an HTTP POST.

curl -X POST https://changethisfile.com/v1/convert \
     -H "Authorization: Bearer ctf_sk_..." \
     -F "file=@photo.png" \
     -F "target=jpg" \
     -F "quality=85" \
     --output photo.jpg
import requests

with open("photo.png", "rb") as f:
    r = requests.post(
        "https://changethisfile.com/v1/convert",
        headers={"Authorization": "Bearer ctf_sk_..."},
        files={"file": f},
        data={"target": "jpg", "quality": 85},
    )
r.raise_for_status()
open("photo.jpg", "wb").write(r.content)
print(r.headers.get("X-Conversion-Time"), len(r.content), "bytes")
import { readFileSync, writeFileSync } from 'node:fs';

const form = new FormData();
form.append('file', new Blob([readFileSync('photo.png')]), 'photo.png');
form.append('target', 'jpg');
form.append('quality', '85');

const r = await fetch('https://changethisfile.com/v1/convert', {
  method: 'POST',
  headers: { Authorization: 'Bearer ctf_sk_...' },
  body: form,
});
if (!r.ok) throw new Error(`${r.status} ${await r.text()}`);
writeFileSync('photo.jpg', Buffer.from(await r.arrayBuffer()));

Failure modes #

HTTPerror.codeCause
400bad_requestMissing field, invalid source/target, or unparseable multipart body.
401invalid_api_keyMissing or revoked key.
413file_too_largeFile exceeds the active plan's max size.
422unsupported_routeThis source→target combo isn't in our routing table. See Formats.
429rate_limited / quota_exceededPer-minute throttle or monthly quota hit.
502service_unavailableConversion engine temporarily unavailable. Safe to retry.