OpenRouter আর Tokens, দুটোই একটা OpenAI-compatible endpoint দিয়ে অনেক provider-এর model দেয়। তাই OpenRouter-এর জন্য লেখা app-এ সাধারণত অন্য যেকোনো OpenAI app-এর মতোই তিনটা বদল লাগে: base URL, key আর model id। আসল কাজটা অন্য জায়গায়: OpenAI format-এর ওপর OpenRouter যা যা বাড়তি করে, যেমন provider routing, model fallback, model suffix, attribution header আর cost field। Tokens এগুলো করে না। প্রতিটার কী হয়, তা এই পাতায় আলাদা করে বলা আছে।
কী বদলায়, কী একই থাকে#
| Setting | OpenRouter | Tokens | কোথায় বসাবেন |
|---|---|---|---|
| Base URL | https://openrouter.ai/api/v1 | https://tokens.bd/v1 | SDK-তে base_url বা baseURL |
| API key | OpenRouter-এর একটা key | API keys থেকে tok_live_... | api_key বা apiKey |
| Model id | provider/model, OpenRouter-এর slug | provider/model, /models থেকে Tokens-এর alias | প্রতিটা request-এর model field-এ |
| Header | HTTP-Referer, X-Title (attribution, ঐচ্ছিক) | লাগে না। বাদ দিন। | default_headers বা defaultHeaders |
যা একই থাকে:
POST /v1/chat/completions-এর OpenAI request ও response format:messages,tools,streamআরusageobject-সহ। দেখুন Chat Completions।Authorization: Bearer <key>দিয়ে authentication, আর Server-Sent Events streaming।- যেকোনো OpenAI SDK, অথবা OpenRouter-এ যে Vercel AI SDK, LangChain-এর মতো library চালাচ্ছিলেন, সেগুলো। এদের base URL আর key একই জায়গায় বদলে নিন।
আগে আর পরে#
import os
from openai import OpenAI
client = OpenAI(
- base_url="https://openrouter.ai/api/v1",
- api_key=os.environ["OPENROUTER_API_KEY"],
- default_headers={
- "HTTP-Referer": "https://example.com",
- "X-Title": "My app",
- },
+ base_url="https://tokens.bd/v1",
+ api_key=os.environ["TOKENS_API_KEY"],
)
resp = client.chat.completions.create(
- model="provider/openrouter-model-slug",
+ model="deepseek/deepseek-v4.1-flash",
messages=[{"role": "user", "content": "What does HTTP 429 mean?"}],
max_tokens=300,
)
print(resp.choices[0].message.content) import OpenAI from "openai";
const client = new OpenAI({
- baseURL: "https://openrouter.ai/api/v1",
- apiKey: process.env.OPENROUTER_API_KEY,
- defaultHeaders: { "HTTP-Referer": "https://example.com", "X-Title": "My app" },
+ baseURL: "https://tokens.bd/v1",
+ apiKey: process.env.TOKENS_API_KEY,
});
const resp = await client.chat.completions.create({
- model: "provider/openrouter-model-slug",
+ model: "deepseek/deepseek-v4.1-flash",
messages: [{ role: "user", content: "What does HTTP 429 mean?" }],
max_tokens: 300,
});
console.log(resp.choices[0].message.content);-curl https://openrouter.ai/api/v1/chat/completions \
- -H "Authorization: Bearer $OPENROUTER_API_KEY" \
- -H "HTTP-Referer: https://example.com" \
- -H "X-Title: My app" \
+curl https://tokens.bd/v1/chat/completions \
+ -H "Authorization: Bearer $TOKENS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
- "model": "provider/openrouter-model-slug",
+ "model": "deepseek/deepseek-v4.1-flash",
"messages": [{"role": "user", "content": "What does HTTP 429 mean?"}],
"max_tokens": 300
}'curl ব্যবহার করলে Content-Type: application/json রাখুন। এটা না থাকলে Tokens body পড়ে না, আর 400 invalid_request ফেরত দেয়।
আপনি যদি সেটিং পাঠান OPENAI_BASE_URL আর OPENAI_API_KEY variable দিয়ে (official OpenAI Python ও Node.js SDK এ দুটো পড়ে, SDK source দেখে নিশ্চিত করা, অক্টোবর 2026), তাহলে শুধু variable দুটো আর model id বদলান। আর কিছু ছুঁতে হবে না।
Model id দেখতে এক, কিন্তু আসলে এক নয়#
OpenRouter-এর slug আর Tokens-এর alias, দুটোই provider/model ধাঁচের। কিন্তু তালিকা দুটো আলাদা। যে slug OpenRouter-এ চলে, Tokens-এ সেটা অচেনা হতে পারে, বা অন্য version-এর নাম হতে পারে। আবার Tokens-এর alias সবসময় provider-এর নিজের id-র সাথে মেলে না। কোনো pattern ধরে id বদলাতে যাবেন না।
- আপনার key কোন কোন model call করতে পারে, তা দেখুন:
curl -s https://tokens.bd/v1/models -H "Authorization: Bearer $TOKENS_API_KEY"। প্রতিটাdata[].idওই key-র জন্য valid। - দাম, context window আর capability আছে /models পাতায়। API-র তালিকায় শুধু id থাকে। দেখুন Choosing a model।
- পুরোনো id থেকে নতুন id-র একটা table configuration-এ রাখুন, যাতে mapping code-এর নানা জায়গায় ছড়িয়ে না থাকে।
model-এর মান alias-এর সাথে হুবহু মিলতে হবে। অচেনা id দিলে 404 model_not_found আসে।
OpenRouter-এর feature, একটা একটা করে#
OpenRouter-এর প্রতিটা feature নিয়ে Tokens কী করে, তা এখানে। এটা gateway code দেখে লেখা, আন্দাজে নয়।
Model suffix। OpenRouter :free, :nitro, :floor, :exacto (আর বাতিল হয়ে যাওয়া :online, :thinking, :extended) model id-র অংশ হিসেবে চালায়। Tokens পুরো string-টাকেই alias হিসেবে খোঁজে, তাই some/model:nitro দিলে 404 model_not_found আসে। suffix বাদ দিন। এর সমতুল্য কিছু নেই: Tokens-এ suffix নেই, আর request ধরে provider sort করাও নেই। সস্তা বা দ্রুত model চাইলে সেটা আলাদা একটা model id।
Router model। openrouter/auto আর OpenRouter-এর অন্য router id Tokens catalog-এ নেই (404 model_not_found)। একটা নির্দিষ্ট model বেছে নিন।
Provider routing (provider)। Tokens এই field পড়ে না। আবার প্রত্যাখ্যানও করে না: যে model-এর provider OpenAI protocol বোঝে (সাধারণত তাই হয়), সেখানে request body-র বাকি অংশ যেমন পাঠানো হয়েছে তেমনই provider-কে দিয়ে দেওয়া হয়। provider field-টা উপেক্ষা করবে নাকি 400 দেবে, তা provider-এর ব্যাপার, আর Tokens কোনো provider-এর আচরণ যাচাই করেনি। field-টা বাদ দিন। কোন provider model চালাবে, সেটা ঠিক করে Tokens, আপনি নন। আর response-এর model field-এ সবসময় সেই id-ই থাকে যেটা আপনি চেয়েছিলেন।
Fallback (models, route)। Implement করা নেই। নিয়ম ওপরের মতোই: forward হয়, কিন্তু কাজে লাগানো হয় না, তাই field-টার কোনো কাজ নেই। Tokens failover করে ঠিকই, তবে শুধু একই model-এর এক source থেকে আরেক source-এ: 429, 502, 503, 504 বা connection কেটে গেলে, আর উত্তরের একটা byte-ও আপনার কাছে পৌঁছানোর আগে, সে ওই model-এরই আরেকটা source-এ retry করে। অন্য model-এ কখনো যায় না। model fallback চাইলে সেটা আপনার code-এ করুন:
import os
import openai
from openai import OpenAI
client = OpenAI(base_url="https://tokens.bd/v1", api_key=os.environ["TOKENS_API_KEY"], max_retries=0)
MODELS = ["deepseek/deepseek-v4.1-flash", "your-second-model-id"] # ids from GET /v1/models
def ask(messages):
last = None
for model in MODELS:
try:
return client.chat.completions.create(model=model, messages=messages, max_tokens=512)
except openai.APIStatusError as e:
if e.status_code not in (429, 500, 502, 503, 504) or e.code == "window_exhausted":
raise
last = e
raise lastwindow_exhausted বাদ রাখা হয়েছে, কারণ এটা পুরো অ্যাকাউন্টের plan window। অন্য model-এও একই জায়গায় আটকাবে।
Prompt transform আর plugin (transforms, plugins)। OpenRouter এগুলো দিয়ে লম্বা prompt ছোট করা, file parse করা, web search আর response মেরামতের সুবিধা দেয়। Tokens এর কিছুই করে না। provider-এর মতো এই field-গুলোও অচেনা body field হিসেবে provider-এর কাছে চলে যায়। prompt নিজেই ছোট করে নিন: model-এর context window ছাড়িয়ে গেলে Tokens prompt ছোট করে না, তখন কী হবে তা provider ঠিক করে।
reasoning আর usage field। বাকিগুলোর মতোই forward হয়। যে model reasoning সাপোর্ট করে, সেখানে এ দুটো নিয়ে কী করা হবে তা provider ঠিক করে। usage: {"include": true} request field-টা লাগেই না: OpenRouter-এর নিজের documentation একে deprecated বলে আর usage এমনিতেই পাঠায়, আর Tokens-এর উত্তরে provider-এর পাঠানো usage object সবসময় থাকে।
Attribution header। HTTP-Referer, X-Title আর X-OpenRouter-* header-গুলোর দাম শুধু OpenRouter-এ, app-এর পাতা আর ranking-এর জন্য। Tokens provider-কে request header পাঠায় একটা ছোট allow-list ধরে (content-type, accept, openai-beta আর anthropic-version, সাথে আরও কয়েকটা), বাকিগুলো error ছাড়াই ফেলে দেয়। পাঠালে ক্ষতি নেই, কিন্তু কোনো কাজও হয় না। code যেন উল্টো কিছু না বোঝায়, তাই বাদ দিয়ে দিন।
কোনো field নিরাপদ কি না নিশ্চিত না হলে। যে model ব্যবহার করবেন, সেটাতেই test করুন, নিচে বলা test key দিয়ে। provider কোনো field মানে কি না, তার একমাত্র প্রমাণ হলো চলতে থাকা একটা request।
যে model শুধু এমন provider থেকে আসে যে Anthropic-এর Messages protocol বোঝে, সেখানে Tokens আপনার chat completions request forward করে না, অনুবাদ করে নেয়। শুধু এই field-গুলো যায়: messages (text, image, tool call আর tool result), max_tokens বা max_completion_tokens, temperature, top_p, stop, stream, tools, tool_choice, parallel_tool_calls আর user। বাকি সব বাদ পড়ে, যেমন n, response_format, seed, logprobs আর penalty-গুলো। temperature সর্বোচ্চ 1-এ আটকে দেওয়া হয়, আর আপনি max_tokens না দিলে সেটা default 4096।
Response আর usage-এর পার্থক্য#
- Response-এর
model-এ সবসময় সেই id-ই থাকে যেটা আপনি চেয়েছিলেন, যে provider-ই উত্তর দিক। OpenRouter-এরmodel-এ থাকে সে আসলে কোন model-এ route করেছে। তাই routing কী হলো দেখতে আপনি যদি এটা log করতেন, নতুন কিছু পাবেন না। - Cost field। OpenRouter response-এ
cost,cost_detailsআরnative_finish_reasonজুড়ে দেয়। Tokens provider-এর body-তে কিছুই যোগ করে না। প্রতিটা request-এর খরচ আছে আপনার usage dashboard-এ আর CSV export-এ, model-এর catalog দামে হিসাব করা। আপনার নিজের customer-কে bill করতে যদিusage.costপড়তেন, তাহলে export-এ চলে আসুন, নয়তো token সংখ্যা আর /models-এর দাম দিয়ে নিজেই খরচ হিসাব করুন। - Usage খোঁজা।
GET /generation?id=বাGET /keyবলে কিছু নেই।GET /v1/tokens/usageদেয় plan window, Wallet-এর ব্যালান্স আর যে key দিয়ে call করছেন তার cap (Models and usage)। response headerx-tokens-request-idহলো সেই id, যা log-এ রাখবেন আর support-কে জানাবেন। - Streaming। Stream-এর শেষে usage chunk আসে যখন আপনি
stream_options: {"include_usage": true}পাঠান। দেখুন Streaming।
যে পার্থক্যগুলো ঝামেলা বাধাতে পারে#
Error#
OpenRouter-এর error দেখতে {"error": {"code": 429, "message": "...", "metadata": {...}}}, যেখানে code একটা সংখ্যা। Tokens-এর error হলো {"error": {"message", "type", "code", "param", "request_id"}}, আর এর code একটা string, যেমন rate_limited। যে code error.code-কে সংখ্যা ধরে পড়ে, বা error.metadata পড়ে, তা বদলাতে হবে: error কোন ধরনের তা বুঝতে HTTP status আর কারণ বুঝতে error.code দেখুন। পুরো table errors পাতায়।
| অবস্থা | OpenRouter | Tokens |
|---|---|---|
| Credit শেষ | 402 | 402 insufficient_credits, no_funding বা outstanding_debt |
| Rate limit | 429 | 429 rate_limited, concurrency_limit, window_exhausted বা model_limit_reached |
| আপনার দেওয়া key limit | Key credit limit: 402 | 403 monthly_spend_cap_exceeded (দেখুন API keys) |
| Timeout | 408 | 504 upstream_timeout |
| Model down | 502 | 502 upstream_unreachable, অথবা provider-এর 5xx, upstream_error হিসেবে |
| কোনো provider নেই | 503 | 503 no_upstream_available |
OpenRouter-এর documentation বলে, streaming শুরু হওয়ার পরে কিছু fail করলে সেটা 200 response-এর ভেতরে একটা error event হয়ে আসে। এর জন্য আপনি body-র ভেতরে যে error check বসিয়েছিলেন, সেটা রেখে দিন।
Provider-এর নিজের error message বদলে একটা সাধারণ message বসানো হয়, আর metadata.provider_*-এর খুঁটিনাটি তথ্য নেই।
Rate limit#
OpenRouter free model-কে প্রতি মিনিটে 20 request-এ বেঁধে রাখে, আর paid model-এ platform-এর কোনো cap দেয় না। Tokens সীমিত করে অ্যাকাউন্ট ধরে প্রতি মিনিটের request (default 60, নয়তো আপনার plan-এর মান) আর অ্যাকাউন্ট ধরে একসাথে চলা request (plan থাকলে 10, না থাকলে 3)। OpenRouter-এ যে agent বা batch job অনেক parallelism নিয়ে চলত, সে এখানে concurrency_limit-এ আটকাতে পারে। তার parallelism সীমিত করুন। বেশি key বানালে limit বাড়ে না।
- 429-এর সাথে
Retry-Afterআসে সেকেন্ডে।X-RateLimit-*header নেই। plan window দেখতেGET /v1/tokens/usagepoll করুন। window_exhaustedআরmodel_limit_reachedমানে অপেক্ষা ঘণ্টা বা দিনেরও হতে পারে। এ দুটো loop-এ retry করবেন না।- সংখ্যাগুলো আর একটা backoff উদাহরণ পাবেন rate limits পাতায়।
আরও কিছু পার্থক্য#
/api/v1path নেই। Tokens-এর path/v1:https://tokens.bd/v1।- Supported নয় এমন endpoint। Images, audio, files, batches, assistants, fine-tuning আর moderations 404
unsupported_endpointদেয়। Embeddings চলে শুধু embedding model-এ। Supported:/v1/chat/completions,/v1/responses,/v1/completions(legacy),/v1/embeddings,/v1/models,/v1/messagesআর/v1/messages/count_tokens। - Parameter নির্ভর করে model-এর ওপর।
modelআরn(1 থেকে 4) ছাড়া Tokens body validate করে না। Tool calling,response_format, vision আর reasoning model ভেদে আলাদা। OpenAI o-series আর GPT-5 ও তার পরের model-এ chat completions-এ gatewaymax_tokens-এর নাম বদলেmax_completion_tokensকরে দেয়, আরtemperatureওtop_p1-এর সমান না হলে সরিয়ে ফেলে। - Output reservation। Tokens প্রতিটা request-এর সবচেয়ে খারাপ ক্ষেত্রের খরচ reserve করে,
max_tokensধরে, না দিলে 8,192 output token ধরে। ব্যালান্স কম থাকলে বা key-র cap প্রায় ভরে এলে বড়max_tokensপ্রত্যাখ্যাত হতে পারে বা কমিয়ে দেওয়া হতে পারে। যতটা দরকার ততটাই দিন। - CORS নেই। Browser থেকে call fail করে। Tokens call করুন server থেকে।
- Body-র সাইজ। 10 MB পর্যন্ত।
- গোপনীয়তা। Tokens রাখে usage metadata, prompt-এর লেখা নয়। যে provider model চালায় সে prompt দেখে, আর তার policy-ই খাটে। দেখুন security and privacy।
- পেমেন্ট। Tokens-এ bill হয় USD বা BDT-তে, plan বা Wallet থেকে। OpenRouter credit বলে কিছু নেই। দেখুন plans and wallet।
Anthropic SDK দিয়ে OpenRouter-এর Anthropic-ধাঁচের Messages endpoint call করলে Anthropic থেকে চলে আসা পাতাটাও পড়ে নিন। Tokens POST /v1/messages দেয়, আর সেখানে base URL-এ /v1 থাকে না।
নিরাপদে switch পরীক্ষা করুন#
- দ্বিতীয় একটা key বানান /dashboard/keys-এ, একটা কম monthly spend cap দিয়ে, আর এমন allowed-models list দিয়ে যাতে শুধু test করা model-গুলোই থাকে। দুটোর কোনোটাই পরে বদলানো যায় না। Tokens প্রতিটা request-এর সবচেয়ে খারাপ ক্ষেত্রের খরচ cap-এর বিপরীতে গোনে।
- Base URL, key আর model id configuration থেকে পড়ুন, constant থেকে নয়, যাতে switch করা মানে শুধু config বদল।
- কিছুদিন দুটোই চালান। log থেকে request replay করুন, অথবা live traffic-এর একটা অংশ Tokens-এ mirror করে তার উত্তর ফেলে দিন। একই prompt দুই জায়গায় চালিয়ে মিলিয়ে দেখুন:
| যা দেখবেন | কীভাবে |
|---|---|
| মান | নিজের prompt বা eval চালান। জোড়ার দুটো model খুব কমই একই model হয়। |
| যে field আর পাঠাচ্ছেন না | provider, models, transforms আর header ছাড়া একবার চালান। কোনো কিছু কি এগুলোর ওপর নির্ভর করত? |
| Tool call | আপনার বেছে নেওয়া model-এ আপনার schema অনুযায়ী argument valid JSON কি না। |
finish_reason | আগের চেয়ে বেশি length মানে max_tokens কম পড়ছে। |
| একটা কাজ শেষ করার খরচ | OpenRouter-এর usage.cost-এর সাথে usage-এর খরচ মেলান। |
| Error | error.code ধরে গুনুন। |
- ধীরে ধীরে বাড়ান, feature flag বা শতাংশ ধরে। cut over-এর আগেই আপনার পছন্দমতো cap দিয়ে production key বানিয়ে রাখুন।
Roll back করবেন যেভাবে#
- Tokens পুরো একটা billing cycle production traffic সামলানোর আগে পর্যন্ত আপনার OpenRouter key আর credit রেখে দিন।
- পুরোনো base URL, key, model id আর header configuration-এ ফিরিয়ে দিন, তারপর deploy করুন বা flag উল্টে দিন।
- যে Tokens key আর লাগবে না, সেটা /dashboard/keys-এ revoke করুন। আপনার Wallet-এর ব্যালান্স অ্যাকাউন্টেই থাকে। দেখুন refund policy।
id mapping যেহেতু configuration-এ আছে, roll back মানে একই বদলটা উল্টো দিকে করা।
এরপর কোথায় যাবেন#
- Chat Completions, Streaming আর Tool calling।
- Errors আর Rate limits।
- OpenAI-ধাঁচের switch-এর সাধারণ অংশগুলোর জন্য OpenAI থেকে চলে আসা।
সূত্র, অক্টোবর 2026-এ দেখা: OpenRouter-এর API overview, errors, limits, provider routing, model fallbacks, model variants, usage accounting আর app attribution। Tokens-এর আচরণ নেওয়া হয়েছে gateway code আর এখানে দেওয়া পাতাগুলো থেকে। কোনো live OpenRouter অ্যাকাউন্টের বিপরীতে এটা test করা হয়নি।