Vision Endpoint
Analyze, describe, OCR, and classify images with GPT-4o, Claude, and Gemini.
Send an image (URL, base64, or file upload) and get back rich analysis — description, OCR, classification, or an answer to your own prompt. Routes across OpenAI, Anthropic, and Google vision-capable models.
Endpoints
Basic Request
const result = await cencori.vision.analyze({
image: { url: 'https://example.com/photo.jpg' },
prompt: 'What breed of dog is this?'
});
console.log(result.analysis);From a local file (browser)
const b64 = await fileToBase64(file);
const result = await cencori.vision.analyze({
image: { base64: b64, mimeType: file.type }
});From a local file (Node.js)
import fs from 'node:fs';
const buf = fs.readFileSync('receipt.png');
const result = await cencori.vision.ocr({
image: { base64: buf.toString('base64'), mimeType: 'image/png' }
});
console.log(result.text);Request Parameters
Response
{
analysis: "The image shows a golden retriever puppy...",
model: "gpt-4o-mini",
provider: "openai",
usage: {
promptTokens: 1103,
completionTokens: 87,
totalTokens: 1190
},
cost: {
providerCostUsd: 0.000217,
cencoriChargeUsd: 0.000326,
markupPercentage: 50
}
}/describe returns description instead of analysis. /ocr returns text. /classify returns classification (parsed JSON when the model returns valid JSON) plus the raw string in raw.
Multiple images
Pass an images array instead of a single image to analyze several images together. All three providers support multi-image input.
// Compare two product photos
const result = await cencori.vision.analyze({
images: [
{ url: 'https://cdn.example.com/product-a.jpg' },
{ url: 'https://cdn.example.com/product-b.jpg' },
],
prompt: 'Compare these two products. Which looks better lit and why?',
});// Multi-page image-based document
const pages = await rasterizePdf(pdfBuffer); // your own helper
const { text } = await cencori.vision.ocr({
images: pages.map(buf => ({ base64: buf.toString('base64'), mimeType: 'image/png' })),
});Multipart uploads also support multi-image: send multiple file fields (or a single files[] field with several files):
curl -X POST https://cencori.com/api/ai/vision \
-H "CENCORI_API_KEY: csk_..." \
-F "file=@before.jpg" \
-F "file=@after.jpg" \
-F "prompt=What changed between these two photos?"Per-image validation runs before the provider call, so oversize/wrong-format errors name the specific offender.
Use cases: before/after comparison, multi-angle product tagging, multi-page image-based documents, chart-plus-reference-doc analysis, signature verification against a reference.
Streaming
Set stream: true to receive a Server-Sent Events stream of text deltas. The final data: line before [DONE] contains the usage + cost summary.
const res = await fetch('https://cencori.com/api/ai/vision', {
method: 'POST',
headers: {
'CENCORI_API_KEY': 'csk_...',
'Content-Type': 'application/json',
},
body: JSON.stringify({
image_url: 'https://example.com/photo.jpg',
prompt: 'Describe this scene',
stream: true,
}),
});
const reader = res.body!.getReader();
const decoder = new TextDecoder();
while (true) {
const { value, done } = await reader.read();
if (done) break;
const text = decoder.decode(value);
// Parse SSE frames: `data: {...}\n\n`
for (const line of text.split('\n')) {
if (line.startsWith('data: ') && line !== 'data: [DONE]') {
const chunk = JSON.parse(line.slice(6));
if (chunk.delta) process.stdout.write(chunk.delta);
}
}
}Supported Models
Image Format Limits
Universal safe set: JPEG, PNG, WEBP, GIF. Requests that violate the provider's limit return a structured 400 with error: "unsupported_format" or "image_too_large".
Multipart file upload
The endpoints also accept multipart/form-data for direct file upload:
curl -X POST https://cencori.com/api/ai/vision \
-H "CENCORI_API_KEY: csk_..." \
-F "file=@photo.jpg" \
-F "prompt=What's in this image?" \
-F "model=gpt-4o"Drop-in React component
The cencori/react subpath ships a <VisionUploader /> component you can embed in your own product — drag-drop, client-side format validation, auto-downscale for oversize JPEG/PNG/WEBP, clear error banner:
import { VisionUploader } from 'cencori/react';
<VisionUploader
endpoint="https://cencori.com/api/ai/vision"
apiKey={process.env.NEXT_PUBLIC_CENCORI_KEY}
task="describe"
onResult={(result) => console.log(result)}
/>HTTP API
curl -X POST https://cencori.com/api/ai/vision \
-H "CENCORI_API_KEY: csk_..." \
-H "Content-Type: application/json" \
-d '{
"image_url": "https://example.com/receipt.png",
"prompt": "Extract the total amount as JSON",
"response_format": "json"
}'In other SDKs
Python
from cencori import Cencori
cencori = Cencori()
# Analyze
result = cencori.vision.analyze(
image_url="https://example.com/photo.jpg",
prompt="What breed of dog is this?",
)
print(result["analysis"])
# OCR from a local file
import base64
with open("receipt.png", "rb") as f:
b64 = base64.b64encode(f.read()).decode()
ocr = cencori.vision.ocr(image_base64=b64, mime_type="image/png")
print(ocr["text"])Async variants: a_analyze(), a_describe(), a_ocr(), a_classify(). Full example: examples/vision.py.
Go
import (
"context"
"encoding/base64"
"os"
"github.com/cencori/cencori-go"
)
client, _ := cencori.NewClient(cencori.WithAPIKey(os.Getenv("CENCORI_API_KEY")))
// Analyze
result, _ := client.Vision.Analyze(context.Background(), &cencori.VisionParams{
ImageURL: "https://example.com/photo.jpg",
Prompt: "What breed of dog is this?",
})
fmt.Println(result.Analysis)
// OCR from a local file
data, _ := os.ReadFile("receipt.png")
ocr, _ := client.Vision.OCR(context.Background(), &cencori.VisionParams{
ImageBase64: base64.StdEncoding.EncodeToString(data),
MimeType: "image/png",
})
fmt.Println(ocr.Text)Full example: examples/12-vision.
PHP
use Cencori\Cencori;
$cencori = new Cencori();
// Analyze
$result = $cencori->vision->analyze([
'image_url' => 'https://example.com/photo.jpg',
'prompt' => 'What breed of dog is this?',
]);
echo $result['analysis'];
// OCR from a local file
$b64 = base64_encode(file_get_contents('receipt.png'));
$ocr = $cencori->vision->ocr(['image_base64' => $b64, 'mime_type' => 'image/png']);
echo $ocr['text'];Full example: examples/vision.php.
Rust
use cencori::vision::VisionParams;
use cencori::Cencori;
let cencori = Cencori::new(None, None, None)?;
// Analyze
let params = VisionParams::from_url("https://example.com/photo.jpg")
.with_prompt("What breed of dog is this?");
let result = cencori.vision.analyze(¶ms)?;
println!("{}", result.analysis);
// OCR: build with VisionParams::from_base64(base64_string, "image/png")
let ocr = cencori.vision.ocr(&VisionParams::from_base64(b64, "image/png"))?;
println!("{}", ocr.text);Async variants: cencori.vision.async_analyze(¶ms).await. Full example: examples/vision.rs.
Also: automatic vision routing in chat
You don't have to use /api/ai/vision directly to get vision. If you send image content through the regular /api/ai/chat endpoint, Cencori auto-detects the image, transparently routes through Vision, and even upgrades the model if the one you picked doesn't support vision (claude-3-5-haiku → claude-3-5-sonnet-latest, gpt-3.5-turbo → gpt-4o-mini, etc.). See Vision Auto-Routing in Chat.
Use /api/ai/vision directly when:
- You want OCR / describe / classify presets
- You want structured JSON classification
- You want the multipart file upload path
- You want cleaner cost tracking against a Vision endpoint
Use /api/ai/chat with images when:
- You're extending an existing chat flow
- You want a model-agnostic path that "just works" no matter which model your users pick

