POST https://api.iconscout.com/v3/convert/{operation}
Convert an uploaded file between formats.
Most operations are synchronous: the converted file is returned directly in the response body with the appropriate Content-Type. There is no JSON envelope and no base64 — write the body straight to disk.
Lottie render operations (lottie-to-gif, lottie-to-webp, lottie-to-mp4, lottie-to-webm) are asynchronous because they can take tens of seconds. They return 202 Accepted with a job to poll via GET /v3/convert/jobs/{job}.
| Operation | Input | Output | Mode |
|---|---|---|---|
png-to-jpg | png | jpg | sync |
jpg-to-png | jpg | png | sync |
svg-to-png | svg | png | sync |
svg-to-jpg | svg | jpg | sync |
svg-to-pdf | svg | sync | |
lottie-optimize | json | json | sync |
lottie-to-dotlottie | json | lottie | sync |
svg-to-lottie | svg | json | sync |
lottie-to-gif | json | gif | async |
lottie-to-webp | json | webp | async |
lottie-to-mp4 | json | mp4 | async |
lottie-to-webm | json | webm | async |
Each call consumes API credits from your account's single credit balance. Call GET /v3/convert/operations for the current credit cost of each operation and whether your plan includes it — prices are configuration and are best read from the API rather than hard-coded. For plans and pricing, see iconscout.com/api.
A 403 means your plan does not include the capability, or the app has file conversion switched off in the API dashboard; a 429 means it does, but your credits are spent. Failed conversions are not charged.
Note that the upload is validated before entitlement is, so a malformed call to an operation your plan excludes reports the validation error first and the 403 only once the request is otherwise valid. Read available_on_your_plan from GET /v3/convert/operations to know before you call.
Idempotency-Key suppresses duplicate work. It does not replay a response, which is where it differs from the same header on Stripe or Square — read it as a duplicate guard.
| Key's original run | What a repeat gets |
|---|---|
| still in flight | 409 idempotency_in_progress |
| completed, async | 202 with the original job — poll it as normal |
| completed, sync | 409 idempotency_already_completed — the output is gone |
| failed and refunded | 409 idempotency_already_failed |
The sync row is the one to design around. A conversion that succeeded but whose response you lost to a network timeout has already been charged, and its bytes are not retained: the only way forward is a new key, which is a second charge. If that matters to your integration, prefer the async operations, whose job survives the round trip.
lottie-to-svg is performed client-side in the iconscout.com editor and has no API equivalent.
| Name | In | Type | Notes |
|---|---|---|---|
operation | path | string | required; one of svg-to-png, svg-to-jpg, svg-to-pdf, png-to-jpg, jpg-to-png, lottie-optimize, lottie-to-dotlottie, svg-to-lottie, lottie-to-gif, lottie-to-webp, lottie-to-mp4, lottie-to-webmConversion to perform. See the table above, or call GET /v3/convert/operations. |
Idempotency-Key | header | string | optional Optional. 16–128 URL-safe characters. A replay guard, not a replay: a key whose original run already completed is rejected rather than re-dispatched. For an async render the original job is returned, so the result is still reachable. A synchronous conversion streams its bytes and keeps no copy, so a repeated key gets a 409 and the output cannot be recovered — retry with a new key, and the original charge stands. |
| Status | Meaning |
|---|---|
200 | Converted file, returned directly. Synchronous operations only. |
202 | Render queued. Asynchronous operations only.
Location points at the job to poll and Retry-After gives the interval to poll it at — follow them rather than guessing, which is how a client ends up polling a 40-second render every 200ms. A 1080×1080 MP4 typically completes in well under a minute. |
400 | Validation error, or the uploaded file does not match the operation input format. data.reason is invalid_request, invalid_input_file, invalid_idempotency_key, async_operation or sync_operation. |
401 | Unauthorized — missing or invalid Client-ID / Client-Secret. data.reason is invalid_credentials. |
403 | Your plan does not include the capability this operation requires, or the app has file conversion switched off in the API dashboard. data.reason is entitlement_required for both; the message says which. Check available_on_your_plan on GET /v3/convert/operations before calling to avoid this round trip — it accounts for the plan and the app setting alike. |
404 | Unknown conversion operation. data.reason is unknown_operation. |
409 | This Idempotency-Key has already been used. data.reason is idempotency_in_progress, idempotency_already_completed or idempotency_already_failed — see the Idempotency section above for what each means and what is recoverable. |
413 | Uploaded file exceeds max_upload_kb for your plan. data.reason is invalid_request. |
422 | This client holds no active API subscription. data.reason is subscription_required. |
429 | Two different conditions, told apart by data.reason.
rate_limit_exceeded — too many requests this minute for this client. Transient; Retry-After says when to retry, and the X-RateLimit-* headers say what the budget is. Those headers are on every response the limiter admits, not only this one; only a 401, refused before the limiter runs, carries none.
quota_exhausted — the API credit allowance for the billing period is spent. A spend ceiling, not a request-rate limit: waiting does not clear it before the period rolls over. |
500 | The conversion service could not complete the request. data.reason is conversion_failed. Credits are not charged. |
504 | The conversion service did not respond in time. Credits are not charged. |
curl -X POST 'https://api.iconscout.com/v3/convert/svg-to-png' \
-H 'Client-ID: your-client-id' \
-H 'Client-Secret: your-client-secret' \
-F 'file=@logo.svg' \
-o logo.pngcurl -X POST 'https://api.iconscout.com/v3/convert/lottie-to-mp4' \
-H 'Client-ID: your-client-id' \
-H 'Client-Secret: your-client-secret' \
-F 'file=@animation.json' \
-F 'width=1080' \
-F 'height=1080'curl -X POST 'https://api.iconscout.com/v3/convert/svg-to-lottie' \
-H 'Client-ID: your-client-id' \
-H 'Client-Secret: your-client-secret' \
-F 'file=@icon.svg' \
-F 'preset=fade-in-up' \
-o animation.jsonimport requests
with open('logo.svg', 'rb') as handle:
resp = requests.post(
'https://api.iconscout.com/v3/convert/svg-to-png',
headers={
'Client-ID': 'your-client-id',
'Client-Secret': 'your-client-secret',
},
files={'file': handle},
)
resp.raise_for_status()
with open('logo.png', 'wb') as out:
out.write(resp.content)
print('credits charged:', resp.headers['X-Credits-Consumed'])const form = new FormData();
form.append('file', new Blob([await fs.readFile('animation.json')]), 'animation.json');
form.append('width', '1080');
form.append('height', '1080');
const res = await fetch('https://api.iconscout.com/v3/convert/lottie-to-mp4', {
method: 'POST',
headers: {
'Client-ID': 'your-client-id',
'Client-Secret': 'your-client-secret',
},
body: form,
});
const { response } = await res.json();
console.log(response.job.uuid, response.job.status);
Try it in the interactive reference →