Building software that understands images β screenshots, UI mockups, error dialogs, PDF pages, diagrams β used to mean wiring up a separate vision model, an OCR service, or a custom pipeline. The Claude API's vision capability changes this completely. You pass images directly to the same Messages API you're already using for text, and Claude can describe, analyze, extract data from, and reason about whatever it sees.
In this tutorial I'll walk through exactly how to call the vision API from Python and C#, what the token math looks like, and three practical patterns I'd use in production β a screenshot-to-bug-report pipeline, a form extraction helper, and a CI/CD visual diff workflow. All code is ready to run; swap in your own API key and go.
How the Claude Vision API Works
The Claude API's vision feature is built into the existing messages endpoint. Rather than a separate API call, you compose a message whose content is an array of content blocks β a mix of text and image blocks, in whatever order makes sense for your prompt. Claude reads the whole message in context, treating the images as first-class inputs alongside the text.
Per Anthropic's documentation, two source types are supported for image blocks. The first is a direct URL: Claude fetches the image at request time. The second is base64-encoded data: you encode the file yourself and embed it in the request body. URL sources are simpler when the image is already hosted somewhere; base64 is what you need for local files, generated screenshots, or anything that shouldn't be publicly accessible.
The supported image formats are JPEG, PNG, GIF, and WebP, per Anthropic's docs. The maximum image size is 20 MB per image, and you can include up to 20 images in a single request. For image token counting, Anthropic uses a tiled calculation: a 1,092 Γ 1,092 pixel image costs approximately 1,334 tokens per their published guidelines β higher resolution images cost proportionally more. This matters a lot when you're building a high-throughput pipeline; I'll cover model selection and cost tradeoffs below.
All Claude 3 and later models support vision, including claude-haiku-4-5, claude-sonnet-4-6, and claude-opus-4-5, per Anthropic's model overview page. Haiku is the right call for high-volume, simple classification tasks; Sonnet hits the sweet spot for complex extraction; Opus is worth the cost only when reasoning quality is paramount.
Setting Up Your Environment
Install the official Anthropic SDK for Python:
pip install anthropic
For C#, add the Anthropic.SDK NuGet package to your project:
dotnet add package Anthropic.SDK
Store your API key as an environment variable rather than hardcoding it. The Python SDK reads ANTHROPIC_API_KEY automatically; in C# you'll pass it explicitly to the client constructor. Keep the key out of source control β use a .env file or your CI/CD secrets manager.
# Linux / macOS
export ANTHROPIC_API_KEY="sk-ant-..."
# Windows (PowerShell)
$env:ANTHROPIC_API_KEY = "sk-ant-..."
Analyzing Images from URLs in Python
The simplest vision call takes an image URL and a text prompt. Here's a minimal working example:
import anthropic
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY from env
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "url",
"url": "https://example.com/screenshots/app-error.png",
},
},
{
"type": "text",
"text": "Describe the error shown in this screenshot. What is the likely cause and what should a developer check first?"
}
],
}
],
)
print(message.content[0].text)
Notice the content block order: image first, then text. Claude handles any ordering, but leading with the image gives the model visual context before it reads the question β a small prompt engineering habit worth building in. The response structure is identical to a text-only call: message.content[0].text gives you the reply.
To compare two images β before and after a UI change, or two versions of a form β add a second image block to the same content array:
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=2048,
messages=[
{
"role": "user",
"content": [
{"type": "image", "source": {"type": "url", "url": before_url}},
{"type": "image", "source": {"type": "url", "url": after_url}},
{
"type": "text",
"text": "These are before/after screenshots of a UI change. List every visible difference."
}
],
}
],
)
This two-image pattern is exactly what I'd reach for in a CI/CD visual regression step β before the merge, after the merge, and a structured diff automatically attached to the pull request.
Uploading and Analyzing Local Files in Python
When the image isn't publicly hosted, encode it as base64 and set the source type accordingly. Here's a reusable helper that handles the encoding and media-type detection:
import anthropic
import base64
from pathlib import Path
def analyze_local_image(image_path: str, prompt: str, model: str = "claude-sonnet-4-6") -> str:
client = anthropic.Anthropic()
image_data = Path(image_path).read_bytes()
encoded = base64.standard_b64encode(image_data).decode("utf-8")
suffix = Path(image_path).suffix.lower()
media_type_map = {
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".png": "image/png",
".gif": "image/gif",
".webp": "image/webp",
}
media_type = media_type_map.get(suffix, "image/png")
message = client.messages.create(
model=model,
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": media_type,
"data": encoded,
},
},
{"type": "text", "text": prompt},
],
}
],
)
return message.content[0].text
# Usage
result = analyze_local_image(
"/tmp/form-screenshot.png",
"Extract all form field labels and their current values as a JSON object."
)
print(result)
One thing to watch: for large images β anything over 2β3 MB β resize or compress before encoding. A 10 MB PNG from a Retina display doesn't give Claude meaningfully more information than a 1 MB JPEG of the same content. It just costs more tokens and slows down the round trip. A quick Pillow resize step before encoding is worth adding to any production pipeline that processes user-uploaded images or high-DPI screenshots.
Building a Screenshot-to-Bug-Report Pipeline
Here's a pattern worth having in your toolbox for a CI/CD test suite that captures screenshots on failure and needs a first-pass description attached to the bug ticket. The goal: take a Playwright or Selenium failure screenshot, ask Claude what's wrong, and pipe the structured response into your issue tracker automatically.
import anthropic
import base64
import json
from pathlib import Path
def generate_bug_report(screenshot_path: str, test_name: str, expected_behavior: str) -> dict:
client = anthropic.Anthropic()
image_data = base64.standard_b64encode(
Path(screenshot_path).read_bytes()
).decode("utf-8")
prompt = (
"You are a QA engineer reviewing a failing test screenshot.\n\n"
f"Test name: {test_name}\n"
f"Expected behavior: {expected_behavior}\n\n"
"Analyze the screenshot and respond ONLY with valid JSON:\n"
'{\n'
' "summary": "One-sentence description of what went wrong",\n'
' "visible_error": "Exact error text visible on screen, or null",\n'
' "ui_state": "What state the UI appears to be in",\n'
' "likely_cause": "Most probable root cause",\n'
' "suggested_checks": ["check1", "check2", "check3"]\n'
'}'
)
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": image_data,
},
},
{"type": "text", "text": prompt},
],
}
],
)
return json.loads(message.content[0].text)
report = generate_bug_report(
screenshot_path="test-output/login-flow-failure.png",
test_name="user_can_login_with_valid_credentials",
expected_behavior="User is redirected to /dashboard after successful login",
)
print(json.dumps(report, indent=2))
Combining this with Claude API Structured Outputs β using the response_format parameter β gives you guaranteed-valid JSON without needing to parse freeform text, which is the production-ready evolution of this pattern. For a deeper dive into how structured outputs work end-to-end, that tutorial covers the full workflow with schema validation examples.
Using Vision in C# / .NET
The Anthropic.SDK NuGet package wraps the same Messages API and handles authentication, retries, and response deserialization. Here's the equivalent URL-based call in C#:
using Anthropic.SDK;
using Anthropic.SDK.Messaging;
var apiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY")
?? throw new InvalidOperationException("ANTHROPIC_API_KEY not set");
var client = new AnthropicClient(apiKey);
var messages = new List<Message>
{
new Message
{
Role = RoleType.User,
Content = new List<ContentBase>
{
new ImageContent
{
Source = new ImageSource
{
Type = SourceType.Url,
Url = "https://example.com/screenshots/app-error.png"
}
},
new TextContent
{
Text = "What error is shown in this screenshot? Describe the likely cause."
}
}
}
};
var parameters = new MessageParameters
{
Messages = messages,
MaxTokens = 1024,
Model = "claude-sonnet-4-6"
};
var response = await client.Messages.GetClaudeMessageAsync(parameters);
Console.WriteLine(response.Content[0].ToString());
For base64 images in C#, use Type = SourceType.Base64, set MediaType to the appropriate MIME string, and assign Data to your base64 string. The pattern mirrors the Python version closely, which makes it straightforward to port logic between services. This is especially useful if you have a Python data-processing script you're lifting into a .NET microservice β the prompt structure and content block format stay identical.
If you're already using the Claude API for tool use or function calling, adding vision is additive β image blocks work alongside tool definitions in the same request. Claude will look at the image, reason about it, and call whatever tools are needed in a single turn. For example: a customer support bot that can receive a screenshot, identify the product from the image, and then call a tool to look up the warranty status in your database.
Practical Use Cases in Production
Beyond the screenshot-to-bug-report pipeline, here are four patterns I'd actually build in production, each with the prompt shape that works for it.
Form field extraction from scanned documents. Pass a scanned PDF page (rendered as an image with a tool like pdf2image) and ask Claude to return all field labels and their handwritten or typed values as JSON. This replaces template-based OCR for forms where the layout varies by vendor or version. Per Anthropic's documentation, the model handles printed and handwritten text; accuracy on dense, low-contrast scans degrades, so run a contrast-enhancement pass for archival documents.
UI component inventory. Drop in a Figma export or a production screenshot and ask Claude to list every UI component present β buttons, inputs, modals, navigation items β with their labels and states. Useful for auto-generating test coverage checklists or accessibility audit starters before a new sprint.
Dashboard screenshot to data table. Pass a screenshot of a chart or graph and ask Claude to reconstruct the underlying data as CSV. It won't match the original data precisely on complex charts, but for quick sanity checks or when you don't have API access to the original data source, it's faster than manual extraction by an order of magnitude.
Image-based log triage. Some enterprise systems still surface errors only in a GUI with no text export. Screenshot the error dialog, run it through Claude, extract the error code and stack trace, and log it as a structured event. Add this as a step in your incident response runbook and your on-call engineers spend less time manually transcribing error messages at 2 AM.
Token Costs and Model Selection
Vision requests consume tokens for both the image and the accompanying text. Per Anthropic's published guidance, images are tokenized based on resolution using a tiled approach: images are split into 512 Γ 512 tiles, each costing a fixed number of base tokens. A standard 1,080-pixel-wide screenshot at 1:1 aspect ratio costs roughly 1,300β1,700 tokens depending on height. At scale this matters β a pipeline processing 10,000 screenshots per day at Sonnet pricing is a meaningful line item.
Use claude-haiku-4-5 for binary classification (is there an error in this screenshot? yes/no), simple OCR, or any task where you're sending hundreds of images and the analysis doesn't need deep reasoning. Use claude-sonnet-4-6 for structured extraction, comparison tasks, and any case where the answer requires understanding context rather than just reading text off the screen. Reserve claude-opus-4-5 for complex reasoning tasks where accuracy matters more than cost β auditing a full user flow across a dozen screens, or analyzing a dense technical architecture diagram.
If you're building a pipeline that processes many images per day, combine vision with prompt caching: cache your system prompt, domain context, and few-shot examples. That portion of the prompt cost drops by up to 90% per Anthropic's pricing docs, while the per-image token cost stays variable. The combination can cut your total API spend in half for high-volume vision workloads.
Best Practices and Common Pitfalls
A few things that trip people up when they first wire in vision.
Don't send unnecessarily large images. A 4K screenshot of a login form doesn't help Claude read the input labels any better than a 720p version. Resize to the smallest dimensions where the relevant content is still legible. The Pillow library in Python and System.Drawing.Common in .NET both handle this in a few lines. The rule of thumb: if the text is readable at 100% zoom on a standard 1080p monitor, the image is large enough.
Be specific in your prompt about what to look at. "What do you see?" is a valid prompt, but "List every error message, warning badge, and disabled button visible in this screenshot" gives Claude a clear task and produces a more scannable, structured response. Vision quality improves with specificity just like text quality does β this is the same principle behind good prompt engineering for text tasks.
Handle errors the same way you handle text API errors. Vision requests can fail with the standard API error codes β rate limits, overloaded servers, invalid media type. Wrap every call in a try/except (Python) or try/catch (C#) and implement exponential backoff for 529 (overloaded) responses. The error handling pattern from the Claude API Streaming tutorial applies directly here.
Test your prompts on a small sample before scaling. Run ten representative images through your extraction prompt and manually verify the outputs before committing to a production pipeline. Vision prompts behave differently across image types β a prompt that works well on clean screenshots may need adjustment for scanned documents or low-contrast UI states.
If you're serious about building production AI systems on top of vision and language models, I'd recommend AI Engineering by Chip Huyen (~$55) β it covers multimodal pipelines, evaluation strategies, and the tradeoffs you'll hit when vision tasks go from prototype to production. For your development setup, a Logitech MX Keys keyboard (~$99) and a Logitech MX Master 3S mouse (~$100) are the peripherals I'd pick for anyone spending serious time in an IDE. And if you're running local experiments on a mini PC, a Crucial 32GB DDR4 SODIMM (~$59) keeps your workstation comfortable with large datasets open alongside your tooling.
Want Claude dialed in for your workflow?
Book a 1-on-1 Claude setup call: 60 minutes, live, in English or Spanish, recorded for you. $127. Book the call β
Or start with the Claude Prompt Pack: 50 bilingual prompts, $17, instant download.