Multimodal base models on Valar accept images mixed into the text of a request. Each image travels one of two ways: inline as a base64 data URI, or by reference as an HTTP(S) URL that Valar fetches on your behalf.
Before you wire up a request, confirm three things: the model accepts images, your images fall inside the size and format limits, and the URLs you reference are publicly reachable.
Confirm the model is multimodal
Image support is per model. The Models page is the source of truth — a checkmark in the Image column means the model accepts the content blocks shown below.
Sending image content to a model that is not multimodal returns 400 with model '<id>' does not support image input.
Stay inside the limits
At most 20 images in a single request.
At most 20 MB per image, measured against the raw decoded bytes rather than the length of the base64 string.
No dimension cap. The 20 MB byte ceiling is the real bound; the model may resize or tile oversized images before decoding.
URLs must be http:// or https:// and reachable on the public internet. Valar gives up after 10 s and returns 400.
Pass an image per API
The content block differs by API surface. Each tab below shows the URL form first, then the base64 form, so you can match whichever client you already use.
Responses
Chat Completions
Messages
Drop an input_image block into the message content array. Its image_url holds either a public URL or a data URI.from openai import OpenAI
client = OpenAI(base_url="https://api.valarhq.ai/v1", api_key="YOUR_KEY")
response = client.responses.create(
model="moonshotai/Kimi-K2.6",
input=[
{
"role": "user",
"content": [
{"type": "input_text", "text": "Describe this photo."},
{
"type": "input_image",
"image_url": "https://example.com/storefront.jpg",
},
],
}
],
background=True,
)
To send the bytes inline, build a data URI and pass it in the same image_url field:import base64, pathlib
raw = pathlib.Path("storefront.jpg").read_bytes()
b64 = base64.b64encode(raw).decode()
data_uri = f"data:image/jpeg;base64,{b64}"
response = client.responses.create(
model="moonshotai/Kimi-K2.6",
input=[
{
"role": "user",
"content": [
{"type": "input_text", "text": "Describe this photo."},
{"type": "input_image", "image_url": data_uri},
],
}
],
background=True,
)
The optional detail field takes "auto", "low", or "high". Use the standard OpenAI image_url content part. The nested url accepts a public URL:response = client.chat.completions.create(
model="moonshotai/Kimi-K2.6",
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": "Describe this photo."},
{
"type": "image_url",
"image_url": {
"url": "https://example.com/storefront.jpg",
"detail": "auto",
},
},
],
}
],
)
The same url field also accepts a data URI:"image_url": {"url": f"data:image/jpeg;base64,{b64}"}
Use the Anthropic image content block, whose source accepts a url type or a base64 type.import anthropic
client = anthropic.Anthropic(
base_url="https://api.valarhq.ai",
api_key="YOUR_KEY",
)
# Reference by URL
response = client.messages.create(
model="moonshotai/Kimi-K2.6",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {"type": "url", "url": "https://example.com/storefront.jpg"},
},
{"type": "text", "text": "Describe this photo."},
],
}
],
)
For inline bytes, pass the raw base64 string with no data: prefix and declare the media_type:response = client.messages.create(
model="moonshotai/Kimi-K2.6",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/jpeg",
"data": b64, # raw base64, no data: prefix
},
},
{"type": "text", "text": "Describe this photo."},
],
}
],
)
When a request is rejected
Every image problem surfaces as a 400. The message tells you which check failed:
| Status | Message | Cause |
|---|
| 400 | model '<id>' does not support image input | The model is not multimodal. |
| 400 | too many images: maximum 20 images per request | The request carried more than 20 images. |
| 400 | image too large: exceeds maximum of ... bytes | An image was larger than 20 MB once decoded. |
| 400 | unsupported image type ... | The MIME type is outside JPEG, PNG, WebP, GIF. |
| 400 | unsupported URL scheme ... | A URL used a scheme other than http or https. |
| 400 | blocked: ... | A URL was not reachable from the public internet. |
| 400 | failed to download image: ... | A URL returned non-200 or timed out after 10 s. |
| 400 | declared type ... does not match detected type ... | A data URI’s declared MIME did not match the actual bytes. |
Choosing between URL and base64
When the bytes are already on hand, a base64 data URI usually beats handing Valar a URL to fetch. Note that image bytes are never cached between requests, so each request re-sends or re-fetches its images.