কোনো request fail করলে, বা খরচ আশার চেয়ে বেশি হলে আপনার দরকার ঠিক ওই call-টাকে আঙুল দিয়ে দেখানো। Tokens-এ প্রতিটা response-এ একটা request id থাকে, আর যে request-এর বিল হয়েছে তার জন্য usage page-এ একই id-সহ একটা row তৈরি হয়। এই পেজে দেখবেন id কোথায় পাবেন, তার আশপাশে আর কী log করবেন, app-এর failure কীভাবে এমন curl command বানাবেন যা যে কেউ চালাতে পারে, আর কী ঘটেছে তা বলে দেওয়া দুটো জায়গা (error body আর usage page) কীভাবে পড়বেন।
Request id header#
/v1-এর প্রতিটা response-এ, সফল হোক বা error, এই header-গুলো থাকে:
| Header | Value |
|---|---|
x-tokens-request-id | এই request-এর জন্য gateway-র id (একটা UUID)। এটা সব সময় Tokens-ই বানায়। Support-কে এটাই জানাবেন। |
x-request-id | আপনি যে x-request-id পাঠিয়েছিলেন হুবহু সেটাই। না পাঠালে ওপরের id-টাই |
x-trace-id | সফল inference response-এ: x-tokens-request-id-এর মতোই একই মান |
Error body-তেও id-টা request_id নামে থাকে। /v1/chat/completions, /v1/responses আর OpenAI format-এর বাকি endpoint-এ সেটা error-এর ভেতরে থাকে। /v1/messages Anthropic-এর error format ব্যবহার করে, তাই সেখানে request_id থাকে body-র একদম ওপরের স্তরে, error-এর পাশে:
{
"error": {
"message": "Prepaid wallet balance is insufficient for this request.",
"type": "insufficient_quota",
"code": "insufficient_credits",
"param": null,
"request_id": "8f0c7a4e-2b1d-4c55-9a51-3f7e2d9b6c10"
}
}{
"type": "error",
"error": {
"type": "billing_error",
"message": "Prepaid wallet balance is insufficient for this request.",
"code": "insufficient_credits"
},
"request_id": "8f0c7a4e-2b1d-4c55-9a51-3f7e2d9b6c10"
}কয়েকটা কথা মনে রাখবেন:
- Tokens provider-এর header পাস করে না। শুধু content type,
cache-control,retry-after,x-request-idআরx-tokens-*header আপনার কাছে পৌঁছায়। model-এর নিজের provider-এর request id কখনো দেখতে পাবেন না, তাই Tokens-এর id-টাই জানান। - Id আসে header-এর সাথেই। Streaming-এ প্রথম token আসার আগেই এটা পাওয়া যায়। call শুরুর সময়ই log করে রাখুন, তাহলে stream পরে মাঝপথে ভেঙে গেলেও id আপনার হাতে থাকবে।
- আপনার নিজের
x-request-idযেমন পাঠিয়েছেন তেমনই ফেরত আসে। এটা দিয়ে আপনার log-এর লাইনের সাথে Tokens-এর id মেলান। প্রতিটা attempt-এর জন্য নতুন একটা পাঠান, user-এর একটা action-এর জন্য একটা নয়। তাহলে retry আর তার প্রথম চেষ্টা আলাদা করে চেনা যায়। - আপনার দিকে timeout হলে Tokens-এর id পাবেন না, কারণ কোনো response-ই আসেনি। এই কারণেই পাঠানোর আগে নিজের id-ও log করে রাখা উচিত।
- SDK-র helper আপনার id দেখাতে পারে, আমাদেরটা নয়। কিছু SDK
x-request-idথেকে পড়া একটাrequest_idদেয়। আপনি নিজেরx-request-idপাঠালে ওই মান আপনারই। gateway-র id পেতে raw header থেকেx-tokens-request-idপড়ুন।
Code-এ id পড়ার উপায়#
curl -sS -i https://tokens.bd/v1/chat/completions \
-H "Authorization: Bearer $TOKENS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"deepseek/deepseek-v4.1-flash","messages":[{"role":"user","content":"ping"}],"max_tokens":20}' \
| grep -i "^x-tokens-request-id"import os
import openai
from openai import OpenAI
client = OpenAI(base_url="https://tokens.bd/v1", api_key=os.environ["TOKENS_API_KEY"])
try:
raw = client.chat.completions.with_raw_response.create(
model="deepseek/deepseek-v4.1-flash",
messages=[{"role": "user", "content": "ping"}],
max_tokens=20,
)
print("request id:", raw.headers.get("x-tokens-request-id"))
completion = raw.parse()
except openai.APIStatusError as e:
print("failed:", e.status_code, e.code, e.response.headers.get("x-tokens-request-id"))
raiseimport OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://tokens.bd/v1",
apiKey: process.env.TOKENS_API_KEY,
});
try {
const { data, response } = await client.chat.completions
.create({
model: "deepseek/deepseek-v4.1-flash",
messages: [{ role: "user", content: "ping" }],
max_tokens: 20,
})
.withResponse();
console.log("request id:", response.headers.get("x-tokens-request-id"));
console.log(data.choices[0]?.message.content);
} catch (err) {
if (err instanceof OpenAI.APIError) {
const headers = err.headers as Headers | Record<string, string | undefined> | undefined;
const id = headers instanceof Headers ? headers.get("x-tokens-request-id") : headers?.["x-tokens-request-id"];
console.error("failed:", err.status, err.code, id);
}
throw err;
}Anthropic SDK-র ক্ষেত্রেও একই header raw response-এ থাকে। সেখানে কীভাবে পৌঁছাবেন, তা প্রতিটা SDK-র নিজের docs-এ আছে। fetch বা requests দিয়ে call করলে response header সরাসরি পড়ুন।
কী log করবেন#
প্রতিটা attempt-এর জন্য একটা structured line-ই যথেষ্ট। response-এর header আসার সময় log করুন, আর পরে কিছু fail করলে আবার।
| Field | কেন |
|---|---|
x-tokens-request-id | এটা দিয়ে support আপনার request খুঁজে পায় |
| আপনার নিজের request id | call-টাকে আপনার log আর retry-র ধারার সাথে বেঁধে রাখে |
| সময়, time zone সহ (UTC) | id না থাকলে এটাই ভরসা |
| Endpoint আর model | chat/completions, messages, আর model id হুবহু |
HTTP status আর error.code | কী ঘটেছে। branch করবেন code দেখে, message দেখে নয় |
| Latency, আর stream হলে প্রথম token আসতে সময় | provider ধীর না আপনার client ধীর, তা আলাদা করা যায় |
Response-এর usage | input token, cached token, output token, যাতে খরচ ব্যাখ্যা করতে পারেন |
| Attempt নম্বর | retry চেনা যায় |
| কোন key (নাম, secret কখনোই নয়) | একাধিক key থাকলে সঠিকটা খুঁজে পাওয়া যায় |
যা log করবেন না:
- API key, কোনোভাবেই নয়। header-এ নয়, dump করা config-এও নয়।
- পুরো prompt আর উত্তর, ডিফল্ট হিসেবে। এতে আপনার customer-এর data থাকতে পারে। Tokens নিজেও prompt-এর content রাখে না, রাখে request id ধরে usage metadata, তাই request খুঁজে পেতে id-ই যথেষ্ট। debug-এর জন্য body log করলে retention ছোট রাখুন আর স্পর্শকাতর অংশ মুছে দিন।
Support-কে id জানানোর নিয়ম#
Support-এ ticket খোলার সময় এগুলো দিন:
x-tokens-request-id। সমস্যাটা বারবার হলে একাধিক id।- কখন ঘটেছে, time zone সহ।
- Endpoint আর model id।
- HTTP status আর
error.code। বিলের প্রশ্ন হলে কত কাটা উচিত ছিল বলে আপনি আশা করেছিলেন। - সমস্যাটা আবার ঘটানো যায় কি না, আর হাতে থাকলে curl command (পরের অংশে আছে)।
API key কখনো দেবেন না। Id থেকেই support আপনার call-এর usage metadata খুঁজে পায়। বাকি প্রক্রিয়া সাহায্য পাওয়ার পেজে আছে, আর সবার জন্যই কিছু ভাঙা কি না তা status page-এ দেখা যায়।
curl দিয়ে fail হওয়া request আবার চালান#
App-এ request fail করলে app-টাকে ছবি থেকে সরিয়ে দিন। একই রকম fail করা একটা curl command থেকে বোঝা যায় সমস্যাটা আপনার code, configuration, key না অ্যাকাউন্টে।
- App যে body পাঠিয়েছিল হুবহু সেটা ধরুন:
model,messages,max_tokens, tools আর বাকি সবকিছুর JSON। যে customer-এর লেখা অন্যকে দেখাতে চান না, সেটা মুছে দিন। - একটা file-এ save করুন আর একই key ও একই endpoint দিয়ে পাঠান।
- Header আর body আলাদা file-এ রাখুন, যাতে id আর error দুটোই থেকে যায়।
curl -sS -D headers.txt -o response.json -w "HTTP %{http_code}\n" \
https://tokens.bd/v1/chat/completions \
-H "Authorization: Bearer $TOKENS_API_KEY" \
-H "Content-Type: application/json" \
-H "x-request-id: repro-$(date +%s)" \
-d @body.json
grep -i "^x-tokens-request-id" headers.txt
jq '.error // .' response.jsoncurl -sS -D headers.txt -o response.json -w "HTTP %{http_code}\n" \
https://tokens.bd/v1/messages \
-H "x-api-key: $TOKENS_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-H "x-request-id: repro-$(date +%s)" \
-d @body.json
grep -i "^x-tokens-request-id" headers.txt
jq '.error // .' response.jsoncurl.exe -sS -D headers.txt -o response.json -w "HTTP %{http_code}`n" `
https://tokens.bd/v1/chat/completions `
-H "Authorization: Bearer $env:TOKENS_API_KEY" `
-H "Content-Type: application/json" `
-d "@body.json"
Select-String -Path headers.txt -Pattern "x-tokens-request-id"
Get-Content response.jsonWindows PowerShell 5.1-এ curl আসলে অন্য একটা command-এর alias, তাই curl.exe লিখুন। Streaming উত্তর আসতে আসতে দেখতে চাইলে -N দিন আর body-তে "stream": true যোগ করুন।
এরপর একবারে একটা জিনিস বদলে সমস্যা ছোট করে আনুন:
| curl call যদি... | তাহলে বুঝবেন |
|---|---|
| একই রকম fail করে | সমস্যা request-এ, key-তে বা অ্যাকাউন্টে। error.code পড়ে errors পেজে খুঁজুন |
| ঠিকঠাক চলে | সমস্যা আপনার app-এ: environment-এ অন্য key, বদলে যাওয়া body, কোনো proxy, timeout, বা library-র default |
stream ছাড়া চলে, দিলে fail করে | মাঝখানে buffering করা কোনো proxy, নয়তো SSE সামলাতে না পারা client। দেখুন streaming |
| শুধু একটা নির্দিষ্ট model-এ fail করে | ওই model-এর parameter বা availability-র সমস্যা। GET /v1/models থেকে অন্য একটা চেষ্টা করুন |
| শুধু tools থাকলে বা body বড় হলে fail করে | tool schema, 10 MB body-র সীমা, বা model-এর context window |
| মাঝে মাঝে fail করে | rate limit, নয়তো provider-এর সমস্যা। Retry-After আর status page দেখুন |
Key আর base URL ঠিক আছে কি না, তার দ্রুত পরীক্ষা হলো GET /v1/models। এতে কোনো model চলে না, আর এটা আপনার per-minute limit-এও গোনা হয় না।
Error body কীভাবে পড়বেন#
Branch করুন error.code দেখে। message মানুষের জন্য, ওটা বদলে যেতে পারে। Error-এর গড়ন আর code-গুলো errors পেজে আছে। একটা error সংক্ষেপে এভাবে পড়ুন:
| অংশ | কীভাবে পড়বেন |
|---|---|
| HTTP status | শ্রেণি: 4xx মানে সমস্যা request, key বা অ্যাকাউন্টে, 5xx মানে gateway বা কোনো provider-এর |
error.code | ঠিক কারণটা। insufficient_credits, window_exhausted, model_not_found ইত্যাদি |
error.message | প্রসঙ্গ: যেমন key যেসব model ব্যবহার করতে পারে তার তালিকা, বা window কখন reset হবে |
request_id | যেটা support-কে জানাবেন |
Retry-After header | 429-এ কত সেকেন্ড অপেক্ষা করবেন |
দুটো জিনিস নিয়ে অনেকে ধাঁধায় পড়েন:
- Provider-এর error সাধারণ message হয়ে আসে। model-এর provider কোনো request reject করলে বা fail করলে, provider-এর ভেতরের তথ্য ফাঁস না হওয়ার জন্য message বদলে একটা সাধারণ message বসানো হয়। তবে status আর
codeথেকে কোন ধরনের failure তা বোঝা যায়। provider থেকে 400 এলে আপনার parameter-গুলো দেখুন, আর model সেগুলো support করে কি না দেখুন। - 200 পাওয়ার পরও stream fail করতে পারে। Streaming শুরু হয়ে গেলে status আগেই 200 হয়ে যায়। provider মাঝপথে fail করলে connection বন্ধ হয়ে যায়
data: [DONE]ছাড়াই (Messages-এmessage_stopছাড়াই), আর কোনো error body আসে না। finish reason ছাড়া শেষ হওয়া stream-কে অসম্পূর্ণ ধরুন, আর শুরুতে log করে রাখা request id কাজে লাগান।
Usage page কীভাবে পড়বেন#
Usage পেজে আপনার সাম্প্রতিক request-গুলো activity table-এ আসে, সবচেয়ে নতুনটা ওপরে:
| Column | কী দেখায় |
|---|---|
| Time | request কখন record হয়েছে |
| Usage | request-এর খরচ |
| Tokens | input · N cached · output। cached অংশ শুধু থাকলেই দেখায়, আর সেটা cache read আর write মিলিয়ে |
| Timing | request-এ কত সময় লেগেছে |
| Model | আপনি যে model id দিয়ে call করেছেন |
| Mode | /v1 দিয়ে আসা request-এর জন্য api |
| Status | বিল হওয়া request-এর জন্য COMPLETED |
| Trace ID | request id। ক্লিক করলে পুরো মান copy হয়; table-এ শুধু প্রথম আটটা অক্ষর দেখায় |
API request-এর Trace ID আর তার x-tokens-request-id একই মান। একটা request খুঁজতে হলে আপনার log থেকে id copy করে এই column-এ খুঁজুন। Table-এ page আছে, তাই পুরোনো request-এর জন্য নিচের per-page selector দিয়ে বেশি row দেখান।
এই পেজ থেকে কী জানা যায় আর কী যায় না:
- শুধু বিল হওয়া request-ই আসে। Request চলে বিল হয়ে গেলে তবেই gateway usage record করে। চলার আগেই reject হওয়া request (ভুল key, ব্যালান্স নেই, rate limit) বা provider-এ fail করা request-এর কোনো row নেই। এগুলোর ক্ষেত্রে আপনার নিজের log-এ রাখা request id আর error body-ই একমাত্র রেকর্ড।
- আপনার cancel করা request-ও থাকে। Stream-এর মাঝখানে আপনার client disconnect করলে input আর তত দূর পর্যন্ত তৈরি হওয়া output-এর বিল হয়, আর row-তে সেই সংখ্যাই দেখায়।
- Estimated badge মানে usage report আসেনি। Provider token-এর হিসাব না পাঠালে Tokens request আর উত্তরের আকার থেকে একটা হিসাব বের করে row-তে চিহ্ন দিয়ে দেয়। আপনার বিল হয় সেই হিসাব ধরেই।
- Cached token কম খরচের কারণ হতে পারে। লম্বা prompt-এও খরচ কম হলে Tokens column-এর cached অংশ দেখুন। দেখুন prompt caching।
- ছোট prompt-এ বড় খরচের কারণ সাধারণত এগুলোই: প্রতিটা turn-এ আবার পাঠানো লম্বা কথোপকথনের history, যে model ব্যবহার করে তার জন্য বড়
max_tokens, output হিসেবে গোনা reasoning token, একাধিকবার বিল হওয়া retry, বা loop থেকে আসা অনেক request। কোনটা, তা প্রতিটা request-এর token দেখলেই বোঝা যায়।
মোট হিসাব, দৈনিক chart আর plan window-গুলো ব্যাখ্যা করা আছে usage and alerts পেজে। Code থেকে দেখে নিতে চাইলে GET /v1/tokens/usage আপনার window, ব্যালান্স আর key-এর cap ফেরত দেয়, এর জন্য কোনো বিল হয় না।
Debugging-এর ছোট একটা রুটিন#
error.codeআর status পড়ুন। code-টা errors পেজে খুঁজুন।- Response থেকে, বা আপনার log থেকে
x-tokens-request-idনিন। - সেভ করা body দিয়ে curl-এ আবার চালান। একবারে একটা জিনিস বদলান।
- একসাথে কয়েকটা request বা model fail করলে status দেখুন।
- প্রশ্নটা খরচ বা token নিয়ে হলে Usage দেখুন।
- তারপরও ব্যাখ্যা না মিললে id, সময়, model, status আর code দিয়ে ticket খুলুন।