Multimodal
Image inputs
Send photos, screenshots and diagrams to vision-capable models with the image_url content part, by HTTPS URL or inline base64.
Add an image_url content part to a user message, pointing at an HTTPS URL or holding the image as a base64 data: URL. The examples use openai/gpt-6-luna; replace it with a model whose endpoints accept image input (see Route to vision-capable endpoints).
Send an image by URL
curl -X POST "https://api.numes.ai/api/v1/chat/completions" \ -H "Authorization: Bearer $NUMES_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "openai/gpt-6-luna", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Transcribe the total and the date from this receipt." }, { "type": "image_url", "image_url": { "url": "https://example.com/images/receipt.jpg" } } ] } ] }'The URL must be publicly reachable over HTTPS when the request runs. Signed URLs work while they're valid. Images on localhost or a private network can't be fetched; send them inline instead.
Send a local image as base64
Send a data: URL with the media type image/png, image/jpeg, image/gif or image/webp, matching the actual file, and standard (not URL-safe) base64:
import base64import osfrom pathlib import Pathfrom openai import OpenAIMEDIA_TYPES = {".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".webp": "image/webp"}def image_data_url(path: str) -> str: file = Path(path) encoded = base64.b64encode(file.read_bytes()).decode("ascii") return f"data:{MEDIA_TYPES[file.suffix.lower()]};base64,{encoded}"client = OpenAI(base_url="https://api.numes.ai/api/v1", api_key=os.environ["NUMES_API_KEY"])completion = client.chat.completions.create( model="openai/gpt-6-luna", messages=[ { "role": "user", "content": [ {"type": "text", "text": "Describe this chart in two sentences."}, {"type": "image_url", "image_url": {"url": image_data_url("chart.png")}}, ], } ],)print(completion.choices[0].message.content)Inline images count toward the 10 MiB chat body limit, and base64 adds about a third to their size. Keep inline images under about 7.5 MiB in total, or send HTTPS URLs.
The image_url part
Content part
type"image_url"RequiredIdentifies an image part.
image_urlobjectRequiredThe image to attach.
Show child attributesHide child attributes
urlstringRequiredAn
https://URL or adata:image/...;base64,URL.detail"auto" | "low" | "high"Accepted for OpenAI compatibility. It isn't forwarded to providers, so it doesn't change resolution or cost.
Add one part per image; images reach the model in the order you list them. The model doesn't see which sentence an image sat next to, so refer to images by position ("the first screenshot"). Images in earlier turns that you resend are sent, and billed, again.
Route to vision-capable endpoints
Routing considers only endpoints whose input_modalities include image. Check a model's endpoints before you send images:
curl "https://api.numes.ai/api/v1/models/author/vision-model/endpoints" \ -H "Authorization: Bearer $NUMES_API_KEY"You can combine image input with the usual routing preferences.
Limits
| Limit | Value |
|---|---|
| Formats | PNG, JPEG, GIF, WebP |
| One image | 8 MiB decoded |
| All media in one request | 24 MiB decoded |
| Chat request body | 10 MiB, including inline data: URLs |
See Sending media for the rules every media URL follows.
Billing
Endpoints price images either per image, reported as input_images in usage, or as prompt tokens. Text tokens and any per-request price apply as usual. pricing.image in GET /models shows the lowest image rate of the model's representative endpoint. See Usage accounting.
Troubleshooting
Image problems return 400 invalid_request, and the message names the failing part, such as messages[0].content[1].image_url.url.
| Symptom | Cause and fix |
|---|---|
400 with a data: URL | The media type isn't one of the four supported types (use image/jpeg, not image/jpg), doesn't match the bytes, or the data isn't standard base64. |
| 400 with an HTTPS URL | The server returned an error status, a compressed response or a mismatched Content-Type (such as text/html for an error page), or the URL isn't publicly reachable. |
400 for an http:// URL | Use HTTPS or inline the image. |
| 400 for a large image | The image exceeds 8 MiB, or all media together exceed 24 MiB. |
| 400 for SVG, HEIC, BMP or TIFF | Convert the image to PNG, JPEG, GIF or WebP. |
| 400 with an image in a system message | Move media into a user message. |
408 timeout | The image host didn't respond in time. |
413 payload_too_large | Inline images pushed the body over 10 MiB. Use HTTPS URLs. |
503 provider_unavailable | No endpoint for the model accepts image input under your routing preferences. |