Vision মানে model-কে একটা image পাঠিয়ে সেটা নিয়ে প্রশ্ন করা: যেমন error-এর screenshot, কোনো diagram, বা whiteboard-এর ছবি। Tokens-এ image আলাদা করে যায় না, সাধারণ chat request-এর ভেতরেই যায়। POST https://tokens.bd/v1/chat/completions-এ যায় image_url content part হিসেবে, আর POST https://tokens.bd/v1/messages-এ image content block হিসেবে। gateway image-টা provider পর্যন্ত পৌঁছে দেয় আর provider যা report করে, সেটাই bill করে। model image পড়তে পারবে কি না, সেটা model-এর ওপর নির্ভর করে।
Tokens-এ আলাদা কোনো image endpoint নেই। image generation, file upload আর Anthropic Files API এখানে চলে না (Models ও usage দেখুন)। এই পেজ শুধু image input নিয়ে।
Model image নেয় কি না যাচাই করুন#
সব model image পড়তে পারে না। /models পেজের model catalog-এ প্রতিটা model-এর context window, দাম আর বর্ণনা আছে, কিন্তু "image support করে" বলে আলাদা কোনো field নেই। জানার উপায়:
- /models পেজে model-এর বর্ণনা পড়ুন, আর model-টা যারা বানিয়েছে তাদের নিজেদের documentation দেখুন।
- Model বেছে নেওয়া পেজের vision অংশে কয়েকটা model-এর তালিকা আছে, কোনটা কী নেয় সেই হিসেবে।
- একটা ছোট test image পাঠিয়ে (নিচের উদাহরণগুলো) জিজ্ঞেস করুন "What is in this image?"। যে model image পড়ে, সে ছবি নিয়েই উত্তর দেবে।
text-only model সব সময় জোরে আওয়াজ করে fail করে না। সেটা provider-এর সিদ্ধান্ত: হয় 400 পাঠাবে (আপনার কাছে আসবে invalid_request হয়ে, errors দেখুন), নয়তো image বাদ দিয়ে শুধু text দেখে উত্তর দেবে। উত্তরে ছবির কোনো ছাপ না থাকলে ধরে নিন, এই model image নেয় না।
Chat Completions: image_url part#
message-এর content-এ string-এর বদলে একটা array দিন। array-র প্রতিটা element হয় text part, নয়তো image_url part। url হবে একটা public https URL, অথবা base64 data URL (data:<media type>;base64,<data>)।
{
"role": "user",
"content": [
{ "type": "text", "text": "What does this error screenshot say?" },
{
"type": "image_url",
"image_url": { "url": "data:image/png;base64,<BASE64_DATA>", "detail": "auto" }
}
]
}detail হলো OpenAI-এর optional hint (low, high বা auto; না দিলে auto)। অন্য provider এটা না-ও মানতে পারে। Anthropic-style provider-এর জন্য gateway যখন request translate করে, তখন detail বাদ দিয়ে দেয় (নিচে দেখুন)।
curl https://tokens.bd/v1/chat/completions \
-H "Authorization: Bearer $TOKENS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek/deepseek-v4.1-flash",
"max_tokens": 300,
"messages": [
{
"role": "user",
"content": [
{"type": "image_url", "image_url": {"url": "https://YOUR-HOST/path/to/image.png"}},
{"type": "text", "text": "Describe this image in two sentences."}
]
}
]
}'import base64
import os
from openai import OpenAI
client = OpenAI(base_url="https://tokens.bd/v1", api_key=os.environ["TOKENS_API_KEY"])
with open("screenshot.png", "rb") as f:
b64 = base64.b64encode(f.read()).decode("ascii")
resp = client.chat.completions.create(
model="deepseek/deepseek-v4.1-flash",
max_tokens=300,
messages=[
{
"role": "user",
"content": [
{"type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64}"}},
{"type": "text", "text": "Describe this image in two sentences."},
],
}
],
)
print(resp.choices[0].message.content)
print(resp.usage)import fs from "node:fs";
import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://tokens.bd/v1", apiKey: process.env.TOKENS_API_KEY });
const b64 = fs.readFileSync("screenshot.png").toString("base64");
const resp = await client.chat.completions.create({
model: "deepseek/deepseek-v4.1-flash",
max_tokens: 300,
messages: [
{
role: "user",
content: [
{ type: "image_url", image_url: { url: `data:image/png;base64,${b64}` } },
{ type: "text", text: "Describe this image in two sentences." },
],
},
],
});
console.log(resp.choices[0].message.content, resp.usage);https://YOUR-HOST/path/to/image.png-এর জায়গায় আপনার নিজের image-এর link বসান। data URL-এর media type ফাইলের সাথে মিলতে হবে (image/png, image/jpeg, image/webp, image/gif)।
URL দেওয়া image কে আনে
https URL দিলে Tokens image download করে না। URL-টা যেমন আছে তেমন forward করে দেয়, আর image আনে provider। তাই link-টা public হতে হবে আর provider সেখানে পৌঁছাতে পারতে হবে। কোনো কোনো provider শুধু base64 data URL নেয়। URL দিলে fail করছে কিন্তু একই image base64-এ চলছে, তাহলে base64-ই ব্যবহার করুন।
Messages: image content block#
/v1/messages-এ image হলো source-ওয়ালা একটা content block। Tokens-এ দুই ধরনের source চলে:
{
"role": "user",
"content": [
{
"type": "image",
"source": { "type": "base64", "media_type": "image/png", "data": "<BASE64_DATA>" }
},
{ "type": "text", "text": "What does this error screenshot say?" }
]
}{
"type": "image",
"source": { "type": "url", "url": "https://YOUR-HOST/path/to/image.png" }
}data হবে খাঁটি base64 string, শুরুতে data: prefix থাকবে না। Anthropic-এর তৃতীয় source {"type": "file", "file_id": "..."} চলতে হলে তাদের Files API লাগে, যেটা Tokens-এ নেই। তাই image-টাই সরাসরি পাঠান।
curl https://tokens.bd/v1/messages \
-H "x-api-key: $TOKENS_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "deepseek/deepseek-v4.1-flash",
"max_tokens": 300,
"messages": [
{
"role": "user",
"content": [
{"type": "image", "source": {"type": "url", "url": "https://YOUR-HOST/path/to/image.png"}},
{"type": "text", "text": "Describe this image in two sentences."}
]
}
]
}'import base64
import os
import anthropic
client = anthropic.Anthropic(base_url="https://tokens.bd", api_key=os.environ["TOKENS_API_KEY"])
with open("screenshot.png", "rb") as f:
b64 = base64.standard_b64encode(f.read()).decode("ascii")
message = client.messages.create(
model="deepseek/deepseek-v4.1-flash",
max_tokens=300,
messages=[
{
"role": "user",
"content": [
{"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": b64}},
{"type": "text", "text": "Describe this image in two sentences."},
],
}
],
)
print(message.content[0].text)
print(message.usage)import fs from "node:fs";
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({ baseURL: "https://tokens.bd", apiKey: process.env.TOKENS_API_KEY });
const b64 = fs.readFileSync("screenshot.png").toString("base64");
const message = await client.messages.create({
model: "deepseek/deepseek-v4.1-flash",
max_tokens: 300,
messages: [
{
role: "user",
content: [
{ type: "image", source: { type: "base64", media_type: "image/png", data: b64 } },
{ type: "text", text: "Describe this image in two sentences." },
],
},
],
});
console.log(message.content[0], message.usage);Anthropic-এর পরামর্শ: যে text দিয়ে image নিয়ে প্রশ্ন করছেন, image রাখুন তার আগে। একাধিক image থাকলে text-এ label দিন ("Image 1:", "Image 2:"), তাহলে পরে নাম ধরে বলা যাবে। conversation-এর আগের image model-এর চোখের সামনেই থাকে, কিন্তু প্রতিটা request-এ সেগুলো আবার পাঠাতে হবে, কারণ API কিছু মনে রাখে না।
Command line থেকে base64#
base64 image এত বড় যে curl -d '...' argument-এ হাতে লেখা যায় না। request-টা একটা file-এ লিখে সেই file পাঠান। macOS আর Linux-এ, jq install থাকলে:
base64 screenshot.png | tr -d '\n' > screenshot.b64
jq -n --rawfile img screenshot.b64 '{
model: "deepseek/deepseek-v4.1-flash",
max_tokens: 300,
messages: [{
role: "user",
content: [
{type: "image_url", image_url: {url: ("data:image/png;base64," + $img)}},
{type: "text", text: "Describe this image in two sentences."}
]
}]
}' > request.json
curl https://tokens.bd/v1/chat/completions \
-H "Authorization: Bearer $TOKENS_API_KEY" \
-H "Content-Type: application/json" \
-d @request.jsonImage নিয়ে gateway কী করে#
native request-এ (provider আপনার request-এর format-ই বোঝে) gateway body forward করে, শুধু model field বদলায় (আর stream করা Chat Completions-এ usage report করার একটা option যোগ করে)। image decode, resize বা যাচাই কিছুই করে না।
কিছু model এমন provider-এর কাছেই পাওয়া যায়, যে অন্য format বোঝে। তখন gateway request translate করে, আর image-এর ক্ষেত্রে হয় এরকম:
| আপনার request | Provider যে format বোঝে | Image-এর কী হয় |
|---|---|---|
Messages (image block) | OpenAI format | Base64 হয়ে যায় data URL, আর URL থাকে URL-ই, user message-এর image_url part হিসেবে। assistant turn-এর image বাদ পড়ে। tool_result-এর ভেতরের image আর image হিসেবে যায় না (সতর্কতাটা দেখুন)। |
Chat Completions (image_url part) | Anthropic format | data URL হয়ে যায় base64 image block, অন্য যেকোনো URL হয় URL image block। detail বাদ পড়ে। শুধু user message-এর image যায়। |
Tool result-এর ভেতরের image
যে Messages request gateway OpenAI format-এ translate করে, সেখানে tool_result-এর content-এ image block থাকলে পুরোটা text হয়ে যায়, আর image block-টা JSON হিসেবে সেই text-এর ভেতরে লেখা হয়। model কোনো ছবি দেখে না, উল্টে base64 data input token হিসেবে গোনা হয়। আপনার agent যদি tool থেকে screenshot ফেরত দেয়, তাহলে Anthropic format-এ চলে এমন model নিন, অথবা tool-কে দিয়ে image-টার বর্ণনা text-এ ফেরত দেওয়ান।
translation-এর একই নিয়ম আর যেসব field সাথে যায়, সেগুলো Messages পেজে লেখা আছে।
Size limit#
দুটো limit আছে, আর যেটা ছোট সেটাই খাটে।
Tokens: পুরো request body সর্বোচ্চ 10 MB হতে পারে, নইলে gateway 413 request_entity_too_large দেয়। base64 করলে data ফাইলের চেয়ে প্রায় এক-তৃতীয়াংশ বড় হয়ে যায়। তাই এক request-এর সব image মিলিয়ে বড়জোর প্রায় 7 MB-র image file যেতে পারে, আর text এবং আগের turn গুনলে আরও কম। প্রতিটা request পুরো conversation আবার পাঠায়, আগের image-সহ।
Provider: image-এর format, dimension, সংখ্যা আর প্রতিটা image-এর size নিয়ে প্রত্যেক provider-এর নিজস্ব limit আছে। October 2026-এ যেমন দেখা গেছে:
- Anthropic: JPEG, PNG, GIF (শুধু প্রথম frame) আর WebP; প্রতি image সর্বোচ্চ 8000 x 8000 pixel; প্রতি image 10 MB (base64); 200K-token context window-ওয়ালা model-এ এক request-এ 100টা পর্যন্ত image, বাকি model-এ 600টা। এক request-এ 20টার বেশি image হলে প্রতি image-এর pixel-এর কড়াকড়ি বাড়ে, আর Anthropic প্রতিটা পাশ 2000 px-এর মধ্যে রাখতে বলে। সূত্র: Anthropic-এর vision documentation।
- OpenAI: PNG, JPEG, WebP আর animation ছাড়া GIF; এক request-এ 1,500টা পর্যন্ত image। সূত্র: OpenAI-এর images ও vision guide। OpenAI-এর 512 MB payload limit Tokens-এর 10 MB-র চেয়ে বড়, তাই আগে Tokens-এর limit-ই লাগে।
- অন্য maker-রা নিজেদের limit নিজেরা প্রকাশ করে। যে model ব্যবহার করছেন, তার maker-এর documentation দেখে নিন।
বড় ছবি পাঠানোর আগে resize করে নিন। ফোনের ছবি প্রায়ই 4000 pixel চওড়া হয়, আর model সেটা এমনিই ছোট করে নেয়। ফলে upload-এর size আর latency-র দাম দিচ্ছেন, কিন্তু detail বাড়ছে না। screenshot-এর লেখা যেন পড়া যায়, সেদিকে খেয়াল রাখুন: JPEG বেশি জোরে compress করলে ছোট লেখা আর পড়া যায় না।
Image input-এর billing#
provider image-কে input token-এ রূপান্তর করে, আর মোট সংখ্যাটা usage-এ report করে। Tokens সেই input count-কে model-এর input price ধরে bill করে, অন্য যেকোনো input-এর মতোই। মোটামুটি, provider-রা image গোনে patch ধরে: Anthropic-এর documentation অনুযায়ী প্রতি image-এ ceil(width / 28) x ceil(height / 28) token, তবে আগে image ছোট করে একটা সীমায় আনা হয় (standard tier-এ প্রায় 1,568 token, high-resolution tier-এ 4,784 পর্যন্ত)। OpenAI-র documentation-এ patch-based আর tile-based দুই রকম হিসাব আছে, যা model আর detail-এর ওপর নির্ভর করে। অন্যদের হিসাব আলাদা। তাই আপনার model-এর আসল খরচ জানতে একটা test request পাঠিয়ে usage দেখে নিন।
Admission অন্য একটা সংখ্যা ধরে চলে। request forward করার আগে gateway আপনার ব্যালান্স থেকে তার worst-case খরচ reserve করে, আর input অংশের হিসাব ধরে request body-র প্রতি চার character-এ এক token। বড় base64 image মানে অনেক character, তাই কয়েক MB-র image-ওয়ালা request-এর reservation provider-এর শেষ পর্যন্ত যা charge করবে তার চেয়ে অনেক বেশি হতে পারে। আপনার কাছ থেকে আসল usage-ই কাটা হয়, reservation নয়। তবু ব্যালান্স কম থাকলে, বা key-র spend cap-এর কাছাকাছি থাকলে, যে request-এর খরচ আসলে খুবই কম হতো সেটাও insufficient_credits বা monthly_spend_cap_exceeded দিয়ে ফিরিয়ে দেওয়া হতে পারে। image resize করলে খরচও কমে, refusal-ও যায়। reservation নিয়ে বিস্তারিত Chat Completions পেজে আছে।
সমস্যা হলে#
| লক্ষণ | সম্ভাব্য কারণ | সমাধান |
|---|---|---|
400 invalid_request, message-এ image বা content আছে | Model image input নেয় না, অথবা media type-টা সে support করে না | image নেয় এমন model চেষ্টা করুন; data URL-এর media type ফাইলের সাথে মিলছে কি না দেখুন। |
| উত্তরে ছবির কোনো উল্লেখ নেই | text-only model-এ provider image বাদ দিয়ে দিয়েছে | image পড়ে এমন model-এ যান। |
413 request_entity_too_large | Body 10 MB-র বেশি, সাধারণত base64 image বা image-ভরা লম্বা history | Resize বা compress করুন, কম image পাঠান, অথবা পুরোনো turn বাদ দিন। |
ব্যালান্স অল্প, তবু 402 insufficient_credits | input-এর হিসাবে base64-এর character-গুলোও ধরা হয় | image resize করুন, অথবা billing থেকে টাকা যোগ করুন। |
| URL image fail করে, base64 চলে | Provider URL আনতে পারেনি, অথবা শুধু base64 নেয় | base64 ব্যবহার করুন, নয়তো URL public করুন। |
| tool screenshot-ওয়ালা Messages request, model কিছুই দেখে না | translate হওয়া model-এ tool_result-এর image text হয়ে যায় | উপরের সতর্কতাটা দেখুন। |
error-এ কী ভুল হয়েছে স্পষ্ট না থাকলে upstream-এর message বদলে একটা সাধারণ message দেওয়া হয়। তখন x-tokens-request-id নিয়ে Support-এ জানান, আর code-গুলোর জন্য errors দেখুন।