# PHP

> PHP আর Laravel থেকে Tokens ব্যবহার করুন: openai-php/client package, openai-php/laravel package আর সাধারণ cURL। Base URL, প্রথম call, streaming, tool call, timeout আর Tokens-এর error code।

Tokens OpenAI Chat Completions protocol বোঝে, তাই যে PHP code HTTP request পাঠাতে পারে, সেটা দিয়েই Tokens চলে। এই পেজে তিনটা উপায় আছে: সাধারণ PHP-তে community package `openai-php/client`, Laravel app-এর ভেতরে `openai-php/laravel`, আর কোনো package ছাড়া raw cURL। Laravel-এর নিজের agent package নিয়ে জানতে দেখুন [Laravel AI SDK](/docs/laravel-ai-sdk)।

:::note[কীসের সঙ্গে মিলিয়ে দেখা হয়েছে]
`openai-php/client` আর `openai-php/laravel`, দুটোর version 0.21.0 (released 17 September 2026, checked October 2026)-এর README ও source, আর PHP ও Guzzle-এর documentation দেখে এই পেজ লেখা। Code-গুলো documentation আর source-এর সঙ্গে মিলিয়ে দেখা হয়েছে, Tokens-এর বিরুদ্ধে শুরু থেকে শেষ পর্যন্ত চালিয়ে দেখা হয়নি। এই package-গুলো community চালায়, OpenAI বা Tokens publish করে না।
:::

## যা লাগবে

- PHP 8.2 বা তার পরের version (দুটো package-ই `php ^8.2` চায়) আর [Composer](https://getcomposer.org/)।
- [API keys](/docs/api-keys) থেকে নেওয়া একটা Tokens key, `TOKENS_API_KEY` নামে export করা।
- [/models](/models) থেকে একটা model id।

সব উদাহরণে base URL হলো `https://tokens.bd/v1`। এতে `/v1` আছে। `openai-php/client`-এ এটা `withBaseUri()`-তে হুবহু এভাবেই দিন: library নিজে একটা `/` আর তার পরে resource path জুড়ে নেয়, ফলে request যায় `https://tokens.bd/v1/chat/completions`-এ। শেষে নিজে slash বা `/chat/completions` বসাবেন না। পুরো URL `https://` সমেত লিখুন। scheme বাদ দিলে library নিজে `https://` বসিয়ে দেয়।

## openai-php/client

### Install

```bash
composer require openai-php/client guzzlehttp/guzzle
export TOKENS_API_KEY="tok_live_your_key"
```

Package-টার একটা PSR-18 HTTP client লাগে। README বলছে, হয় `php-http/discovery` Composer plugin-কে অনুমতি দিন, নয়তো Guzzle-এর মতো একটা client নিজে install করুন। উপরের মতো Guzzle install করাই সবচেয়ে সোজা।

### Client বানান আর call করুন

```php title="hello.php"
<?php

declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

$client = OpenAI::factory()
    ->withApiKey((string) getenv('TOKENS_API_KEY'))
    ->withBaseUri('https://tokens.bd/v1')
    ->withHttpClient(new GuzzleHttp\Client([
        'connect_timeout' => 10,
        'timeout' => 300,
    ]))
    ->make();

$response = $client->chat()->create([
    'model' => 'deepseek/deepseek-v4.1-flash',
    'messages' => [
        ['role' => 'system', 'content' => 'Answer in one short paragraph.'],
        ['role' => 'user', 'content' => 'When should I use a queue instead of running code in the request?'],
    ],
    'max_tokens' => 400,
]);

echo $response->choices[0]->message->content, PHP_EOL;
echo "{$response->usage->promptTokens} in, {$response->usage->completionTokens} out", PHP_EOL;
```

```bash
php hello.php
```

`OpenAI::factory()`, `withApiKey()`, `withBaseUri()`, `withHttpClient()` আর `make()` README-তে দেওয়া factory method। আলাদা কোনো header লাগলে `withHttpHeader()` প্রতিটা request-এ সেটা জুড়ে দেয়। Tokens-এর জন্য `OpenAI::client($key)` ব্যবহার করবেন না: এতে base URI দেওয়ার জায়গাই নেই, ফলে আপনার key OpenAI-র কাছে চলে যাবে।

### Model বেছে নিন

`model`-এ Tokens-এর id দিন, [/models](/models)-এ যেভাবে লেখা আছে ঠিক সেভাবে, যেমন `deepseek/deepseek-v4.1-flash`। Code থেকে id যাচাই করতে চাইলে:

```php
foreach ($client->models()->list()->data as $model) {
    echo $model->id, PHP_EOL;
}
```

id ভুল হলে `404 model_not_found` আসে।

### Response stream করুন

`createStreamed()` call করে result-এর ওপর loop চালান। Usage চেয়ে নিতে ভুলবেন না, নইলে stream-এ token-এর হিসেব আসে না:

```php
$stream = $client->chat()->createStreamed([
    'model' => 'deepseek/deepseek-v4.1-flash',
    'messages' => [
        ['role' => 'user', 'content' => 'Write a haiku about merge conflicts.'],
    ],
    'stream_options' => ['include_usage' => true],
]);

foreach ($stream as $chunk) {
    $text = $chunk->choices[0]->delta->content ?? null;
    if ($text !== null) {
        echo $text;
        flush();
    }

    if ($chunk->usage !== null) {
        echo PHP_EOL, "{$chunk->usage->promptTokens} in, {$chunk->usage->completionTokens} out", PHP_EOL;
    }
}
```

`include_usage` চালু থাকলে শেষ chunk-এ `choices` list ফাঁকা থাকে আর শুধু `usage` থাকে। `?? null` সেই কেসটাই সামলায়। বাকি সব chunk-এ `usage` হলো `null`। আরও দেখুন [Streaming](/docs/streaming)।

Stream চলার মাঝে key বা কোনো limit-এ সমস্যা হলে library `foreach`-এর ভেতর থেকেই `OpenAI\Exceptions\ErrorException` throw করে। তাই loop-টা আপনার `try` block-এর ভেতরে রাখুন।

### Tool call

যে model tool calling সাপোর্ট করে, শুধু সেখানেই এটা চলে। আগে [/models](/models)-এ model-এর পেজ দেখে নিন।

```php
$tools = [[
    'type' => 'function',
    'function' => [
        'name' => 'get_weather',
        'description' => 'Current weather for a city',
        'parameters' => [
            'type' => 'object',
            'properties' => ['city' => ['type' => 'string']],
            'required' => ['city'],
        ],
    ],
]];

$messages = [['role' => 'user', 'content' => 'Is it raining in Dhaka?']];

$first = $client->chat()->create([
    'model' => 'deepseek/deepseek-v4.1-flash',
    'messages' => $messages,
    'tools' => $tools,
]);

$message = $first->choices[0]->message;

if ($message->toolCalls !== []) {
    $messages[] = [
        'role' => 'assistant',
        'content' => $message->content,
        'tool_calls' => array_map(fn ($call) => [
            'id' => $call->id,
            'type' => 'function',
            'function' => ['name' => $call->function->name, 'arguments' => $call->function->arguments],
        ], $message->toolCalls),
    ];

    foreach ($message->toolCalls as $call) {
        $args = json_decode($call->function->arguments, true);
        $result = ['city' => $args['city'], 'condition' => 'light rain', 'temp_c' => 29]; // your real lookup here
        $messages[] = [
            'role' => 'tool',
            'tool_call_id' => $call->id,
            'content' => json_encode($result),
        ];
    }

    $final = $client->chat()->create([
        'model' => 'deepseek/deepseek-v4.1-flash',
        'messages' => $messages,
        'tools' => $tools,
    ]);

    echo $final->choices[0]->message->content, PHP_EOL;
}
```

Request আর response-এর গড়ন আছে [Tool calling](/docs/tool-calling) পেজে।

### লম্বা request-এর timeout

README বলছে, default timeout নির্ভর করে আপনি কোন HTTP client ব্যবহার করছেন তার ওপর। বাড়াতে হলে একটা configure করা client `withHttpClient()`-এ দিন। Guzzle-এর ক্ষেত্রে:

| Option            | মানে (Guzzle docs অনুযায়ী)                                                                                  |
| ----------------- | ------------------------------------------------------------------------------------------------------------ |
| `connect_timeout` | Connect করার জন্য কত সেকেন্ড অপেক্ষা করবে। Default 0, মানে অনন্তকাল অপেক্ষা।                                 |
| `timeout`         | পুরো request-এর মোট সময়, সেকেন্ডে। Default 0, মানে অনন্তকাল অপেক্ষা।                                        |
| `read_timeout`    | Stream করা body-র প্রতিটা read-এর timeout। Default হলো `default_socket_timeout` ini setting-এর মান।          |

Non-streamed সবচেয়ে লম্বা উত্তরের চেয়ে `timeout` বেশি রাখুন, প্রথম উদাহরণে যেমন আছে। Stream-এর বেলায় মোট `timeout` আপনার stream পড়ার সময়টাও গোনে, তাই লম্বা উত্তর মাঝপথে কেটে যেতে পারে। Stream-এর জন্য এমন client নিন যাতে মোট সময়ের সীমা নেই, আর ভরসা `read_timeout`-এর ওপর:

```php
$streamClient = OpenAI::factory()
    ->withApiKey((string) getenv('TOKENS_API_KEY'))
    ->withBaseUri('https://tokens.bd/v1')
    ->withHttpClient(new GuzzleHttp\Client([
        'connect_timeout' => 10,
        'timeout' => 0,
        'read_timeout' => 120,
    ]))
    ->make();
```

Tokens-এর দিকে, gateway upstream থেকে response header আসার জন্য সর্বোচ্চ 600 সেকেন্ড অপেক্ষা করে। Streaming করলে এত সময় চুপচাপ একটা connection ধরে বসে থাকতে হয় না।

আপনার client-এর আগেই PHP নিজেও লম্বা request থামিয়ে দিতে পারে। `php.ini`-র web server setting `max_execution_time` web request-এর জন্য default 30 সেকেন্ড (command line-এ কোনো সীমা নেই)। Web app-এ লম্বা generation হলে উত্তর stream করুন, নয়তো call-টা একটা queue job-এ চালান।

### Error সামলান

Library Tokens-এর OpenAI-ধাঁচের error body পড়ে নেয়, তাই `ErrorException`-এ Tokens-এর `code` পাওয়া যায়:

```php
use OpenAI\Exceptions\ErrorException;
use OpenAI\Exceptions\TransporterException;

try {
    $client->chat()->create([
        'model' => 'deepseek/deepseek-v4.1-flash',
        'messages' => [['role' => 'user', 'content' => 'hi']],
    ]);
} catch (ErrorException $e) {
    $requestId = $e->response->getHeaderLine('x-tokens-request-id');
    error_log(sprintf('%d %s %s request=%s', $e->getStatusCode(), $e->getErrorCode(), $e->getErrorMessage(), $requestId));

    if ($e->getErrorCode() === 'insufficient_credits') {
        // top up at https://tokens.bd/dashboard/billing
    }
} catch (TransporterException $e) {
    error_log('Network problem, timeout or server error: ' . $e->getMessage());
}
```

Library-র source থেকে, HTTP client Guzzle হলে: JSON error body সহ যেকোনো 4xx response (401, 402, 403, 404 আর 429-ও) `ErrorException` হয়ে আসে। Network failure, timeout আর 5xx response আসে `TransporterException` হয়ে, আর Guzzle-এর exception থাকে `$e->getPrevious()`-এ, response এসে থাকলে তাতে সেটাও থাকে। যেসব HTTP client error status-এ exception ছোড়ে না, তাদের জন্য library `RateLimitException` আর `ServerException`-ও বানিয়ে রেখেছে, দুটোতেই একটা public `$response` আছে।

প্রতিটা failure-এর সঙ্গে `x-tokens-request-id` log করুন, তাহলে [support](/docs/support) আপনার request খুঁজে বের করতে পারবে। সচরাচর যেসব code পাবেন: `invalid_api_key` (401), `model_not_allowed_on_key` আর `tier_permission_denied` (403), `insufficient_credits` (402), আর `rate_limited`, `concurrency_limit` ও `window_exhausted` (429)। পুরো তালিকা [Errors](/docs/errors) পেজে।

## openai-php/laravel

Laravel package-টা একই client-কে একটা service provider আর `OpenAI` facade-এর ভেতরে মুড়ে দেয়। এটা চায় PHP 8.2+ আর Laravel `^11.29`, `^12.12` বা `^13.0`।

### Install

```bash
composer require openai-php/laravel
php artisan openai:install
```

দ্বিতীয় command `config/openai.php` বানায় আর `.env`-এর শেষে ফাঁকা `OPENAI_API_KEY` ও `OPENAI_ORGANIZATION` লাইন জুড়ে দেয়। Tokens-এ organization লাগে না, তাই `OPENAI_ORGANIZATION` লাইনটা মুছে দিন।

### Configure

README-তে এই variable-গুলো আছে:

```ini title=".env"
OPENAI_API_KEY=tok_live_your_key
OPENAI_BASE_URL=https://tokens.bd/v1
OPENAI_REQUEST_TIMEOUT=300
```

| Variable                 | Config key        | নোট                                                                                  |
| ------------------------ | ----------------- | ------------------------------------------------------------------------------------ |
| `OPENAI_API_KEY`         | `api_key`         | আপনার Tokens key।                                                                    |
| `OPENAI_BASE_URL`        | `base_uri`        | Default `api.openai.com/v1`। `https://tokens.bd/v1` দিন, `/v1` সমেত।                  |
| `OPENAI_REQUEST_TIMEOUT` | `request_timeout` | সেকেন্ডে, default 30। লম্বা উত্তরের জন্য বাড়ান।                                      |

:::warning[অন্য কিছু OPENAI_API_KEY পড়লে Tokens-এর আলাদা নাম নিন]
`OPENAI_API_KEY` নামটা অনেক package-ই পড়ে। যেমন `laravel/ai`-এর built-in `openai` provider এটা পড়ে আর default-এ OpenAI-র কাছে পাঠায়। ওখানে Tokens key বসালে অন্য কোনো package সেটা ভুল জায়গায় পাঠিয়ে দিতে পারে। নিরাপদ উপায়: `config/openai.php` বদলে নিজের নামগুলো পড়ান, আর `OPENAI_*` unset রাখুন।
:::

```php title="config/openai.php"
return [
    'api_key' => env('TOKENS_API_KEY'),
    'base_uri' => env('TOKENS_BASE_URL', 'https://tokens.bd/v1'),
    'request_timeout' => env('TOKENS_REQUEST_TIMEOUT', 300),
];
```

Publish করা file-এ `organization` আর `project`-ও আছে। ও দুটো বাদ দিন, নয়তো `null` রাখুন। যে server config cache করে, সেখানে `.env` বা config বদলানোর পর `php artisan config:clear` চালান।

### Call করুন

```php
use OpenAI\Laravel\Facades\OpenAI;

$response = OpenAI::chat()->create([
    'model' => 'deepseek/deepseek-v4.1-flash',
    'messages' => [
        ['role' => 'user', 'content' => 'Explain job batching in Laravel in two sentences.'],
    ],
]);

echo $response->choices[0]->message->content;
```

Facade-এ ওপরের client-এর মতোই `chat()`, `models()` আর বাকি resource আছে, তাই streaming, tool call আর error handling আগের section-এর মতোই। README-র নিজের উদাহরণে `OpenAI::responses()` আছে। Tokens `/v1/responses`-ও চালায় ([Responses](/docs/responses)), কিন্তু এই পেজে যাচাই করা হয়েছে উপরের Chat Completions রূপটাই।

### Route থেকে stream করুন

```php title="routes/web.php"
use Illuminate\Support\Facades\Route;
use OpenAI\Laravel\Facades\OpenAI;

Route::get('/ask', function () {
    return response()->stream(function () {
        $stream = OpenAI::chat()->createStreamed([
            'model' => 'deepseek/deepseek-v4.1-flash',
            'messages' => [['role' => 'user', 'content' => 'Explain queues in Laravel.']],
        ]);

        foreach ($stream as $chunk) {
            $text = $chunk->choices[0]->delta->content ?? null;
            if ($text !== null) {
                echo $text;
                if (ob_get_level() > 0) {
                    ob_flush();
                }
                flush();
            }
        }
    }, 200, ['Content-Type' => 'text/plain; charset=utf-8', 'Cache-Control' => 'no-cache']);
});
```

### Laravel-এ timeout

Package নিজের Guzzle client বানায় `request_timeout`-কে Guzzle-এর `timeout` option হিসেবে দিয়ে। এটা মোট সময়ের সীমা, তাই stream করা উত্তরও এর মধ্যে পড়ে। আপনার সবচেয়ে লম্বা উত্তরের চেয়ে এটা বেশি রাখুন। যদি Guzzle-এর `read_timeout` দরকার হয়, তাহলে factory দিয়ে client বানান (আগের section) আর নিজের service provider-এ package-টার client-এর জায়গায় সেটা register করুন।

## Raw cURL

কোনো Composer package লাগে না। এটা PHP-র `curl` extension ব্যবহার করে।

```php title="curl-hello.php"
<?php

declare(strict_types=1);

$ch = curl_init('https://tokens.bd/v1/chat/completions');
$requestId = '';

curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('TOKENS_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'model' => 'deepseek/deepseek-v4.1-flash',
        'messages' => [['role' => 'user', 'content' => 'Say hello in five words.']],
        'max_tokens' => 100,
    ], JSON_THROW_ON_ERROR),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 300,
    CURLOPT_HEADERFUNCTION => function ($ch, string $header) use (&$requestId): int {
        if (stripos($header, 'x-tokens-request-id:') === 0) {
            $requestId = trim(substr($header, strlen('x-tokens-request-id:')));
        }
        return strlen($header);
    },
]);

$body = curl_exec($ch);

if ($body === false) {
    fwrite(STDERR, 'cURL error ' . curl_errno($ch) . ': ' . curl_error($ch) . PHP_EOL);
    exit(1);
}

$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

$data = json_decode($body, true, flags: JSON_THROW_ON_ERROR);

if ($status >= 400) {
    $error = $data['error'] ?? [];
    fwrite(STDERR, sprintf("%d %s %s request=%s\n", $status, $error['code'] ?? '', $error['message'] ?? '', $requestId));
    exit(1);
}

echo $data['choices'][0]['message']['content'], PHP_EOL;
```

`CURLOPT_TIMEOUT` হলো request-এর জন্য মোট অনুমোদিত সময়, আর `0` (default) মানে কোনো সীমা নেই। `CURLOPT_CONNECTTIMEOUT` শুধু connection করার সময়টা ধরে।

Stream করতে হলে `"stream": true` পাঠান আর `CURLOPT_WRITEFUNCTION` দিয়ে body আসার সঙ্গে সঙ্গে পড়ুন। Body-টা `data: {...}` লাইনের একটা সারি, শেষে থাকে `data: [DONE]`:

```php title="curl-stream.php"
<?php

declare(strict_types=1);

$buffer = '';
$errorBody = '';

$ch = curl_init('https://tokens.bd/v1/chat/completions');

curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('TOKENS_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'model' => 'deepseek/deepseek-v4.1-flash',
        'messages' => [['role' => 'user', 'content' => 'Write a haiku about merge conflicts.']],
        'stream' => true,
        'stream_options' => ['include_usage' => true],
    ], JSON_THROW_ON_ERROR),
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 0, // no total limit; streams can be long
    CURLOPT_WRITEFUNCTION => function ($ch, string $data) use (&$buffer, &$errorBody): int {
        if (curl_getinfo($ch, CURLINFO_RESPONSE_CODE) >= 400) {
            $errorBody .= $data; // an error comes back as plain JSON, not as a stream
            return strlen($data);
        }

        $buffer .= $data;
        while (($pos = strpos($buffer, "\n")) !== false) {
            $line = trim(substr($buffer, 0, $pos));
            $buffer = substr($buffer, $pos + 1);

            if (!str_starts_with($line, 'data:')) {
                continue;
            }
            $payload = trim(substr($line, 5));
            if ($payload === '[DONE]') {
                continue;
            }

            $chunk = json_decode($payload, true);
            $text = $chunk['choices'][0]['delta']['content'] ?? '';
            if ($text !== '') {
                echo $text;
                flush();
            }
        }

        return strlen($data);
    },
]);

$ok = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

if ($ok === false) {
    fwrite(STDERR, 'cURL error ' . curl_errno($ch) . ': ' . curl_error($ch) . PHP_EOL);
    exit(1);
}
if ($status >= 400) {
    fwrite(STDERR, "HTTP $status: $errorBody" . PHP_EOL);
    exit(1);
}
curl_close($ch);
echo PHP_EOL;
```

Write function-কে যত byte পেয়েছে ঠিক তত return করতে হবে, নইলে cURL transfer বাতিল করে দেয়। `choices[0]` পড়তে `??` লাগে, কারণ `include_usage` চালু থাকলে শেষ chunk-এ কোনো choice থাকে না।

## ঠিকঠাক চলছে কি না দেখুন

ওপরের যেকোনো program চালান। ছোট একটা উত্তর print হলে বুঝবেন key, URL আর model id তিনটাই ঠিক আছে। PHP ছাড়াই দেখতে চাইলে:

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

## সীমাবদ্ধতা

- `openai-php/client` শুধু OpenAI API-র মডেল জানে। Tokens নিজে যা যোগ করেছে (যেমন [Models and usage](/docs/models-and-usage)-এ বলা usage endpoint), তার জন্য package-এ কোনো method নেই। সেটা cURL বা Guzzle দিয়ে call করুন।
- Tokens প্রতি অ্যাকাউন্টে মিনিটপ্রতি request আর একসাথে চলা request সীমিত রাখে। একসাথে অনেক job চালানো queue worker pool `429 concurrency_limit` খেতে পারে। দেখুন [Rate limits](/docs/rate-limits)।
- Browser-এর JavaScript থেকে Tokens call করবেন না। Tokens কোনো CORS header পাঠায় না, আর key-ও সবার চোখে পড়ে যাবে। Call-টা PHP থেকে করে result ফেরত দিন।
- PHP-FPM আর web server output buffer করে রাখে। Browser-এ stream করতে `ob_flush()`, `flush()` লাগতে পারে, আর nginx-এ ওই route-এর response buffering বন্ধ করতে হতে পারে।

## সমস্যা হলে

| লক্ষণ                                                                                                           | কারণ ও সমাধান                                                                                                                                                                                                                                   |
| --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `404 unsupported_endpoint`                                                                                      | Base URI ভুল। `https://tokens.bd/v1` দিন, `/v1` সমেত, শেষে `/chat/completions` ছাড়া।                                                                                                                                                             |
| `404 model_not_found`                                                                                           | Model id ভুল। [/models](/models) থেকে copy করুন।                                                                                                                                                                                                |
| `401 missing_api_key` বা `invalid_api_key`                                                                      | Key ফাঁকা ছিল বা ভুল। PHP process-এ variable set না থাকলে `getenv()` `false` দেয়। PHP-FPM অনেক সময় shell variable দেখতে পায় না, তাই `.env`, config file বা `clear_env = no` ব্যবহার করুন। Laravel-এ `php artisan config:clear` চালান। |
| `To use stream requests you must provide an stream handler closure via the OpenAI factory` (exception message) | আপনি এমন custom HTTP client দিয়েছেন যা Guzzle বা Symfony নয়। Guzzle নিন, অথবা README-তে যেমন বলা আছে `withStreamHandler()` জুড়ুন।                                                                                                              |
| `cURL error 28: Operation timed out` অথবা অনেকক্ষণ অপেক্ষার পর `TransporterException`                           | আপনার নিজের timeout response আসার সময়ের চেয়ে ছোট। `timeout` বাড়ান, নয়তো stream করুন।                                                                                                                                                       |
| Stream-এর মাঝপথে পেজ থেমে যায়                                                                                  | মোট timeout বা `max_execution_time` request থামিয়ে দিয়েছে। দুটোই বাড়ান।                                                                                                                                                                      |
| `402 insufficient_credits`                                                                                      | [billing](/dashboard/billing)-এ টাকা যোগ করুন।                                                                                                                                                                                                  |
| `429 rate_limited` বা `concurrency_limit`                                                                       | `Retry-After`-এ যত সেকেন্ড বলা আছে অপেক্ষা করুন, নয়তো একসাথে কম request চালান। `window_exhausted` মানে plan-এর window শেষ; reset না হওয়া পর্যন্ত retry করে লাভ নেই।                                                                           |

Tokens-এর সব error code আছে [Errors](/docs/errors) পেজে, আর প্রতিটার সমাধান [Troubleshooting](/docs/troubleshooting)-এ।

:::warning
Key রাখুন environment variable বা secrets manager-এ, কখনো commit করা file-এ নয়। Leak হলে [/dashboard/keys](/dashboard/keys)-এ গিয়ে rotate করুন। পুরোনো secret সঙ্গে সঙ্গে অকেজো হয়ে যায়।
:::

---
Page: https://tokens.bd/bn/docs/php
