# Send an Image to the Claude API (base64, URL, Files)

> The image content block in Python and curl, the format and size limits from the docs, and a screenshot-to-CSV extraction that doesn't invent numbers.

- Canonical: https://guides-ai.pages.dev/guides/claude-api-vision-images/
- Plate 15.13 · Topic: Building with the API (https://guides-ai.pages.dev/topics/api/)
- Published: 06 Sept 2026 · 4 min read
- Source site: guides-ai — https://guides-ai.pages.dev/

Images ride in the same `messages` array as text — an `image` content block with one of three source types: `base64`, `url`, or a `file_id` from the Files API.

## 1. Python: a local file as base64

```python
import base64, anthropic

client = anthropic.Anthropic()
with open("invoice.png", "rb") as f:
    data = base64.standard_b64encode(f.read()).decode()

msg = client.messages.create(
    model="claude-opus-5",
    max_tokens=2048,
    messages=[{"role": "user", "content": [
        {"type": "image", "source": {
            "type": "base64", "media_type": "image/png", "data": data}},
        {"type": "text", "text": "Describe this image."},
    ]}],
)
print(next(b.text for b in msg.content if b.type == "text"))
```

`media_type` has to match the actual bytes — `image/jpeg`, `image/png`, `image/gif`, or `image/webp`. Note the ordering: the docs recommend putting the image **before** the text.

## 2. curl: an image by URL

```bash
curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-opus-5",
    "max_tokens": 1024,
    "messages": [{"role": "user", "content": [
      {"type": "image", "source": {"type": "url",
        "url": "https://example.com/chart.png"}},
      {"type": "text", "text": "What is the trend in this chart?"}
    ]}]
  }'
```

Anthropic's servers fetch the URL, so nothing gets base64-encoded into your request. For an image you'll reuse across turns, upload it once via the Files API and send `{"type": "file", "file_id": "..."}` instead — base64 re-sends the whole image on every turn of a conversation.

## 3. Limits worth knowing

- **Formats:** JPEG, PNG, GIF, WebP. Animations aren't supported — only the first frame is read.
- **Per image:** 10 MB base64 on the Claude API (5 MB on Bedrock and Google Cloud), max 8000×8000 px.
- **Per request:** 32 MB total. Up to 600 images per API request, or 100 for models with a 200k-token context window.
- **Above 20 image blocks in one request**, a stricter per-image dimension limit kicks in for *every* image in it. Keep each under 2000 px on both sides, or keep the request to 20 blocks.
- **Token cost** is `⌈width / 28⌉ × ⌈height / 28⌉` visual tokens. Oversized images are downscaled first — to a 2576 px long edge (max 4784 visual tokens) on Claude 4.7 and later, 1568 px (max 1568 tokens) on earlier models.

## 4. Use case: table out of a screenshot

```python
{"type": "text", "text":
 "Return this table as CSV, header row first. Copy values exactly. "
 "Leave a cell empty if you cannot read it — never guess a number."}
```

The "leave it empty" clause is the important half: without it a blurry digit becomes a confident wrong digit. Re-add the extracted totals yourself and compare them to the totals in the image — that one check catches most OCR-style errors. Claude also won't identify people in images, and reads no EXIF or other metadata.

---

Next: [send a PDF to the Claude API](/guides/claude-api-pdf-documents/) · [your first Claude API request](/guides/claude-api-first-request/).
