# Rate Limits

> মিনিটে request, concurrency, plan-এর usage window আর key-ভিত্তিক মাসিক spend cap: প্রতিটা limit কীভাবে কাজ করে, কী error আসে, আর ঠিকভাবে backoff করার উপায়।

model-এর কাছে পৌঁছানোর আগেই চার ধরনের limit আপনার request আটকে দিতে পারে: মিনিটে request, একসাথে চলা request, plan-এর usage window আর key-র মাসিক spend cap। এর ওপর আবার upstream provider-দের নিজস্ব rate limit আছে। এই পেজে প্রতিটার কাজ, কী error আসে আর client-এর কী করা উচিত, সবই পাবেন।

## এক নজরে rate limit

| Limit                   | কোথায় খাটে                 | Default                                 | Error                            | `Retry-After`                           |
| ----------------------- | --------------------------- | --------------------------------------- | -------------------------------- | --------------------------------------- |
| মিনিটে request          | অ্যাকাউন্ট (সব key মিলিয়ে) | 60 RPM, বা আপনার plan-এর মান            | 429 `rate_limited`               | পরের মিনিট শুরু হতে যত সেকেন্ড বাকি     |
| একসাথে চলা request      | অ্যাকাউন্ট                  | plan-এর limit; plan থাকলে 10, না থাকলে 3 | 429 `concurrency_limit`          | 2                                       |
| Usage window            | সাবস্ক্রিপশন                | plan ঠিক করে দেয়                       | 429 `window_exhausted`           | window reset হতে যত সেকেন্ড বাকি        |
| মাসিক spend cap         | একটা key                    | key বানানোর সময় না দিলে নেই             | 403 `monthly_spend_cap_exceeded` | পাঠানো হয় না                           |
| Upstream provider-এর limit | Provider                 | provider ঠিক করে                        | 429 `rate_limit_exceeded`        | provider পাঠালে সেটাই সরাসরি দেওয়া হয়  |

আপনার plan-এর ঠিক সংখ্যাগুলো [billing](/dashboard/billing)-এ দেখা যায়, আর [plans and wallet](/docs/plans-and-wallet) পেজে বোঝানো আছে।

## মিনিটে request

মিনিটের limit প্রতি অ্যাকাউন্টের request গোনে, ঘড়ির মিনিট ধরে ধরে এক মিনিটের নির্দিষ্ট bucket-এ। অ্যাকাউন্টের সব key একই bucket ভাগ করে নেয়, তাই বেশি key বানালে limit বাড়ে না। Dashboard-এর playground-এর আলাদা limit আছে, 10 RPM, আর এটা আপনার API বরাদ্দ খরচ করে না।

limit পার হলে পান 429 `rate_limited`। `Retry-After`-এ থাকে চলতি মিনিটের বাকি সেকেন্ড, তাই অপেক্ষা কখনো 60 সেকেন্ডের বেশি হয় না। error message-এ "this key" লেখা থাকে, কিন্তু limit আসলে অ্যাকাউন্টের।

টাকার অভাবে (402 `insufficient_credits` বা `no_funding`) কিংবা usage window শেষ হওয়ায় ফেরানো request-ও ওই মিনিটের হিসাবে গোনা হয়। তাই 402 পেয়ে tight loop-এ retry করলে per-minute limitও ধরবে। 402 কখনোই retry করবেন না।

`GET /v1/models` আর `GET /v1/tokens/usage` গোনা হয় না।

## Concurrency limit

Concurrency মানে আপনার অ্যাকাউন্টে একই সময়ে চলতে থাকা request-এর সংখ্যা। streaming request admission থেকে stream শেষ হওয়া পর্যন্ত একটা slot দখল করে রাখে। তাই যে coding agent একসাথে কয়েকটা stream খোলে, বা যে script `asyncio.gather` দিয়ে অনেকগুলো request ছড়িয়ে দেয়, সে মিনিটের limit-এর আগেই এখানে আটকে যেতে পারে।

429 `concurrency_limit` response-এ আসে `Retry-After: 2`। এর সমাধান সাধারণত আরও জোরে retry করা নয়, নিজের দিকে parallelism সীমিত করা, যেমন plan-এর limit-এর চেয়ে কম মানের একটা semaphore বসানো। যে stream আর লাগবে না সেটা close বা abort করুন। ফেলে রাখা stream শেষ না হওয়া পর্যন্ত তার slot ধরে রাখে।

## Usage window

সাবস্ক্রিপশন plan-এ usage window থাকতে পারে। প্রতিটা window-র সীমা credit-এ (USD-তে মাপা) অথবা request-এর সংখ্যায়:

| Window         | API-তে `type`     | কখন reset হয়                                    |
| -------------- | ----------------- | ------------------------------------------------ |
| 5-hour session | `session_5h`      | যে request window খুলেছিল তার 5 ঘণ্টা পর          |
| Weekly         | `weekly`          | প্রতি সোমবার 00:00 UTC-তে                         |
| Monthly        | `monthly`         | সাবস্ক্রিপশন period-এর শেষে                       |

যেকোনো একটা window শেষ হয়ে গেলে request-এ আসে 429 `window_exhausted`, আর `Retry-After` হলো ওই window reset হতে বাকি সেকেন্ড। এটা কয়েক ঘণ্টাও হতে পারে, তাই নিজে নিজে retry করাবেন না। user-কে reset-এর সময়টা দেখান, নয়তো কাজটা থামিয়ে দিন। লম্বা agent চালানোর আগে কতটা বাকি আছে দেখে নিন:

```bash
curl -s https://tokens.bd/v1/tokens/usage -H "Authorization: Bearer $TOKENS_API_KEY"
```

response-এর গড়ন [models and usage](/docs/models-and-usage) পেজে আছে। 50, 75, 90 আর 100 শতাংশে alert কীভাবে আসে, সেটা [usage and alerts](/docs/usage-and-alerts) পেজে পাবেন।

## Key-ভিত্তিক মাসিক spend cap

একটা key-র USD-তে মাসিক spend cap থাকতে পারে, যেটা key বানানোর সময়ই ঠিক করতে হয়। খরচ গোনা হয় ক্যালেন্ডার মাস (UTC) ধরে। প্রতিটা request-এর আগে gateway দেখে key-র এ পর্যন্ত খরচ, তার সাথে নতুন request-এর সবচেয়ে খারাপ ক্ষেত্রের খরচ যোগ করে। এই হিসাব হয় request-এর `max_tokens` ধরে, দেওয়া না থাকলে 8,192 output token ধরে। তাই cap-এর কাছাকাছি গেলে বড় `max_tokens`-ওয়ালা request ফিরিয়ে দেওয়া হতে পারে, অথচ ছোটটা পার হয়ে যায়।

error আসে 403 `monthly_spend_cap_exceeded`, 429 নয়, কারণ কয়েক সেকেন্ড অপেক্ষা করলে কিছু বদলায় না। বানানোর পর cap edit করা যায় না। বেশি দরকার হলে নতুন key বানাতে হবে। যে key shared CI system-এ বা আপনি নজর রাখেন না এমন agent-এ যাবে, তাতে একটা মাত্র setting দিলে সেটা হোক spend cap। আরও জানতে [API keys](/docs/api-keys) দেখুন।

## Retry-After আর rate limit header

`X-RateLimit-Limit` বা `X-RateLimit-Remaining` header নেই। rate limit-এর একমাত্র header হলো `Retry-After`, সেকেন্ডে, যা 429 response-এ আসে। আগে থেকে দেখতে চান কতটা বাকি আছে? `GET /v1/tokens/usage` poll করুন।

## Client-side backoff-এর উদাহরণ

OpenAI আর Anthropic SDK কিছু 429 আর 5xx error নিজে নিজেই retry করে (`max_retries`, default 2)। নিচের উদাহরণে সেটা বন্ধ করে নিজের হাতে সামলানো হয়েছে, যাতে `window_exhausted` retry না হয় আর `Retry-After` মানা হয়।

:::code-tabs

```python title="Python"
import os
import random
import time

import openai
from openai import OpenAI

client = OpenAI(
    base_url="https://tokens.bd/v1",
    api_key=os.environ["TOKENS_API_KEY"],
    max_retries=0,
)

RETRYABLE = {429, 500, 502, 503, 504}


def create_with_backoff(max_attempts: int = 5, **kwargs):
    delay = 1.0
    for attempt in range(1, max_attempts + 1):
        try:
            return client.chat.completions.create(**kwargs)
        except openai.APIStatusError as e:
            if (
                e.status_code not in RETRYABLE
                or e.code == "window_exhausted"
                or attempt == max_attempts
            ):
                raise
            try:
                wait = float(e.response.headers.get("retry-after", delay))
            except ValueError:
                wait = delay
            request_id = e.response.headers.get("x-tokens-request-id")
            print(f"{e.status_code} {e.code} (request {request_id}), retrying in {wait:.1f}s")
        except openai.APIConnectionError:
            if attempt == max_attempts:
                raise
            wait = delay
        time.sleep(min(wait, 60) + random.uniform(0, 0.5 * delay))
        delay = min(delay * 2, 30)


resp = create_with_backoff(
    model="deepseek/deepseek-v4.1-flash",
    messages=[{"role": "user", "content": "Say hello."}],
    max_tokens=50,
)
print(resp.choices[0].message.content)
```

```typescript title="Node.js"
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://tokens.bd/v1",
  apiKey: process.env.TOKENS_API_KEY,
  maxRetries: 0,
});

const RETRYABLE = new Set([429, 500, 502, 503, 504]);
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));

function retryAfterMs(err: InstanceType<typeof OpenAI.APIError>): number | undefined {
  const h: unknown = err.headers; // a Headers object or a plain record, depending on SDK version
  const value =
    h instanceof Headers
      ? h.get("retry-after")
      : (h as Record<string, string | undefined> | undefined)?.["retry-after"];
  return value ? Number(value) * 1000 : undefined;
}

async function createWithBackoff(
  body: OpenAI.Chat.ChatCompletionCreateParamsNonStreaming,
  maxAttempts = 5
) {
  let delay = 1000;
  for (let attempt = 1; ; attempt++) {
    try {
      return await client.chat.completions.create(body);
    } catch (err) {
      if (!(err instanceof OpenAI.APIError) || attempt >= maxAttempts) throw err;
      const connectionError = err instanceof OpenAI.APIConnectionError;
      if (
        !connectionError &&
        (!RETRYABLE.has(err.status ?? 0) || err.code === "window_exhausted")
      ) {
        throw err;
      }
      const wait = (!connectionError && retryAfterMs(err)) || delay;
      await sleep(Math.min(wait, 60_000) + Math.random() * 0.5 * delay);
      delay = Math.min(delay * 2, 30_000);
    }
  }
}

const resp = await createWithBackoff({
  model: "deepseek/deepseek-v4.1-flash",
  messages: [{ role: "user", content: "Say hello." }],
  max_tokens: 50,
});
console.log(resp.choices[0].message.content);
```

:::

সফল হওয়া প্রতিটা retry-ও একটা billed request, তাই চেষ্টার সংখ্যা কম রাখুন। কোন code retry করা যাবে আর কোনটা যাবে না, তার পুরো তালিকা [errors](/docs/errors) পেজে আছে।

---
Page: https://tokens.bd/bn/docs/rate-limits
