> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sideren.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Images and documents.

> Send an image in either dialect and a vision model reads it. Which models can is a smaller list than the catalog suggests — check the capability flag before you send, because a text-only model is a 400, not a shrug.

## Check the capability first

Vision is a per-model capability, and today it is a **short list**. Read it from the
catalog rather than from a model's name or reputation:

```sh theme={null}
curl -s https://api.sideren.io/v1/models \
  | jq '.data[] | select(.capabilities.vision) | .id'
```

| Capability | What it means | Where to read it |
| - | - | - |
| `capabilities.vision` | Accepts image input. | [`GET /v1/models`](/api/models-endpoint) · **Vision** tag on [Models](/models) |
| `capabilities.documents` | Accepts binary documents — PDF, DOCX, CSV — as a document block rather than as text. | Same |

<Warning>
  The **Vision** tag on the [Models](/models) page covers preview ids too — models whose
  name is accepted but whose placement is not routed yet. `GET /v1/models` lists only the
  routed models, so it is the list to trust when you need image input to actually work
  today. Document input is narrower still: one routed model reads binary documents.
</Warning>

Sending an image to a text-only model is a `400`, not a silent drop. That is deliberate —
a request whose image was ignored produces a confidently wrong answer, which is worse than
an error.

## Sending an image

<CodeGroup>
  ```json Anthropic (/v1/messages) theme={null}
  {
    "model": "gpt-5.6-terra",
    "max_tokens": 1024,
    "messages": [{
      "role": "user",
      "content": [
        { "type": "text", "text": "What does this chart show?" },
        {
          "type": "image",
          "source": {
            "type": "base64",
            "media_type": "image/png",
            "data": "iVBORw0KGgo..."
          }
        }
      ]
    }]
  }
  ```

  ```json OpenAI Chat (/v1/chat/completions) theme={null}
  {
    "model": "gpt-5.6-terra",
    "messages": [{
      "role": "user",
      "content": [
        { "type": "text", "text": "What does this chart show?" },
        {
          "type": "image_url",
          "image_url": { "url": "data:image/png;base64,iVBORw0KGgo..." }
        }
      ]
    }]
  }
  ```

  ```python Python theme={null}
  import base64
  from openai import OpenAI

  client = OpenAI(base_url="https://api.sideren.io/v1", api_key="sdn_your_key")

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

  r = client.chat.completions.create(
      model="gpt-5.6-terra",
      messages=[{
          "role": "user",
          "content": [
              {"type": "text", "text": "What does this chart show?"},
              {"type": "image_url",
               "image_url": {"url": f"data:image/png;base64,{b64}"}},
          ],
      }],
  )
  print(r.choices[0].message.content)
  ```
</CodeGroup>

Both dialects accept data URLs. The Chat Completions dialect also accepts a plain `https://`
URL in `image_url.url` — but a base64 data URL is the more reliable choice, because it does
not depend on the image being publicly reachable.

## Documents and PDFs

The Messages dialect carries binary documents as their own content block:

```json theme={null}
{
  "type": "document",
  "source": { "type": "base64", "media_type": "application/pdf", "data": "JVBERi0..." }
}
```

Where the serving model does not declare `capabilities.documents`, a document block is not
an error — it becomes a deterministic text placeholder, so the turn still completes and the
model is told a document was present rather than being handed silence. If the document's
*content* is what matters, either pin a document-capable model or extract the text yourself
and send it as text.

## Cost and context

* **Images are input tokens.** A large screenshot can be worth thousands of them, drawn
  from your credits at the model's input rate ([Plans & limits](/plans)).
  Down-scale before sending — most chart and UI questions are answered as well at 1024px
  wide as at 4096.
* **Images sit in the conversation.** In an agent loop, every subsequent turn re-sends
  them. On a model that publishes a cached input rate, a stable image prefix is cheaper on
  repeat; where no cached rate is published, you pay full input every turn
  ([Prompt caching](/guides/prompt-caching)).
* **Budget `max_tokens` for the answer, not the image.** Input size does not consume the
  output ceiling — but on a reasoning model, thinking does ([Reasoning](/guides/reasoning)).

## Failure modes

| What you see | Why |
| - | - |
| `400` on a request with an image | The model does not accept image input. Check `capabilities.vision`. |
| `422` | The request declared a requirement no placement of that model guarantees. Switch models or drop the requirement — see [Errors](/errors). |
| A confident answer that ignores the image | You are on a model that turned a document block into a placeholder. Check `capabilities.documents`. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.