Vision, PDFs, and the Files API
Claude is natively multimodal: it reads images and PDFs with the same attention it gives text, in the same request, through the same content-block system you already know. That unlocks a class of application most teams underestimate — screenshots as bug reports, charts as data sources, scanned documents as structured records — and it costs you one new block type to learn.
Images are content blocks
An image rides in the content list beside text, as base64 or a URL, and you can interleave several:
import base64
img = base64.standard_b64encode(open("dashboard.png", "rb").read()).decode()
response = client.messages.create(
model="claude-opus-5",
max_tokens=16000,
messages=[{
"role": "user",
"content": [
{"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": img}},
{"type": "text", "text": "Which service is causing the latency spike, and when did it start?"},
],
}],
)Claude reads charts, diagrams, screenshots, whiteboard photos, and UI mockups — not just "what is in this picture" but reasoning over what is in it: extracting the numbers behind a bar chart, spotting the misaligned element in a screenshot, turning a whiteboard architecture sketch into a component list. The workflows that pay off immediately for developers: paste a failing UI screenshot instead of describing it; hand an error-dialog photo from a user straight to triage; extract tables from images of documents that never had a digital form.
Three practical rules. Resolution is a cost dial — images are billed in tokens that scale with size, and current frontier models accept high-resolution input (useful for dense screenshots and small text) at several times the token cost of a downscaled copy; send the fidelity the task needs, not the fidelity the camera produced. Put the image before the question — Claude reads in order, and question-after-evidence measurably beats evidence-after-question. Text in images is read, not OCR-bolted-on — but for very long documents, the PDF path below is the right tool.
PDFs: pages and pixels together
PDFs get a dedicated block type, and it does something subtle: Claude sees both the extracted text and the rendered page images, so charts, tables, stamps, and layout survive:
pdf = base64.standard_b64encode(open("q3_report.pdf", "rb").read()).decode()
response = client.messages.create(
model="claude-opus-5",
max_tokens=16000,
messages=[{
"role": "user",
"content": [
{
"type": "document",
"source": {"type": "base64", "media_type": "application/pdf", "data": pdf},
"title": "Q3 Financial Report",
"citations": {"enabled": True},
},
{"type": "text", "text": "What drove the change in gross margin this quarter?"},
],
}],
)The citations flag is the line to notice — it returns in the next lesson as the backbone of trustworthy document Q&A. And the document block isn't only for PDFs: plain-text sources take the same shape, which keeps data cleanly separated from instructions in every document workflow.
Upload once: the Files API
Re-sending megabytes of base64 with every request is waste with a latency bill. The Files API uploads a file once and hands back an ID that any later request can reference:
uploaded = client.beta.files.upload(file=open("q3_report.pdf", "rb"))
# ...then, in any request, forever after:
{"type": "document", "source": {"type": "file", "file_id": uploaded.id}}Files persist until you delete them, and the same mechanism works in reverse: when Claude's code-execution tool generates files — a chart, a cleaned CSV, a generated spreadsheet — they come back as file IDs you download. Upload-once also composes with the caching lesson: a document referenced by ID in a stable prefix position is cached like any other prefix bytes, so "the corpus every request needs" becomes cheap twice over.
What to take into the next lesson
Images and PDFs are content blocks, not a separate product; resolution is a cost dial; evidence goes before the question; Files turns repeated payloads into references. You now know how to put any artifact in front of Claude. The next lesson is about doing that systematically — grounding a whole application in your data, and choosing the architecture that fetches the right data at the right time.