# Image input

Send an image to a chat model as a content part. The model answers in text. The part has the shape of the API that you use, so an unchanged client works.

```bash
curl https://router.inference.ai.exchange/v1/chat/completions \
  -H "Authorization: Bearer sk-liquid-..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-oss-120b",
    "messages": [{
      "role": "user",
      "content": [
        { "type": "text", "text": "what is in this picture?" },
        {
          "type": "image_url",
          "image_url": { "url": "data:image/png;base64,iVBOR…", "detail": "high" }
        }
      ]
    }]
  }'
```

Put the text part first, then the images.

## Examples

**Read a chart.** Send a screenshot of a chart and ask for its numbers.

```json
{
  "role": "user",
  "content": [
    { "type": "text", "text": "List the value of each bar as a table." },
    {
      "type": "image_url",
      "image_url": { "url": "data:image/png;base64,iVBOR…", "detail": "high" }
    }
  ]
}
```

Use `detail: "high"` for small text. It costs more tiles.

**Compare two photos.** Send more than one image in one message. Each image is a part, and the cap counts all of them.

```json
{
  "role": "user",
  "content": [
    { "type": "text", "text": "What changed between these two photos of the shelf?" },
    { "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,/9j/4AAQ…" } },
    { "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,/9j/4BBR…" } }
  ]
}
```

Check `max_references` in `input_modalities`. An offer can limit the number of images.

## Send an image

| API              | Part                                | Source                        |
| ---------------- | ----------------------------------- | ----------------------------- |
| Chat Completions | `image_url` with `url` and `detail` | `https://` URL or `data:` URL |
| Responses        | `input_image` with `image_url`      | `https://` URL or `data:` URL |
| Messages         | `image` block with a `source`       | `base64` or `url`             |

Accepted formats are `image/png`, `image/jpeg`, `image/webp` and `image/gif`. An offer can accept fewer.

`detail` is `auto`, `low` or `high`. The exchange sends it to the provider without change.

## URL or base64

The exchange does not fetch an image that you give as a URL. It prices the image at 1,200 tokens and never sees its media type. So an offer that limited the formats or the sources to `base64` cannot serve it.

If you send the bytes inline, the exchange sets the cap from the real image, and more offers can serve the request. Use a URL only when the image is large and the cap is not a concern.

## Cost

The exchange reads the width and height from the header. Each offer converts them to input tokens. Without an offer table, the exchange uses 85 tokens plus 170 for each 512-pixel tile.

Tiles follow the published rule of OpenAI. Fit the long side to 2048 pixels, then the short side to 768, then count 512-pixel tiles. `detail: "low"` is one tile for all sizes.

The charge is the token count that the provider meters. See [What an attachment costs](https://inference.ai.exchange/docs/modalities.md#what-an-attachment-costs).

## Limits

An offer can limit the largest image (`max_bytes`), the formats, the sources and the number of images in one request (`max_references`). A request that breaks a limit does not reach that offer. The reason is `media_unsupported`.

## Troubleshooting

| Error                       | Cause                                                    | Fix                                              |
| --------------------------- | -------------------------------------------------------- | ------------------------------------------------ |
| `400 unsupported_parameter` | No offer for the model reads images.                     | Pick a model with `image` in `input_modalities`. |
| `400 unsupported_content`   | The bytes are not base64, or the header is not an image. | Check the encoding and the format.               |
| `413 request_too_large`     | The body is over 20 MB.                                  | Send a smaller image.                            |
| `503 no_eligible_offer`     | Offers read images, but none takes this image.           | Read `eligibility.excluded`.                     |

## Next steps

- [Modalities](https://inference.ai.exchange/docs/modalities.md): the map of all modalities.
- [Document input](https://inference.ai.exchange/docs/document-input.md): send a PDF.
- [Image generation](https://inference.ai.exchange/docs/image-generation.md): make an image.
