Get a key

Reference

Developer documentation

One key for image, video, voice and LLMs, with the exact charge on every response.

01

Quickstart

A key, a POST, a finished image with its price attached.

  1. 01Create an account and add prepaid credit, from $5.
  2. 02Create an API key in the dashboard. It is shown once.
  3. 03Send the request below with that key.
curl
curl -X POST https://api.corent.tech/v1/images/generate \  -H "Authorization: Bearer $CORENT_API_KEY" \  -H "Content-Type: application/json" \  -d '{"prompt": "a fox reading a book", "tier": "air"}'
Response
{  "id": "...",  "status": "completed",  "images": [{ "url": "https://...", "width": 1024, "height": 1024 }],  "meta": { "model": "image-air", "cost_cents": 2, "duration_ms": 1500 }}

Images usually return inline with the charge. A slow render comes back as a job id to poll instead.

02

Authentication

Every request carries a bearer token from a key you created in the dashboard.

Header
Authorization: Bearer co_live_...
Base URL
https://api.corent.tech
Scope
Server-side only, never in browser code.

A key can be locked to specific models, or to tiers only, when you create it.

Keys are created, listed and revoked in the dashboard, where you also set each key's limits. The account endpoints behind that screen take a dashboard login, not an API key, so a leaked key can never create more keys or raise its own cap.

03

Images

POST/v1/images/generate

Generate an image. Returns inline unless you ask for a job.

FieldTypeRequiredDescription
promptstringyesWhat to generate.
tierstringnoair, lite, premium, pro or max_pro. Omit it if you pass model.
modelstringnoPin an exact model from GET /v1/models. Never substituted.
stylestringnoStyle hint, for example photorealistic.
aspect_ratiostringnoFor example 1:1, 16:9 or 9:16.
asyncbooleannotrue returns 202 with a job id. Use it for max_pro and other slow renders.
reference_image_urlsstring[]noOne to four public https images. The prompt is applied as an EDIT of them, so a face, character or product stays the same across generations. Served by premium and up; air and lite refuse it with a 400 naming the tiers that work.
nnumberno1 to 10. Each one is a real render at the normal price, so n=4 costs four images. meta.cost_cents is the total.
seednumbernoSame seed, prompt and model gives the same image, so you can render it again and change one thing. The seed used comes back in meta.seed even when you did not send one.
negative_promptstringnoWhat must NOT appear, for example text or a watermark.
source_image_urlstringnoStart from this picture instead of from noise. Pair it with strength.
strengthnumberno0 to 1. How far the render may travel from source_image_url. 0 keeps it, 1 ignores it.
output_formatstringnopng, jpeg or webp. png is the one that carries transparency.
transparentbooleannoTransparent background for logos and cutouts. Forces png.
width / heightnumbernoExplicit pixel size. Pass both or neither.
enhance_promptbooleannoCorent rewrites your prompt before dispatch to get a better render. false sends it exactly as typed.
customer_idstringno1 to 128 characters. Your own id for the end customer this render is for. Comes back on the receipt and filters GET /v1/account/usage.
Idempotency-KeyheadernoAny unique string up to 255 chars. A repeat returns the original job instead of billing twice.

Not every model takes every setting. Whatever the chosen model could not honour comes back in meta.unsupported_options, so an ignored setting never reads as an applied one.

Request body
{  "prompt": "a fox reading a book in a cozy library",  // one of air, lite, premium, pro, max_pro  "tier": "air",  // both optional  "style": "photorealistic",  "aspect_ratio": "1:1",  // optional; true returns 202, then poll /v1/jobs/{id}  "async": false}

POST/v1/images/edit

Regenerate only the masked region and leave every other pixel alone.

Different from reference_image_urls, which REGENERATES your subject from a reference, so its logo and colour are a likeness rather than a copy. Here the unmasked pixels are your own file, byte for byte, which is what an advertisement needs when the product carries someone else’s trademark. Send image_url, a black-and-white mask_url the same size (white is regenerated, black is preserved) and a prompt.

POST/v1/images/upscale

Make an existing image bigger and sharper. Send image_url, and an optional prompt to guide the added detail. Flat price, no tier.

POST/v1/images/remove-background

Cut the subject out. Returns a PNG with a real transparent background, for logos, product shots and compositing. Send image_url.

04

Video and jobs

POST/v1/videos/generate

Returns 202 with an id. Video takes one to five minutes and bills per second of output.

FieldTypeRequiredDescription
promptstringyesWhat to generate.
tierstringnoair, lite, premium, pro or max_pro. Omit it if you pass model.
modelstringnoPin an exact model from GET /v1/models. Never substituted.
duration_snumbernoSeconds of output. The tier menu carries the allowed range; corent-seedance-2.5 takes 4 to 30.
resolutionstringno720p, 1080p or 4k. Above the tier cap it clamps down, it never errors. corent-seedance-2.5 goes up to 1080p.
aspect_ratiostringnoFor example 16:9 or 9:16, or adaptive to follow the input frame or clip.
image_urlstringnoAnimate an existing image instead of generating from text alone.
audiobooleannoAsk for native sound. true routes only to models that actually render audio, so a silent model can never quietly serve the request; false prefers a silent one. GET /v1/tiers reports supports_audio per tier.
end_image_urlstringnoThe frame the clip should END on. With image_url this is the go-from-A-to-B effect.
camerastringnostatic, pan_left, pan_right, zoom_in, zoom_out, orbit_left, orbit_right, tilt_up or tilt_down.
negative_promptstringnoWhat must NOT appear in the clip.
seednumbernoReproducibility, same contract as images.
fpsnumbernoFrames per second, where the model offers a choice.
customer_idstringno1 to 128 characters. Your own id for the end customer. Rides on the job and its receipt.
Request body
{  "prompt": "a small sailboat crossing a calm harbor",  "tier": "premium",  "aspect_ratio": "16:9",  "duration_s": 5,  // tier-capped: clamped down, never rejected  "resolution": "720p",  // optional, image-to-video  "image_url": "https://..."}

GET/v1/jobs/{id}

Poll a job. The charge lands here when the video completes, next to the delivered pixel dimensions measured from the stored file.

Prefer GET /v1/jobs/{id}/stream, which is server-sent events, to avoid polling.

Or pass webhook_url on the request and the full result is POSTed to you when the job finishes. Add webhook_secret and every delivery carries X-Corent-Signature: t=<unix seconds>,v1=<hex>, where v1 is HMAC-SHA256 of the secret over "t.rawbody" (Stripe's scheme). Verify over the raw bytes, compare in constant time, and reject a t older than 300 seconds. Corent tries up to three times, at about 0, 2 and 6 seconds, and GET /v1/jobs/{id}/webhook_deliveries lists every attempt. An unsigned delivery only means no secret was given.

Response
{  "id": "...",  "status": "completed",  "videos": [{    "url": "https://...",    "width": 1920,    "height": 1080,    "resolution": "1080p",    "duration_s": 5  }],  "meta": { "model": "video-premium", "cost_cents": 162, "duration_ms": 94000 }}

POST/v1/jobs/{id}/cancel

Stop a job that has not finished and release its money hold. Charges nothing. A job that completed just before the cancel landed stays completed and stays billed for what was actually delivered, and the cancelled flag in the reply tells you which happened. Cancelling is best effort against the supplier: the render may already be running on their side, and that cost is ours to absorb, not yours.

Consistent characters, editing, and extending

Six more fields on POST /v1/videos/generate keep a character, product or place the same from shot to shot, change a clip you already have, or carry one forward. Name a reference in the prompt by its position in the list: @Image 1, @Video 2, @Audio 1.

FieldTypeRequiredDescription
reference_image_urlsarraynoUp to 30 urls. Keep a character, product or place consistent. Refer to them in the prompt as @Image 1, @Image 2, in the order given.
reference_video_urlsarraynoUp to 10 urls. Copy motion, camera or style from a clip, and the source for edit and extend. @Video 1 in the prompt.
reference_audio_urlsarraynoUp to 10 urls. Follow a voice or a music track. @Audio 1 in the prompt.
taskstringnoauto (default), reference, edit or extend. edit changes an existing clip: at least one reference video, and words like add, remove, replace or change in the prompt. extend continues a clip forward or backward: a reference video, and words like extend or continue.
return_last_framebooleannotrue adds last_frame_url to the finished job, a PNG of the final frame stored by Corent. Pass it as image_url on the next call and the next shot starts exactly where this one ended.
output_formatstringnomp4 (default) or mov, which keeps more colour precision for grading.

Four rules. First and last frame (image_url and end_image_url) and the reference_* fields cannot be mixed in one call. On first/last-frame, edit and extend tasks the aspect ratio follows the input, so omit aspect_ratio or pass adaptive. An edit keeps the length of the source clip, so omit duration_s. These fields work today on corent-seedance-2.5, so pin it with model; any other model answers with a clear 400 and bills nothing.

GET /v1/models reports what each video model can do: supports_reference_images, max_reference_images, supports_reference_videos, supports_reference_audio, supports_video_edit, supports_video_extend, supports_last_frame and supports_first_last_frame. Build the picker from those flags, not from this page.

Real faces: the supplier refuses photos or videos of real people as references. It accepts characters made inside Corent, meaning any image you generated through the API in the last 30 days. Make the character first, then reference it.

Reference to video, with the last frame kept
curl -X POST https://api.corent.tech/v1/videos/generate \  -H "Authorization: Bearer $CORENT_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "prompt": "@Image 1 steps out of the cafe into light rain, handheld, dusk",    "model": "corent-seedance-2.5",    "reference_image_urls": ["https://..."],    "duration_s": 8,    "return_last_frame": true  }' // the finished job carries the final frame as a PNG stored by Corent:// "last_frame_url": "https://..."
The next shot, from last_frame_url
curl -X POST https://api.corent.tech/v1/videos/generate \  -H "Authorization: Bearer $CORENT_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "prompt": "she opens an umbrella and crosses the street",    "model": "corent-seedance-2.5",    "image_url": "https://.../last_frame.png",    "duration_s": 8,    "return_last_frame": true  }' // image_url is the last_frame_url of the previous job, so shot two// starts on the exact frame shot one ended on. No aspect_ratio: it// follows the frame.
Extend a clip
curl -X POST https://api.corent.tech/v1/videos/generate \  -H "Authorization: Bearer $CORENT_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "prompt": "continue @Video 1 forward: she reaches the far kerb and looks back",    "model": "corent-seedance-2.5",    "task": "extend",    "reference_video_urls": ["https://..."],    "duration_s": 5  }' // the new footage continues the clip; the prompt says forward or backward
Edit a clip
curl -X POST https://api.corent.tech/v1/videos/generate \  -H "Authorization: Bearer $CORENT_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "prompt": "in @Video 1 replace the red umbrella with a yellow one, keep everything else",    "model": "corent-seedance-2.5",    "task": "edit",    "reference_video_urls": ["https://..."]  }' // no duration_s: an edit keeps the length of the source clip

The connector (0.9.0) takes the same fields on generate_video and adds edit_video and extend_video. The SDKs (0.5.0) take them on videos.generate and add videos.edit(...) and videos.extend(...).

05

Films

One sentence in, one finished film with sound out.

Describe a film in one sentence and Corent makes the whole thing. It writes the script, creates the characters once, and shoots every scene with those same characters speaking their lines. It checks each scene, reshoots a bad one once, and joins everything into one video with sound.

EndpointMethodWhat it does
/v1/films/planPOSTWrites the film and prices it. Free. The plan is kept for 24 hours.
/v1/filmsPOSTStarts making a planned film, or plans and makes one from a brief. Returns 202 with a film_id.
/v1/films/{film_id}GETThe stage, progress, each scene, and when done the finished video_url and the charge.
/v1/films/{film_id}/streamGETThe same, pushed as server-sent events on every change, instead of polling.
/v1/filmsGETYour films, newest first, one page at a time. Takes ?customer_id= to narrow it.
/v1/films/{film_id}/cancelPOSTStops a film that has not finished. A cancelled film is not billed.

POST/v1/films/plan

FieldTypeRequiredDescription
briefstringyesThe film in a sentence, up to 2000 characters.
duration_snumbernoLength of the finished film, 10 to 120 seconds. Default 30.
aspect_ratiostringno16:9 (default), 9:16 or 1:1.
resolutionstringno720p (default) or 1080p. 1080p costs more.
languagestringnoThe language the characters speak, for example en.
customer_idstringno1 to 128 characters. Your own id for the end customer.
curl, plan
curl -X POST https://api.corent.tech/v1/films/plan \  -H "Authorization: Bearer $CORENT_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "brief": "a 50 second parody where 5 founders start a startup, know nothing, and the AI part is easy because they used Corent",    "duration_s": 50,    "aspect_ratio": "16:9"  }'
Response
{  "plan_id": "...",  "title": "...",  "logline": "...",  "style": "...",  "characters": [{ "id": "c1", "name": "...", "description": "..." }],  "scenes": [{    "index": 0,    "duration_s": 6,    "setting": "...",    "action": "...",    "camera": "...",    "characters": ["c1"],    "dialogue": [{ "character_id": "c1", "line": "..." }]  }],  "total_duration_s": 50,  "estimated_cost_cents": 1400,  "estimated_cost_range": { "min": 1000, "max": 2000 },  // the plan is kept for 24 hours  "expires_at": "..."}

POST/v1/films

FieldTypeRequiredDescription
max_cost_centsnumberyesThe most this film may cost. If the estimate is above it the request is refused with a 400 and nothing is spent.
plan_idstringnoThe plan from POST /v1/films/plan: the story and price you showed. Send this or a brief.
briefstringnoInstead of plan_id: the film in a sentence, with the same options as the plan call, planned and made in one go.
webhook_urlstringnoReceives the finished film, signed like every other webhook when webhook_secret is set.
webhook_secretstringnoSigns the delivery with X-Corent-Signature.
customer_idstringno1 to 128 characters. Rides on the film and its receipt.
Idempotency-KeyheadernoA repeat returns the original film instead of starting a second one.
curl, make the film
curl -X POST https://api.corent.tech/v1/films \  -H "Authorization: Bearer $CORENT_API_KEY" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: film-order-42" \  -d '{    "plan_id": "...",    "max_cost_cents": 1600  }' // 202: { "film_id": "...", "status": "queued", "estimated_cost_cents": 1400 }

A 402 means the balance is too low for the estimate. A 50 second 720p film with five characters costs roughly $10 to $20 and takes about 15 to 30 minutes, so follow it on the stream, poll GET /v1/films/{film_id} about once a minute, or pass webhook_url.

The stages

queued
Waiting to start.
writing
Writing the script: the story, the scenes and every line of dialogue.
casting
Creating the characters, once, so each one looks the same in every scene.
shooting
Shooting the scenes with those characters speaking their lines.
checking
Checking each scene, and reshooting a bad one once.
editing
Joining the scenes into one video with sound.
completed
Done. video_url is the finished film and cost_cents is the charge.
failed
It could not be finished. Not billed. error_message says why.
cancelled
Stopped on request. Not billed.

progress_percent and stage_detail say how far along it is, and each scene carries its own status, its clip and how many times it was reshot. quality_notes lists anything the checks found.

Finished film
{  "film_id": "...",  "status": "completed",  "progress_percent": 100,  "title": "...",  "video_url": "https://...",  "duration_s": 50,  "cost_cents": 1312,  "billed": true,  "quality_notes": ["..."],  "scenes": [{ "index": 0, "status": "completed", "video_url": "https://...", "reshoots": 0 }]}

The billing promise. The price is shown before anything is paid: planning is free. The charge never goes above max_cost_cents. A film that fails or is cancelled is not billed, and reads billed: false.

POST /v1/intent now answers task_type film, with next_step POST /v1/films/plan, for anything one clip cannot deliver: longer than one take, or several characters telling a story. /v1/intent/execute will not make a single clip for it. The connector (0.10.0) has plan_film, make_film, get_film and cancel_film. The SDKs (0.6.0) have films.plan, films.create, films.get, films.wait, films.cancel and films.list.

06

Speech

POST/v1/audio/speech

Synchronous. Returns the finished audio file and the exact charge.

FieldTypeRequiredDescription
textstringyesThe text to speak. Billed per 1,000 characters, started thousands count in full.
voice_idstringnoFrom GET /v1/voices. Omit it for the default narration voice.
modelstringnoPins an exact speech model, for example corent-eleven-multilingual-v2.
stabilitynumberno0 to 1. Low is more expressive and varies more take to take; high is steadier.
similaritynumberno0 to 1. How closely to hold the original voice's character.
stylenumberno0 to 1. Extra expressiveness.
speednumbernoPlayback speed. 1.0 is natural pace.
languagestringnoISO code for the multilingual models, for example en or pt-BR.
output_formatstringnomp3, wav or pcm.
customer_idstringno1 to 128 characters. Your own id for the end customer. Rides on the receipt.
Response
{  "id": "...",  "status": "completed",  "audio_url": "https://...",  "meta": { "model": "corent-speech-tts", "cost_cents": 14, "duration_ms": 1840 }}

corent-speech-tts is a tier name, not a model. Sending it back as model returns 404. Pinnable names come from GET /v1/models.

Request body
{  "text": "Welcome to Corent. One API for image, video, and voice.",  // optional  "voice_id": "...",  // optional; pins a speech model  "model": "corent-eleven-multilingual-v2"}

GET/v1/voices

Every voice this endpoint accepts as voice_id, with a display name, a preview clip and neutral descriptors you can filter on. Call it before naming a voice: voice_id cannot be guessed, and until this existed the only reliably reachable voice was the default.

07

Chat completions

POST/v1/chat/completions

OpenAI-compatible. Point any OpenAI SDK at the base URL and keep your code.

FieldTypeRequiredDescription
modelstringyesA tier, corent/text-premium, or an exact model from GET /v1/models.
messagesarrayyesThe conversation, in the OpenAI shape.
max_tokensnumbernoUpper bound on the reply.
streambooleannotrue streams the reply as server-sent events.
customer_idstringno1 to 128 characters. Your own id for the end customer. Rides on the receipt, same as every other generating call.
Request body
{  // a tier, or an exact model such as "corent-gpt-5.6-sol"  "model": "corent/text-premium",  "messages": [{ "role": "user", "content": "Write a haiku about shipping fast." }],  "max_tokens": 200,  "stream": false}
Response
{  "id": "chatcmpl-...",  "object": "chat.completion",  "model": "corent/text-premium",  "choices": [{ "message": { "content": "..." }, "finish_reason": "stop" }],  "usage": { "prompt_tokens": 14, "completion_tokens": 19, "total_tokens": 33 },  "corent": { "cost_cents": 1 }}

Streaming, tool calling and JSON mode pass through. Billed per token, with the charge on the corent.cost_cents extension.

08

Uploads

POST/v1/uploads

Send a file, get a url. Every other endpoint accepts that url wherever it asks for an image.

Two ways to send it: multipart with a single field named file, or JSON with the bytes in base64. Up to 25 MB either way. The url comes back ready to pass as image_url, mask_url, source_image_url, end_image_url or a reference, so a file on your side never has to be public first.

FieldTypeRequiredDescription
filemultipartnoThe file itself, as a multipart form field. Use this form or the JSON form, not both.
data_base64stringnoThe file, base64-encoded, for the JSON form.
content_typestringnoThe MIME type, for example image/png. Required with data_base64.
filenamestringnoOptional. Kept for your own reference.
curl, multipart
curl -X POST https://api.corent.tech/v1/uploads \  -H "Authorization: Bearer $CORENT_API_KEY" \  -F "file=@product.png"
Request body, JSON
{  // the file, base64, no data: prefix  "data_base64": "iVBORw0KGgo...",  "content_type": "image/png",  // optional, kept for your own reference  "filename": "product.png"}
Response
{  "id": "...",  "url": "https://...",  "content_type": "image/png",  "bytes": 184322}
FieldTypeDescription
idstringThe upload id.
urlstringThe address every other endpoint accepts as image_url, mask_url or a reference.
content_typestringThe type as stored.
bytesnumberThe size as stored.
Then, as a reference
{  "prompt": "the same bottle on a wet slate counter",  "tier": "premium",  // the url from POST /v1/uploads  "reference_image_urls": ["https://..."]}

An upload is not a generation. It costs nothing and appears on no receipt.

09

Tiers and pinning

Name a tier and the router picks the model. Name a model and it runs exactly that.

Tiers

Five tiers: air, lite, premium, pro, max_pro. Send tier on image and video, and call a language tier as model: corent/text-premium.

Video adds resolution: premium and pro reach 1080p, max_pro reaches 4K. Receipts and status spell tiers out in full, as image-air or video-max_pro.

Preferences

One word instead of a tier. It resolves like this.

PreferenceOptimizes forImage tierVideo tier
cheapCost firstAirAir
fastLatency firstAirAir
balancedCost, speed and quality equallyLitePremium
qualityOutput quality firstPremiumPro

Pinning a model

Pass model instead of tier for direct access: no routing, no substitution. If it cannot deliver, the request fails and you pay nothing.

curl
curl -X POST https://api.corent.tech/v1/images/generate \  -H "Authorization: Bearer $CORENT_API_KEY" \  -H "Content-Type: application/json" \  -d '{"prompt": "a fox reading a book", "model": "corent-flux-schnell"}' // the response names the model you pinned:// "meta": { "model": "corent-flux-schnell", "cost_cents": 2, ... }

Names read corent-flux-schnell. The bare name flux-schnell still resolves, so older code keeps working.

Models
GET /v1/models, public, no authentication.
Tiers
GET /v1/tiers, with per-resolution prices and live status.
Status
GET /v1/status, one line per tier.

10

Response and receipt

Every success says what served it and what it cost. The meta block is the receipt.

FieldTypeDescription
idstringThe request or job id.
statusstringcompleted, processing or failed.
meta.modelstringThe tier that served the request, or the exact model you pinned.
meta.cost_centsnumberThe exact charge for this request, in cents.
meta.duration_msnumberHow long the generation took.

Chat completions carries the same number as corent.cost_cents. Video bills on completion, so its charge appears on GET /v1/jobs/{id}.

GET/v1/requests

Every past request with its charge, the tier that served it, how many candidates were considered and whether failover fired. Failed requests show billed: false.

Response
{  "requests": [{    "request_id": "...",    "status": "completed",    "model": "image-air",    "cost_cents": 2,    "billed": true,    "candidates_considered": 3,    "failover_occurred": false,    "reasoning": "Optimized for cost and speed.",    "output_url": "https://..."  }],  "next_before": null}

GET /v1/routing/explain?request_id=... answers the same question for one request.

11

Per-customer usage

Tag a request with your own customer id and the charge follows that id, so you can bill your users from our receipts.

The tag

customer_id is an optional string, 1 to 128 characters, accepted on every generating call: images, edits, upscales, background removal, video, speech and chat completions. Corent never reads it. It is stored with the job and printed back on the receipt exactly as sent.

Request body
{  "prompt": "a fox reading a book",  "tier": "air",  // your end customer, your own id for them  "customer_id": "acct_8f2c"}

The filter

GET/v1/account/usage?customer_id=

The same usage report, narrowed to one customer: their recent jobs and their total spend, in cents. Without the filter the report covers the whole account.

curl
curl "https://api.corent.tech/v1/account/usage?customer_id=acct_8f2c" \  -H "Authorization: Bearer $CORENT_API_KEY"

The receipt

Every receipt from GET /v1/requests carries customer_id when the request did, so a charge can always be traced to the customer it was made for.

Receipt
{  "request_id": "...",  "status": "completed",  "model": "image-air",  "cost_cents": 2,  "billed": true,  "customer_id": "acct_8f2c"}

12

Errors and retries

Nothing in this table is billed. Charging happens only after a successful result.

StatusMeaningRetryWhat to do
400 / 422Invalid requestNoFix the highlighted field and resend.
401Invalid API keyNoCheck the header: Authorization: Bearer co_live_...
402Insufficient balanceNoAdd funds in the dashboard.
404Resource not foundNoCheck the job or batch id.
429Rate limitedYesWait the seconds given in the Retry-After header.
500Internal errorOnceRetry once. If it persists, contact support.
502 / 503Temporary upstream failureYesSafe to retry: routing already failed over internally.

A provider failure after internal failover is still a failure you are not charged for. Your balance is untouched.

Send an Idempotency-Key on image or video generation and a repeat returns the original job. A timed-out request can be retried blindly and can never double-bill.

13

Limits and balance

Every key carries its own ceiling, and every balance is prepaid.

Limits

Requests per minute, concurrent jobs and an optional daily cost cap, per key. A new key gets 60 requests a minute and 50 concurrent jobs, with no daily cap: your prepaid balance is the ceiling, and a per-key daily cap is yours to switch on. Across all keys, an account is limited to 3000 requests a minute.

PUT/v1/account/api-keys/{id}/limits

Deliberately requires a dashboard login, not an API key: a leaked key must never raise its own cap.

Request body
{ "requests_per_minute": 60, "concurrent_jobs": 50, "daily_cost_cap_cents": 5000 }

requests_per_minute 1 to 6000, concurrent_jobs 1 to 5000, daily_cost_cap_cents an integer or null for no cap.

On 429, Retry-After gives the wait in seconds. Batches take up to 50 items and run five at a time, each billed at the normal rate.

Balance

GET/v1/account/balance

Response
{ "balance_cents": 998, "held_cents": 40, "available_cents": 958 }

held_cents is reserved against jobs still running. A 402 checks available_cents, so a request can be refused while balance_cents still looks healthy.

GET /v1/account/usage returns recent jobs and total spend, and takes ?customer_id= to narrow it to one of your customers. POST /v1/account/deposit opens a card top-up from $5, and /deposit/crypto from $10.