DEVELOPERS / API v1
Images in. Editable files out.
Submit a conversion, follow its progress, and download SVG and PowerPoint files.
1. Create an API key
Sign in with Google and open API keys. Keep your key on your server and send it as a Bearer token. Reading tasks requires conversions:read; creating and deleting requires conversions:write. Keys share your account’s conversion credits.
2. Check credits
curl https://unraster.ai/v1/conversions/quote \
-H "Authorization: Bearer $UNRASTER_API_KEY"The response lists exact prices in prices: SVG costs 1 credit, PPTX costs 2 credits, and requesting both costs 3 credits. It also includes your integer available_credits, the upload limit, and supported outputs.
{"billing_version":"fixed-v1","prices":{"svg":1,"pptx":2},"available_credits":10,"reserved_credits":0,"max_upload_bytes":26214400,"outputs":["svg","pptx"]}The exact price is reserved when you submit. Only requested outputs that are ready to download are charged; credits for unavailable outputs are released. Progress checks and downloads are free.
First PowerPoint free. A new API account’s first PowerPoint conversion is free: one per account and per Google sign-in, for requests with outputs=pptx only. While it is unused, the quote includes "agent_trial_pptx_remaining":1. The free task has "free_reason":"agent_trial" and no billing; you get the full PowerPoint file. If the conversion fails, or ends in needs_review without a ready PowerPoint, the free conversion stays available; if it ends in needs_review with a ready PowerPoint, you get that file and the free conversion is used. After it is used, each PowerPoint costs 2 credits.
3. Submit one image
Upload a static PNG, JPEG, or WebP, up to 25 MiB and 25 million pixels. Send outputs=svg, outputs=pptx, or outputs=svg,pptx. Generate a unique REQUEST_ID and retain it with your request.
curl https://unraster.ai/v1/conversions \
-H "Authorization: Bearer $UNRASTER_API_KEY" \
-H "Idempotency-Key: $REQUEST_ID" \
-F "file=@slide.png" \
-F "outputs=svg,pptx"A new task returns HTTP 202. Save its ID.
{
"id": "cnv_example", "status": "queued", "stage": null,
"version_id": null, "error": null,
"outputs": [
{ "format": "svg", "status": "pending" },
{ "format": "pptx", "status": "pending" }
]
}If the connection fails, retry the same image and output selection with the same idempotency key. A changed request using that key returns HTTP 409. Do not generate another key just because a response was lost.
4. Check progress and download
curl https://unraster.ai/v1/conversions/$TASK_ID \
-H "Authorization: Bearer $UNRASTER_API_KEY"| Status | What to do |
|---|---|
| queued | Waiting to start; check again shortly. |
| processing | Still working; poll every few seconds. |
| completed | Download outputs whose status is ready. |
| needs_review | Download ready outputs; unavailable outputs are not charged. |
| failed | Review your source. No unavailable output is charged. |
| deleted | This task is no longer available. |
outputs is an array. Ready outputs contain a url and expires_at. Paid, settled results use a public link under https://files.unraster.ai that works without an API key for seven days after completion. Anyone holding that link can download the file, so keep it private. A relative URL is a short-lived account-authenticated download; resolve it against the API origin and send your Bearer key only to that origin. Fetch the task again to refresh a relative link. A seven-day public link cannot be extended by fetching the task again.
Deleting with DELETE /v1/conversions/{id} revokes its download links. Deleting a completed task does not refund a successful conversion.
5. Receive webhooks
Add a public HTTPS endpoint in Webhook settings. Save the signing secret when it appears; it is shown only once. Notifications include conversion.completed, conversion.failed, and conversion.needs_review.
Preserve the exact request body bytes. The webhook-id header identifies the event. The webhook-signature header has the form t=TIMESTAMP,v1=HEX_SIGNATURE.
HMAC-SHA256(
hexDecode(signing_secret),
timestamp + "." + webhook_id + "." + raw_body
)Use a timing-safe signature comparison, reject timestamps outside a five-minute window, and verify the event ID matches the body. Deduplicate by event ID before applying your business action. Events can arrive repeatedly or out of order; query the current task for its latest status and download links. Return 2xx after durably accepting an event. Failed deliveries retry for a limited period, so retain polling as a recovery option.
6. Convert in batches
Convert many images with one batch and download the results as one zip. Subscribers can batch on every paid plan: up to 10 files on the $19.90/month plan, 50 on the $49.90/month plan and 100 on the $99.90/month plan (with priority). Through an API key, batches need a plan with API batch access (150 and 333). An account that has credits but no subscription can batch on the website, up to 10 files. Accounts with neither a subscription nor credits, and API keys without batch access, get 403 plan_required (max 0). A batch uses a single output, svg or pptx. Keys need conversions:write to create, upload, start and cancel, and conversions:read to read.
Endpoints
POST /v1/batchesDeclare the files and reserve credits. Needs Idempotency-Key.PUT /v1/batches/{id}/files/{idx}Upload one file as multipart field file.POST /v1/batches/{id}/startBegin conversion. Files not uploaded are released.POST /v1/batches/{id}/cancelStop waiting files; running files are charged only if delivered.GET /v1/batchesYour last 50 batches, newest first, under batches.GET /v1/batches/{id}State, counts, credits and per-file status.GET /v1/batches/{id}/archiveZip of every delivered file.
Flow
- Create the batch, declaring each file’s
name,sizeandtype. Save the returnedidand each item’sidx. - Upload each file to
/files/{idx}. - Start the batch.
- Poll
GET /v1/batches/{id}every few seconds untilfinished_atis set, or wait for thebatch.finishedwebhook. - Download the zip from
/archive. For a single file, useGET /v1/conversions/{task_id}with thetask_idfrom the batch.
curl https://unraster.ai/v1/batches \
-H "Authorization: Bearer $UNRASTER_API_KEY" \
-H "Idempotency-Key: $REQUEST_ID" \
-H "Content-Type: application/json" \
-d '{"output":"svg","files":[
{"name":"a.png","size":183204,"type":"image/png"},
{"name":"b.jpg","size":96311,"type":"image/jpeg"}
]}'A new batch returns HTTP 201 (200 if you repeat the same request with the same key).
{
"id": "job_0b6f6c1e-5c1a-4a58-9d42-2f3f6e0a9a11",
"state": "open",
"item_limit": 50,
"items": [
{ "idx": 0, "task_id": "cnv_example_0" },
{ "idx": 1, "task_id": "cnv_example_1" }
],
"reserved_credits": 2
}The rest of the flow:
BATCH=job_0b6f6c1e-5c1a-4a58-9d42-2f3f6e0a9a11
# Upload each file to its idx (PNG, JPEG or WebP)
curl -X PUT https://unraster.ai/v1/batches/$BATCH/files/0 \
-H "Authorization: Bearer $UNRASTER_API_KEY" \
-F "file=@a.png"
curl -X PUT https://unraster.ai/v1/batches/$BATCH/files/1 \
-H "Authorization: Bearer $UNRASTER_API_KEY" \
-F "file=@b.jpg"
# Start the batch; files not uploaded are released
curl -X POST https://unraster.ai/v1/batches/$BATCH/start \
-H "Authorization: Bearer $UNRASTER_API_KEY"
# Poll until finished_at is set (after a cancel, files already converting still finish)
curl https://unraster.ai/v1/batches/$BATCH \
-H "Authorization: Bearer $UNRASTER_API_KEY"
# Download every delivered file as one zip
curl -o results.zip https://unraster.ai/v1/batches/$BATCH/archive \
-H "Authorization: Bearer $UNRASTER_API_KEY"A batch has ended when finished_at is set. Its final state is completed (no file failed; files you never uploaded are simply not converted), partial, failed or cancelled. After a cancel the state reads cancelled at once, but files already converting still finish and are charged if delivered: wait for finished_at before reading charged_credits or downloading the archive. An open batch that you never start is closed after 24 hours and its credits are released.
{
"id": "job_0b6f6c1e-5c1a-4a58-9d42-2f3f6e0a9a11",
"output": "svg",
"state": "partial",
"counts": { "done": 1, "failed": 1, "running": 0, "waiting": 0, "cancelled": 0 },
"charged_credits": 1,
"reserved_credits": 0,
"items": [
{ "idx": 0, "file_name": "a.png", "state": "done",
"task_id": "cnv_example_0", "error_code": null, "credits": 1 },
{ "idx": 1, "file_name": "b.jpg", "state": "failed",
"task_id": "cnv_example_1", "error_code": "conversion_failed", "credits": 0 }
],
"item_count": 2,
"created_at": "2026-11-08T10:00:00.000Z",
"started_at": "2026-11-08T10:00:20.000Z",
"finished_at": "2026-11-08T10:01:05.000Z"
}Credits
Creating a batch reserves credits for every file at once. A subscriber pays 1 credit per SVG and 2 credits per PowerPoint; a credit-pack batch pays one credit more per file (2 per SVG, 3 per PowerPoint). If your balance does not cover the whole batch, the request returns 402 insufficient_credits and nothing is reserved. Only delivered files are charged. Failed, cancelled and never-uploaded files are released, not charged. Progress checks and downloads are free.
Limits
- Files: PNG, JPEG or WebP, up to 20 MiB each.
- Files per batch: 10 on the $19.90/month plan, 50 on the $49.90/month plan, 100 on the $99.90/month plan. Credit-pack accounts: 10 files per batch, on the website only.
Idempotency-Keyis required when creating a batch. Retry a lost response with the same key.- Uploads: 240 per minute per account.
- Delivered files are kept for seven days. After that,
/archivereturns410 batch_expired. While the batch is still running it returns409 batch_not_finished; if nothing was delivered it returns 404.
The zip
Each delivered file is named {idx}-{original name}.svg (or .pptx), with duplicate names made unique. Files that were not delivered are not included.
The batch.finished webhook
Once a batch reaches a final state, your enabled webhook endpoints receive batch.finished. It is sent once per batch, for batches created through the API. Signing, headers and retries work as in the previous section. The body has counts and charged credits but no download links; fetch the batch or the archive for those. Every file also sends its usual conversion.* event. Events may arrive in any order, so deduplicate by id. A batch that is cancelled or expires before any file starts sends no event.
{
"id": "evt_job_0b6f6c1e-5c1a-4a58-9d42-2f3f6e0a9a11_finished",
"type": "batch.finished",
"created_at": "2026-11-08T10:01:05.000Z",
"data": {
"id": "job_0b6f6c1e-5c1a-4a58-9d42-2f3f6e0a9a11",
"state": "partial",
"output": "svg",
"counts": { "done": 1, "failed": 1, "running": 0, "waiting": 0, "cancelled": 0 },
"charged_credits": 1
}
}Handle errors
Errors use {"error": {"code": "…", "message": "…"}}. Branch on error.code.
- 400 / 413 / 415: correct the request, file size, or format.
- 401 / 403: check your key and permissions.
- 402: insufficient available credits.
- 404: the task or file is unavailable to this account.
- 409: check for an idempotency conflict.
- 429: respect
Retry-Afterbefore retrying. - 503: retry later, preserving the original idempotency key.
Batch requests add these codes:
| Code | HTTP | Meaning |
|---|---|---|
| plan_required | 403 | Your plan has no batch API access. The body includes max. |
| job_too_large | 422 | More files than your plan allows. The body includes max and requested. |
| insufficient_credits | 402 | Not enough credits for the whole batch. The body includes required and available. |
| idempotency_key_required | 400 | Add an Idempotency-Key header to the create request. |
| idempotency_conflict | 409 | The key was already used with a different request. |
| invalid_request | 400 | Malformed or unexpected request body. |
| no_files_uploaded | 400 | Start was called before any file was uploaded. Upload at least one file, or cancel the batch to release the reserved credits. |
| file_already_uploaded | 409 | That index already holds a different file. Each index accepts one upload; the body includes idx. |
| conversion_not_found | 404 | No such batch for this key, or a file index outside the batch. |
| invalid_image | 400 | The file is not a single-frame PNG, JPEG or WebP, or the part’s Content-Type does not match the file. With curl, add the type: -F "file=@a.webp;type=image/webp". |
| unsupported_media_type | 415 | The request is not multipart/form-data. |
| upload_too_large | 413 | A file is over 20 MiB. |
| job_not_open | 409 | The batch was already started or closed; uploads are no longer accepted. |
| batch_not_finished | 409 | The archive is not ready; poll the batch until it finishes. |
| batch_expired | 410 | The seven-day delivery period has passed. |
| rate_limited | 429 | Too many requests. Wait for Retry-After. |