Concepts / Images, audio & files

Images, audio & files

Send images, audio, PDFs and video in a chat request on any API — checked against the model's input modalities before anything is routed or charged.

Content parts

A user message's content can be a list of parts: text plus images, audio, PDF files and video, in the shape each API already uses. Every part is checked against the model's input modalities — the modalities list on GET /v1/models/{id} and on the model page — before anything is routed or charged.

Input/v1/chat/completions/v1/responses/v1/messages
Imageimage_url: {url, detail?}input_image: {image_url: "<url>", detail?}image (source base64 or url)
Audioinput_audio: {data, format}input_audio: {input_audio: {data, format}}not available (no audio input)
PDF / filefile: {file: {filename?, file_data}}input_file: {file_data | file_url, filename?}document (source base64, url, text or content)
Videovideo_url: {video_url: {url}}input_video: {video_url: "<url>"}not available

On /v1/responses, input_audio and input_video are gateway extensions in the chat shape: the gateway serves every API over the same models, so audio and video work there too where the model accepts them.

Examples

An image and a recording in one turn, with the OpenAI Python SDK:

import base64
audio = base64.b64encode(open("question.wav", "rb").read()).decode()

r = client.chat.completions.create(
    model="google/gemini-2.5-flash",
    messages=[{"role": "user", "content": [
        {"type": "text", "text": "Answer the question in the recording about this chart."},
        {"type": "image_url", "image_url": {"url": "https://example.com/chart.png"}},
        {"type": "input_audio", "input_audio": {"data": audio, "format": "wav"}},
    ]}],
    max_tokens=400,
)
print(r.usage.prompt_tokens_details)   # cached_tokens, audio_tokens

A PDF with cURL (the file is sent inline as a data URL):

curl https://api.yelno.ai/v1/chat/completions \
  -H "Authorization: Bearer $YELNO_API_KEY" -H "Content-Type: application/json" \
  -d '{"model": "anthropic/claude-sonnet-4", "max_tokens": 600,
       "messages": [{"role": "user", "content": [
         {"type": "text", "text": "Summarize the termination clause."},
         {"type": "file", "file": {"filename": "contract.pdf",
           "file_data": "data:application/pdf;base64,JVBERi0xLjcK…"}}]}]}'

The same document through /v1/messages with the Anthropic SDK:

client.messages.create(model="anthropic/claude-sonnet-4", max_tokens=600, messages=[{
    "role": "user", "content": [
        {"type": "document", "title": "contract.pdf",
         "source": {"type": "base64", "media_type": "application/pdf", "data": pdf_b64}},
        {"type": "text", "text": "Summarize the termination clause."},
    ]}])

Video, where the model takes it, is a video_url part with an https URL or a data:video/mp4;base64,… URL.

Limits

InputMaxAccepted
Image20 MBhttps URL or data:image/…;base64 — png, jpeg, webp, gif
Audio25 MBbase64 only; format wav, mp3, aiff, aac, ogg, flac, m4a, pcm16 or pcm24
PDF / file50 MBhttps URL or data:application/pdf;base64
Video50 MBhttps URL or data:video/mp4|mpeg|mov|webm;base64
All inline media70 MBper request, across every part of every turn sent

A conversation is sent whole on every turn, so the request limit counts the media of every earlier turn too. Linked (URL) media is fetched by the model's provider and is not measured before dispatch; some models take PDFs inline only, and a linked file to one of them is refused with input_type_unsupported. File references by file_id are not supported: send the bytes inline or a URL.

Refusals

HTTPcodeWhen
400input_type_unsupportedThe model's input modalities do not include this type (the message lists what it accepts), or no host serving the model can carry it. param points at the part, e.g. messages[0].content[1].
413input_too_largeA part (or the request's inline media) is over its limit, measured on the decoded bytes.
400audio_format_unsupportedinput_audio.format is not one of the listed formats — convert webm recordings to wav.
400file_reference_unsupportedA file_id (or an Anthropic file source) names a vendor's file store this gateway cannot reach — send the content inline or as a URL.
400content_part_unsupportedAn unknown part type that carries a payload, or media inside a tool result.
400invalid_base64The payload is not valid base64.
{"error": {"type": "invalid_request", "code": "input_type_unsupported", "param": "messages[0].content[1]",
  "message": "'mistralai/mistral-large-4-0' does not accept audio input; this model accepts text and images", "retriable": false}}

Every refusal happens before a provider is contacted and before any funds are held. A request with fallback models (models / fallback_models) skips the fallbacks that cannot take its inputs.

How inputs are charged

Images, PDF pages and video are counted by the provider as input tokens and charged at the model's input price. Audio is reported apart (usage.prompt_tokens_details.audio_tokens, and input_audio in yelno.usage.units): it is charged at the model's audio input price where one is published, otherwise as ordinary input tokens — never free. prompt_tokens always includes the audio tokens.

The hold is sized for your attachments Before dispatch the gateway holds funds for the request's estimated cost, with media counted by type (about 1,600 tokens per image, 32 tokens per second of audio, 3,000 tokens per PDF page, 300 tokens per second of video). The charge is always the provider's reported usage, so a generous hold is released, not billed.