Vision básica: una imagen en el request
Objetivo
Al terminar esta lección sabrás cómo enviar una imagen a Claude usando base64, qué formatos son soportados, cómo se estructura el bloque type: "image" en el array de content, y cuántos tokens consume una imagen según su tamaño.
Concepto
El bloque image
En el Módulo 1 aprendiste que content puede ser un string o un array de bloques. Hasta ahora usaste bloques type: "text". Para enviar una imagen, usás un bloque type: "image":
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": "iVBORw0KGgoAAAANSUhEUgAAAAIA..."
}
}Cómo se cuentan los tokens de una imagen
Las imágenes se convierten internamente a tokens. El costo depende del tamaño en píxeles:
| Tamaño aproximado | Tokens estimados |
|---|---|
| Ícono (32×32) | ~30 |
| Thumbnail (200×200) | ~200 |
| Screenshot (1280×720) | ~1,500 |
| Full HD (1920×1080) | ~2,500 |
| Imagen grande (4000×3000) | ~5,000+ |
Las imágenes muy grandes se redimensionan automáticamente antes del procesamiento. No hace falta que las redimensiones vos — el modelo lo hace internamente. Pero si sabés que tu imagen es un ícono de 64px, no le mandes un PNG de 4000px de ancho.
El patrón: imagen + pregunta
El caso de uso más común es enviar una imagen seguida de una pregunta en texto:
"content": [
{ "type": "image", "source": { ... } },
{ "type": "text", "text": "¿Qué ves en esta imagen?" }
]El orden importa: la imagen va primero, la pregunta después. Claude procesa los bloques en orden y necesita "ver" la imagen antes de responder sobre ella.
Cálculo rápido de tokens por imagen
La aproximación oficial que conviene tener memorizada:
tokens ≈ (width_px × height_px) / 750Con eso podés estimar antes de pegar una imagen gigante:
| Resolución | Cálculo | Tokens estimados |
|---|---|---|
| 512×512 | 262k / 750 | ~350 |
| 1024×768 | 786k / 750 | ~1050 |
| 1920×1080 | 2.07M / 750 | ~2760 |
| 4000×3000 | 12M / 750 | ~16000 |
Si la imagen supera los límites del modelo, Anthropic la redimensiona antes de cobrarla — pero el redimensionado es genérico, probablemente no el óptimo para tu caso. Si vas a mandar muchas imágenes, redimensioná vos con un criterio claro (ej: lado largo ≤ 1568 px para preservar detalle sin gastar de más).
Multi-imagen en un solo mensaje
Podés mandar varias imágenes en el mismo content:
"content": [
{ "type": "image", "source": { ... } },
{ "type": "image", "source": { ... } },
{ "type": "text", "text": "Compará las dos imágenes y listá 3 diferencias concretas." }
]No hay un máximo teórico "pequeño" (tenés el context window), pero en la práctica más de 20 imágenes en un request degrada la calidad: el modelo empieza a perder detalle por imagen. Si necesitás comparar decenas de imágenes, partí en requests por lotes.
Conceptos de arquitecto
- Vision no es OCR: Claude "lee" texto en imágenes bien, pero no está optimizado para documentos escaneados con layouts complejos o handwriting. Para eso hay servicios dedicados (Google Vision, AWS Textract) más baratos. Usá Claude cuando además del texto necesitás comprensión semántica.
- Los tokens de imagen no se cachean con
cache_controligual que el texto: parte del procesamiento ocurre antes del cache. Si tu caso es "un misma imagen, muchas preguntas", Files API (lección 03) escala mejor que base64 con caching. - El modelo responde sobre lo que ve, no sobre lo que "debería ver": si le mandás una imagen borrosa o con ruido, te va a describir la imagen borrosa. Validá calidad visual antes de diagnosticar el prompt.
Ejecución real
Paso 1 — Crear una imagen de prueba y codificarla en base64
# Crear un PNG mínimo de 2x2 píxeles con colores (rojo, verde, azul, blanco)
python3 -c "
import base64, struct, zlib
def create_png():
w, h = 2, 2
raw = b'\x00\xff\x00\x00\x00\xff\x00' # row 0: red, green
raw += b'\x00\x00\x00\xff\xff\xff\xff' # row 1: blue, white
def chunk(t, d):
c = t + d
return struct.pack('>I', len(d)) + c + struct.pack('>I', zlib.crc32(c) & 0xffffffff)
ihdr = struct.pack('>IIBBBBB', w, h, 8, 2, 0, 0, 0)
return b'\x89PNG\r\n\x1a\n' + chunk(b'IHDR', ihdr) + chunk(b'IDAT', zlib.compress(raw)) + chunk(b'IEND', b'')
png = create_png()
print(base64.b64encode(png).decode())
"Resultado:
iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAEklEQVR4nGP4z8DAAMIM/4EAAB/uBfsL2WiLAAAAAElFTkSuQmCCPaso 2 — Enviar la imagen a Claude con curl
IMG_B64="iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAEklEQVR4nGP4z8DAAMIM/4EAAB/uBfsL2WiLAAAAAElFTkSuQmCC"
curl -s 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-haiku-4-5\",
\"max_tokens\": 150,
\"messages\": [{
\"role\": \"user\",
\"content\": [
{
\"type\": \"image\",
\"source\": {
\"type\": \"base64\",
\"media_type\": \"image/png\",
\"data\": \"$IMG_B64\"
}
},
{
\"type\": \"text\",
\"text\": \"Describí esta imagen en una oración. Sé específico con los colores.\"
}
]
}]
}"Respuesta:
{
"model": "claude-haiku-4-5-20251001",
"content": [
{
"type": "text",
"text": "La imagen muestra un pequeño punto o círculo de color rojo intenso sobre un fondo blanco."
}
],
"stop_reason": "end_turn",
"usage": {
"input_tokens": 32,
"output_tokens": 35
}
}Observá: 32 input tokens para un PNG de 2×2 píxeles. El overhead mínimo de una imagen es bajo, pero escala con el tamaño.
Paso 3 — Lo mismo en TypeScript
import Anthropic from "@anthropic-ai/sdk";
import { readFileSync } from "node:fs";
const client = new Anthropic();
// Leer imagen y convertir a base64
const imageBuffer = readFileSync("/tmp/test-colors.png");
const imageBase64 = imageBuffer.toString("base64");
const resp = await client.messages.create({
model: "claude-haiku-4-5",
max_tokens: 150,
messages: [{
role: "user",
content: [
{
type: "image",
source: {
type: "base64",
media_type: "image/png",
data: imageBase64,
},
},
{
type: "text",
text: "Describí esta imagen en una oración. Sé específico con los colores.",
},
],
}],
});
const text = resp.content[0].type === "text" ? resp.content[0].text : "";
console.log("Respuesta:", text);
console.log(`Tokens: in=${resp.usage.input_tokens} out=${resp.usage.output_tokens}`);Probalo con tu API key
Tu propia API key queda en el localStorage de tu navegador. Los requests los paga tu workspace y podés ajustar el prompt libremente.
Pregunta libre a Sonnet
Multimodal real necesita subir imágenes (complejo desde browser). Este playground te deja probar el modelo con cualquier texto; usá la Messages API directa para ver structure.
💡 Esta respuesta viene del modelo 'conversando' sobre multimodal — la Lección 04 y 05 muestran cómo mandarle imágenes reales.
Anti-patterns
- ❌ Enviar imágenes gigantes sin necesidad. Si vas a preguntar "¿hay texto en esta imagen?", un thumbnail de 800px basta — no mandes el original de 8000px. Más píxeles = más tokens = más costo y latencia.
- ❌ Incluir el prefijo
data:image/png;base64,en el campodata. El campodatasolo lleva la cadena base64 pura. Elmedia_typeva en su propio campo. - ❌ Poner la pregunta antes de la imagen. Claude procesa en orden — la imagen debe ir primero para que el modelo la "vea" al llegar a la pregunta.
- ❌ Usar vision para OCR masivo. Claude es bueno extrayendo texto de imágenes, pero para OCR a escala (miles de documentos) usá herramientas especializadas y reservá Claude para comprensión semántica.
- ❌ Mandar 10 imágenes cuando 2 alcanzan. Cada imagen suma tokens y diluye la atención del modelo. Si vas a pedirle algo sobre "el diagrama", mandá solo ese diagrama, no todo el deck.
- ❌ Usar
media_typeque no coincide con el contenido. Declararimage/pngpero mandar bytes JPEG hace que el backend rechace el request o procese basura. Detectá el tipo real antes de enviar. - ❌ Pegar el prefix
data:image/png;base64,en el campodata. El SDK (y la API) esperan solo el payload base64 puro. Con el prefix, el decode falla y recibís un 400.
Recap
- El bloque
type: "image"consource.type: "base64"permite enviar imágenes embebidas en el JSON. - Formatos soportados: JPEG, PNG, GIF, WebP.
- Los tokens de imagen escalan con el tamaño en píxeles — optimizá el tamaño antes de enviar.
- Patrón fundamental: imagen primero, pregunta en texto después.
- Una imagen de 2×2px consume ~32 tokens; un screenshot HD ~1,500-2,500 tokens.
Fuente oficial: platform.claude.com/docs/en/build-with-claude/visionEjercicio: