Skip to content

Image input

Three of our models read images. Send one as a base64 data URI inside the usual content array and it works with the OpenAI SDK unchanged. Two details decide whether your first attempt succeeds — and one of them looks exactly like the model being blind.

Which models read images

We probed every model in the catalog with the same two images rather than trusting the capability listed upstream — and the two disagreed. Only these three answered from what was actually in the picture:

ModelImage input
qwen3.8-27bYes — verified
kimi-k2.6Yes — verified
kimi-k2.7-codeYes — verified
glm-5.2 · glm-4.7-flashNo
deepseek-v4-flash · deepseek-v4-proNo

The list is also machine-readable: capabilities.vision on GET /v1/models. Sending an image to any other model is refused with vision_not_available before the request leaves us, and costs you nothing.

Why we refuse instead of forwarding

Two of the models that cannot read images accept the payload anyway, ignore the picture, and answer confidently from the text alone. You would be billed in full for a guess, with nothing in the response saying the image was dropped. A refusal you can act on is worth more than a fluent paragraph about an image nobody looked at.

Sending an image

Use the standard content array: one text part and one image_url part. The URL must be a base64 data URI — a remote http(s) link is not fetched, and we reject it up front with image_url_not_supported rather than letting it fail deeper in the stack.

python
import base64
from openai import OpenAI

client = OpenAI(base_url="https://api.clfaigateway.dev/v1", api_key="sk-gw-...")

with open("screenshot.png", "rb") as f:
    b64 = base64.b64encode(f.read()).decode()

resp = client.chat.completions.create(
    model="qwen3.8-27b",
    messages=[{
        "role": "user",
        "content": [
            {"type": "text", "text": "What does this screen show?"},
            {"type": "image_url",
             "image_url": {"url": f"data:image/png;base64,{b64}"}},
        ],
    }],
    max_completion_tokens=400,
)
print(resp.choices[0].message.content)

An empty answer is almost always the token budget

These models think before they answer, and the thinking is spent from max_completion_tokens. Set it too low and the entire budget goes into reasoning, leaving content empty — which is indistinguishable from a model that cannot see. This cost us an hour before we recognised it. Keep the budget at 400 tokens or more when sending images.

Limits and billing

  • Billing: images are charged as input tokens at the model’s normal input rate. There is no separate image fee.
  • Token cost: roughly 64 tokens per 256×256 tile of the image — an image up to 256×256 costs about 66 tokens, a 512×512 one about 258. Those tokens count against the context window like any other input.
  • Request size: up to 10 MB per request body, base64 included. Larger bodies are rejected with request_too_large.
  • Multiple images: allowed — add more image_url parts to the same content array.
  • Privacy: images are request content, and we log metadata only — never prompt or response content. See the data policy.

What we have not measured

Our probe establishes that a model genuinely reads the image it is given. It does not establish OCR quality: small print, scanned documents and dense charts are untested, so we do not sell them as features. Test those on your own material first — a failed request that produced nothing costs $0, and count_tokens is free.