Skip to main content
Docs · API reference

Images, audio and video

POST /images/generations, /images/edits, /audio/speech, /audio/transcriptions and /videos. The policy's pool decides the model; `auto` takes the first one of that type.

Browse docs · API reference › Images, audio and video
On this page
  1. Where these models run
  2. Refusals
  3. What the usage row records
  4. The receipt
  5. Image generation
  6. Request
  7. Example
  8. Response
  9. Image edits
  10. Request
  11. Example
  12. Response
  13. Speech
  14. Request
  15. Example
  16. Streaming
  17. Transcription
  18. Example
  19. Response
  20. Speech and transcription share one model type
  21. Video
  22. Example
  23. The job

None of these endpoints routes. Nothing Inferbase measures ranks image models against each other, or speech models, or video models, so there is no evidence to choose on. The routing policy decides instead: name a model, or send auto and take the first model of that type the key can reach.

What auto reaches depends on the policy's pool. A policy that names a pool makes that pool the whole list, in the order you arranged, and a request naming a model outside it is refused with 403. A policy on the default catalog pool restricts nothing, and auto takes the first model of that type in catalog order.

Where these models run

These endpoints serve on an Inferbase route or on your own credential: a provider key or a cloud account you have connected. Connect a key under Connections, add the model to your routing policy's pool, and it serves.

An Inferbase route serves a model only when it carries a rate in the unit its provider bills in, read from that provider's own price list. The catalog's inference_available flag says which models run this way. Naming a model whose Inferbase route carries no such rate returns 503 with the fix in the message.

The reason is pricing rather than capability. Image, audio and video providers bill per image, per megapixel, per character, per minute or per video, never per token, and a per-token rate computes nothing against a request that spends no tokens. Serving one without a rate in the right unit would mean charging you nothing for generation that costs us money. On your own credential the provider bills you directly, so there is nothing for us to compute and nothing to get wrong.

Refusals

Five refusals are specific to choosing the model. All five arrive before the provider is called, so nothing is billed.

StatusWhen
403The request named a model the policy's pool does not hold. The message names the model and the endpoint.
403The request said auto and the pool holds no model of that type. The message names the pool.
400The model is served, but on another endpoint of the same kind: an editing model named on /images/generations, or a transcription model on /audio/speech. The message names the endpoint to use.
404The model is not in the catalog at all. GET /models lists what the key can name.
503The model exists but runs only on an Inferbase route that carries no rate for this endpoint's unit, or the credential it would run on does not offer this surface. The message names the fix.

Rate limits, the key's spend cap and the project's budget apply here as they do on chat, so 429 and 402 are also possible.

What the usage row records

Chat, embeddings and rerank are metered in tokens. These are not, so the usage row records its own units and leaves the token counts at zero. Logs shows those units in place of a token count.

A row records every unit the request makes knowable, not only one. Providers disagree about which unit they charge in, and one that charges per image today may charge per megapixel tomorrow, so the row is written to stay readable either way.

EndpointUnits recorded
/images/generations, /images/editsImages returned, or images requested when the call failed, and the megapixels they came to
/audio/speechCharacters spoken
/audio/transcriptionsSeconds and minutes of audio, when the provider states a duration
/videosThe video, and the seconds requested when the request stated a length

A transcription only states its duration when you ask for verbose_json. Without one the row records nothing for that request rather than estimating seconds from the file size. The duration is recorded exactly as the provider stated it, unrounded.

A row for a request served on your own credential costs zero, because your provider billed you. A row for a request served on an Inferbase route carries that route's rate against the units recorded, and a failed generation costs zero either way.

The receipt

Every response from these endpoints carries the usage row it wrote, as usage, so you read the cost and the units without a second request to Logs:

FieldMeaning
usage_log_idThe row's id, the same id Logs shows
modelThe id of the model that served, the same id the catalog and /v1/models use
served_position, chain_lengthWhere the serving route sat in the fallback chain (1 is the first tried) and how long the chain was
attemptsEvery route tried, with its outcome and HTTP status
meteredThe units recorded, keyed by dimension (image_out_count, image_out_megapixel, text_in_character, audio_in_second, audio_in_minute, video_out_count, video_out_second)
cost_microsThe provider cost in microdollars; zero on your own credential
byokWhether your own credential served it
latency_msWall time of the request; for a video, creation to completion

Speech puts the same object in the X-Inferbase-Usage header as JSON, because its body is the audio. A video job carries usage only once it is completed or failed, and every later poll returns the same receipt; served_position, chain_length and attempts are absent on it, since a job is created once and rendered by the provider that accepted it.

A chat completion answered by one of these surfaces, through a stated routing.intent or an inferred one, carries the same object as generation_usage next to its zero-token usage, and a streamed one emits it as the usage event after the media event.

Image generation

Method and pathPOST /images/generations
PurposeGenerate images, OpenAI shape.
AuthenticationAPI key or session
LimitsUp to 10 images per call, and a prompt of up to 32,000 characters.

Request

FieldMeaning
modelAn image model the policy's pool holds, or auto for the first one in it. Required. policy:<slug> runs the request under another routing policy the key may call.
scopeWhich models auto may pick from: inferbase (Inferbase routes), keys (your provider keys and cloud accounts), or all. May be narrower than the policy's scope, never wider (403). Ignored when you name a model or the policy sets a pool.
promptWhat to generate. Required.
nHow many images. Default 1.
response_formaturl or b64_json. Any other value is a 422. On Inferbase's own routes images always come back as b64_json, and asking for url there is a 400; a model on your own provider key or cloud account answers in the format you ask for.
size, quality, style, userForwarded when set.
background, output_format, seedProvider-specific parameters, forwarded untouched.

These are the whole list. A field outside it is a 422, including a parameter the provider itself accepts, because a silently dropped field would make the response a different request from the one you sent.

Example

bash
curl https://api.inferbase.ai/api/v1/inference/images/generations \
  -H "Authorization: Bearer inf_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "auto",
    "prompt": "a slate rooftop in the rain, seen from above",
    "n": 1
  }'

Response

The OpenAI shape: data carries one entry per image with a url or a b64_json, plus revised_prompt when the provider rewrote your prompt before generating. An image made on Inferbase's own routes is always inline as b64_json, as OpenAI's gpt-image models answer: a provider's download link is not handed on. The response also names the model that served, adds served_by, and carries the receipt as usage.

When a request names no size, an Inferbase route priced per megapixel sends 1024x1024 and bills that size, so the hold, the charge and the receipt describe the same picture.

Image edits

Method and pathPOST /images/edits
PurposeChange an image you send, as the prompt says, OpenAI shape.
AuthenticationAPI key or session
LimitsUp to 10 input images and 25 MB of images per call, PNG, JPEG or WebP.

Only a model that reads an image serves this endpoint, and a model that cannot draw from text alone, such as an editing model, does not serve /images/generations. So auto on either endpoint takes the first model in the pool that fits it.

Request

Multipart form data, as the OpenAI SDKs send it.

FieldMeaning
imageThe image to edit. Send several as image[]. Required.
promptWhat to change. Required.
maskOptional. An image whose transparent areas mark where to edit, for models that read one.
modelAn image model the policy's pool holds that reads an image, or auto. Default auto. policy:<slug> runs the request under another routing policy the key may call.
nHow many images. Default 1.
sizeForwarded when set.
response_formaturl or b64_json. As on generations: inline on Inferbase's own routes, where url is a 400.
userForwarded when set.
provider_optionsOptional. A JSON object of the model's own parameters, passed through unchanged. Values are strings, numbers or booleans, and it cannot set the fields above. An AWS Bedrock Stability tool reads its tool fields here, for example {"search_prompt": "the blue car"} for search and replace.

A file that is not a PNG, JPEG or WebP image is refused with 400 before the provider is called, so nothing is billed.

Example

bash
curl https://api.inferbase.ai/api/v1/inference/images/edits \
  -H "Authorization: Bearer inf_your_api_key" \
  -F model=auto \
  -F prompt="turn this into a watercolor" \
  -F image=@photo.png

Response

The same shape as image generation, and billed the same way.

Speech

Method and pathPOST /audio/speech
PurposeTurn text into audio, OpenAI shape.
AuthenticationAPI key or session
LimitsUp to 8,000 characters per call.

The response body is the audio itself, so the disclosure other endpoints put in a served_by object rides in headers here: X-Inferbase-Model, and X-Inferbase-Connection when one of your own connections served the request, with the receipt as JSON in X-Inferbase-Usage. A request served by an Inferbase managed route sends no X-Inferbase-Connection header. The media type is whatever the provider returned.

This endpoint does not support idempotency replay, because a replayed body would have to be stored as audio.

Request

FieldMeaning
modelA speech model the policy's pool holds, or auto. Required. policy:<slug> runs the request under another routing policy the key may call.
scopeWhich models auto may pick from: inferbase (Inferbase routes), keys (your provider keys and cloud accounts), or all. May be narrower than the policy's scope, never wider (403). Ignored when you name a model or the policy sets a pool.
inputThe text to speak. Required.
voiceThe provider's voice name, or on a voice-design model a description of the voice to generate. Up to 1,000 characters.
response_formatmp3, opus, aac, flac, wav or pcm, as the provider supports.
speed0.25 to 4.0.
instructionsStyle direction, where the provider supports it. Up to 4,000 characters.
stream_formataudio or sse. Omit it for one body, which is what every client got before.

Example

bash
curl https://api.inferbase.ai/api/v1/inference/audio/speech \
  -H "Authorization: Bearer inf_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"model": "auto", "input": "The rooftop is wet.", "voice": "alloy"}' \
  --output speech.mp3

Streaming

Set stream_format and the audio arrives as it is made, instead of after the whole sentence. For a voice agent that is the difference between answering and waiting.

Whether it actually arrives early depends on the provider and the format you ask for, so measure before you rely on it. On DeepInfra's Qwen3-TTS, response_format: "pcm" streams and every other format is synthesised in full first. Measured from our own infrastructure on 97 characters, five runs: first audio at about 1.2 seconds with pcm, against about 4.7 seconds without it, which is the whole synthesis. Providers sometimes advertise a much lower figure for the model itself; that is not what an HTTP request returns.

audio streams the provider's own container in chunks. Play it as it arrives, or write it to a file. The headers still name the model, and the connection when your own credential served it, but there is no X-Inferbase-Usage: headers are sent before the first byte and the usage row is written at the end. Read the receipt from the usage API, or use sse.

sse wraps the same bytes as events and has room for the receipt:

text
data: {"type": "speech.audio.delta", "audio": "<base64>"}
data: {"type": "speech.audio.delta", "audio": "<base64>"}
data: {"type": "speech.audio.done", "usage": { ... }}

Two things hold whichever format you choose. A stream can only fail over to another provider before its first byte, so once audio is flowing you are on the model that started; a provider that refuses outright still falls through to the next in the pool. And billing does not depend on how much you listened to: speech is charged per character sent, all of which are in the request, so hanging up mid-sentence records the same usage as hearing it out.

bash
curl https://api.inferbase.ai/api/v1/inference/audio/speech \
  -H "Authorization: Bearer inf_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"model": "auto", "input": "The rooftop is wet.", "voice": "alloy", "stream_format": "audio", "response_format": "pcm"}' \
  --output speech.pcm

Transcription

Method and pathPOST /audio/transcriptions
PurposeTurn audio into text, OpenAI shape.
AuthenticationAPI key or session
Limits25 MB per file.

Multipart, as every OpenAI SDK sends it. Send file and the fields below as form fields.

FieldMeaning
fileThe audio file. Required. Its extension is how most providers detect the container, so keep it. An empty file is a 400 and one over the size limit a 413.
modelA transcription model the policy's pool holds, or auto. Defaults to auto. policy:<slug> runs the request under another routing policy the key may call.
language, prompt, response_format, temperatureForwarded when set.

Example

bash
curl https://api.inferbase.ai/api/v1/inference/audio/transcriptions \
  -H "Authorization: Bearer inf_your_api_key" \
  -F file=@meeting.m4a \
  -F model=auto \
  -F response_format=verbose_json

Response

text is the transcript. Ask for verbose_json and the provider's extra fields ride through untouched, including duration and segments. Ask for anything else and you get the plain shape, even where Inferbase asked the provider for more in order to meter the request. The response shape follows what you asked for, never what we asked the provider for, so it does not change with the credential that served it.

The response also names the model that served and carries the receipt as usage.

Speech and transcription share one model type

Both directions are filed under the catalog model_type of audio, so a pool can hold a text-to-speech model and a transcriber at the same time.

Each endpoint drops the models whose stated output contradicts it, so a transcriber the catalog knows produces text is never what auto picks on /audio/speech. A model whose output the catalog does not state stays eligible for both, because excluding it would hide a model that does work on the strength of missing data. Name the model rather than sending auto when a pool holds both directions and one of them is uncatalogued.

Video

Video generation is asynchronous at every provider, so this is three endpoints rather than one: start a job, poll it, download the result.

Method and pathPOST /videos
PurposeStart a video generation. Returns a job, not a video.
AuthenticationAPI key or session
FieldMeaning
modelA video model the policy's pool holds, or auto. Required. policy:<slug> runs the request under another routing policy the key may call.
scopeWhich models auto may pick from: inferbase (Inferbase routes), keys (your provider keys and cloud accounts), or all. May be narrower than the policy's scope, never wider (403). Ignored when you name a model or the policy sets a pool.
promptWhat to generate. Required, up to 32,000 characters.
secondsRequested length in seconds, 1 to 600. The billing unit, and the row records none without it.
sizeFor example 1280x720, as the provider supports.

Example

bash
# Start the job.
curl https://api.inferbase.ai/api/v1/inference/videos \
  -H "Authorization: Bearer inf_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"model": "auto", "prompt": "rain on a slate rooftop", "seconds": 8}'

# Poll it, using the id the create call returned.
curl https://api.inferbase.ai/api/v1/inference/videos/video_9f2c... \
  -H "Authorization: Bearer inf_your_api_key"

# Download it once status is completed.
curl https://api.inferbase.ai/api/v1/inference/videos/video_9f2c.../content \
  -H "Authorization: Bearer inf_your_api_key" \
  --output clip.mp4

The job

status is one of queued, in_progress, completed or failed. Each provider's own status words are mapped onto those four. A word no provider has been mapped from reads as in_progress, so an unknown state means keep polling rather than a failure. progress is a percentage where the provider reports one, and error carries the provider's message on a failure.

The id is Inferbase's, not the provider's. Store it, because it is what both reads take.

The job stays tied to the credential that started it. Every poll and every download resolves that credential again, so disconnecting the key or cloud account leaves the job unreadable and both reads return 503. A job belonging to another project reads as 404. There is no endpoint that lists jobs, so keep the ids you started.

A poll that cannot reach the provider returns 502 and leaves the job alone. It does not mark it failed, because the generation is very likely still running.

The usage row for a video is written by the poll that first sees the job finished or failed, not by the call that started it. A job nobody polls to a terminal state records no usage. Once the row exists the job carries it as usage, on that poll and every poll after it.

Downloading before the job finishes returns 409 naming the state, not 404. The video is fetched from the provider on demand through the credential that made it; Inferbase never stores it.

Last updated October 1, 2026.