Vision: image and PDF input
Send images by URL or data URI and PDFs as a file part in chat/completions: picking a model, the 20 MB body limit, and how image usage is charged.
Updated: September 7, 2026
Multimodal models accept images and files inside the same messages array. Instead of a plain string, content becomes an array of parts: text, image_url, and file. This is OpenAI's own format and it reaches the provider unchanged.
Which models support it
- Images: the
visionbadge on the models page; inGET /v1/modelsthat means"image"appears ininput_modalities. More than 250 models in the catalogue can do this. - Files (PDF): the
filesbadge, equivalent to"file"ininput_modalities. The main OpenAI, Anthropic, and Google models support it.
Send an image to a model that does not support one and the provider usually answers 400, at which point the reservation is released. Sometimes the model instead ignores the image silently and answers the text alone. For production work, choose the model from its badge, not from its name.
Image by URL
from openai import OpenAI
client = OpenAI(base_url="https://api.uttapen.ir/v1", api_key="sk-up-...")
resp = client.chat.completions.create(
model="openai/gpt-5-mini",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "What line items are on this invoice? Answer as a list."},
{"type": "image_url", "image_url": {"url": "https://example.com/invoice.jpg", "detail": "high"}},
],
}],
max_tokens=500,
)
print(resp.choices[0].message.content)
The URL has to be downloadable from the public internet, because the provider fetches it itself. Addresses on your company network or behind a login will not work — send those as a data URI instead. Not every provider honours detail (low, high, auto), but on OpenAI it does affect cost.
Image as a data URI (base64)
import base64, pathlib
data = base64.b64encode(pathlib.Path("invoice.jpg").read_bytes()).decode()
resp = client.chat.completions.create(
model="google/gemini-2.5-flash",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "Extract the total amount and the date from this invoice."},
{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{data}"}},
],
}],
)
The usual formats are image/jpeg, image/png, image/webp, and image/gif. Downscale to 1000-1500 px wide before sending: that is plenty for reading invoices and forms, and it costs fewer tokens.
PDF files
pdf = base64.b64encode(pathlib.Path("contract.pdf").read_bytes()).decode()
resp = client.chat.completions.create(
model="anthropic/claude-sonnet-4.5",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "Summarise the termination clauses of this contract."},
{"type": "file", "file": {"filename": "contract.pdf", "file_data": f"data:application/pdf;base64,{pdf}"}},
],
}],
max_tokens=1500,
)
Always pass filename — some providers infer the file type from its extension. Scanned (image-only) PDFs work with models that do their own OCR, but cost more than a text PDF. For long documents, pull the text out with a PDF library first and send it as text: cheaper, and far easier to control.
Limits
- The request body may not exceed 20 MB. Beyond that you get
413 request_too_largeand the request never reaches the provider. base64 inflates data by roughly 33%, so the raw file should stay under about 15 MB. - Providers enforce their own limits too (images per message, dimensions, PDF page count). Their errors pass through with the original status.
- The model's reply to an image is ordinary text
content. For image generation, look for models with theimage outbadge; those currently work throughchat/completionswith themodalitiesfield, not through/v1/images.
Cost
Image cost takes one of three shapes depending on the provider, and in every case you are charged for the usage the model itself reports for that request:
- Per token — the image is converted into a number of input tokens (OpenAI, Google) and billed at that model's
promptprice. - Per image — some models carry a separate
imageprice. The model page shows it as "toman / image" and it is exposed asuttapen_pricing.image_toman. - Per file — a PDF is normally converted to tokens page by page.
For the hold placed before the request goes out, each image is estimated at 1000 input tokens (or the model's image price where it has one), and each inline part is estimated from its size, at roughly one token per 4 bytes. The hold is temporary and settlement uses the real amount — but it does mean you need a meaningful balance to send a 10 MB PDF, or you will get a 402. Details in pricing.
Image and file content, like text, is never stored in the gateway; it goes to the provider for that one request and nowhere else. For confidential PDFs, also check the data-retention policy of the destination provider (privacy).