API и SDKИнструкция
Стриминг ответов в Node.js через OpenAI SDK
Как получать ответ чата по токенам в Node.js: цикл for await, остановка по запросу, обработка ошибок и usage в OpenAI SDK.

Содержание
Стриминг позволяет показывать ответ по мере генерации, а не ждать весь текст. В этом руководстве вы напишете небольшой скрипт на Node.js: он получает ответ чата от API CheapTokens по токенам, останавливается по запросу и обрабатывает ошибки. Версия для Python — в статье Стриминг ответов в Python.
Что понадобится#
- Node.js 22 или новее (минимальная версия из README пакета
openai, проверено 22 октября 2026 года). - API-ключ CheapTokens. Если его ещё нет, пройдите Первый запрос к API CheapTokens.
- Официальный SDK:
npm install openai.
У CheapTokens OpenAI-совместимый API по адресу https:/, поэтому официальный пакет openai работает после замены только базового URL и ключа. Храните ключ в переменной окружения, а не в коде:
export CHEAPTOKENS_API_KEY="your-key"Шаг 1. Стримим ответ через for await#
Создайте файл stream.mjs (расширение .mjs позволяет использовать import и await на верхнем уровне):
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: process.env.CHEAPTOKENS_API_KEY,
baseURL: 'https://api.cheaptokens.dev/v1',
});
const stream = await client.chat.completions.create({
model: 'deepseek-ai/DeepSeek-V4-Flash-0731', // take the id from the CheapTokens docs
messages: [{ role: 'user', content: 'Explain streaming in two sentences.' }],
stream: true,
stream_options: { include_usage: true },
});
let usage = null;
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content;
if (delta) process.stdout.write(delta);
if (chunk.usage) usage = chunk.usage;
}
console.log('\n\nusage:', usage);Запустите: node stream.mjs. SDK читает Server-Sent Events от API и отдаёт по одному фрагменту (chunk) на каждую итерацию for await. Важны три детали:
- Текст приходит в
chunk.. В некоторых фрагментах текста нет (первый часто содержит только роль), поэтому проверяйте, чтоchoices[0]. delta. content deltaне пуст. process.печатает без перевода строки, и слова появляются одно за другим.stdout. write - С
stream_последний фрагмент содержит объектoptions: { include_ usage: true } usageс числом токенов запроса и ответа, а массивchoicesв нём пуст. Поэтому в кодеchunk.с опциональной цепочкой.choices[0]?.
Id модели берите из документации CheapTokens: у разных провайдеров одна и та же модель называется по-разному.
Шаг 2. Останавливаем поток по запросу#
Пользователь нажимает «Стоп», закрывает вкладку или переключает чат. Отмените запрос, чтобы не ждать токены, которые никто не прочитает. Передайте AbortSignal в параметрах запроса:
const controller = new AbortController();
process.on('SIGINT', () => controller.abort()); // Ctrl+C stops the stream
const stream = await client.chat.completions.create(
{
model: 'deepseek-ai/DeepSeek-V4-Flash-0731',
messages: [{ role: 'user', content: 'Write a long story about a lighthouse.' }],
stream: true,
},
{ signal: controller.signal },
);У объекта потока есть и собственный stream., а выход из цикла for await через break тоже закрывает соединение. Выбирайте удобное: сигнал — когда кнопка отмены в другом месте кода, break — когда решение принимается внутри цикла (например, встретилось стоп-слово).
Шаг 3. Обрабатываем ошибки#
Поток может упасть до первого токена (неверный ключ, пустой баланс) или посередине (обрыв сети). Оберните в один try и запрос, и цикл:
try {
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content;
if (delta) process.stdout.write(delta);
}
} catch (err) {
if (err instanceof OpenAI.APIUserAbortError) {
console.log('\n[stopped by user]');
} else if (err instanceof OpenAI.APIError) {
console.error(`\nAPI error ${err.status}: ${err.message}`);
} else {
throw err;
}
}APIUserAbortErrorприходит после вашегоabort(). Это не сбой, не показывайте его как ошибку.- У
APIErrorесть HTTP-кодstatus: 401 — неверный или отсутствующий ключ, 429 — слишком много запросов, сообщение про токены — балансу нужно пополнение. Другие причины и решения — в статьях Частые ошибки подключения и как их исправить и Ошибка «Not enough tokens». - Уже напечатанный текст после ошибки остаётся корректным. Сохраните его, если хотите показать частичный ответ.
Шаг 4. Читаем usage и оцениваем стоимость#
Объект usage содержит prompt_tokens и completion_. Вместе с ценой модели за 1M токенов он даёт точную стоимость запроса. Для прикидки заранее используйте живую таблицу ниже: в ней стоимость 1000 запросов по 2000 входных и 800 выходных токенов.
| Модель | Вход $/1M | Выход $/1M | За запрос | 1 000 запросов | OpenRouter, 1 000 запросов |
|---|---|---|---|---|---|
| GLM-5.3-Flash | $0.02 | $0.04 | $0.000072 | $0.072 | $0.143 |
| DeepSeek-V4-Flash | $0.015 | $0.10 | $0.00011 | $0.11 | $0.22 |
| MiniMax-M2.7 | $0.05 | $0.15 | $0.00022 | $0.22 | $1.56 |
2 000 входных и 800 выходных токенов на запрос. Цены за 1M токенов актуальны и меняются вместе с нашими тарифами; столбец OpenRouter показывает справочные цены для той же нагрузки.
Для потоковых ответов в чате MiniMax-M2.7 стоит $0.05 за 1M входных и $0.15 за 1M выходных токенов, контекст 180K, до 8 192 токенов в ответе. Цена модели фиксирована в любое время суток, один баланс работает для всех моделей, поэтому для сравнения на ваших промптах достаточно поменять строку model.
Полный скрипт#
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: process.env.CHEAPTOKENS_API_KEY,
baseURL: 'https://api.cheaptokens.dev/v1',
});
const controller = new AbortController();
process.on('SIGINT', () => controller.abort());
try {
const stream = await client.chat.completions.create(
{
model: 'deepseek-ai/DeepSeek-V4-Flash-0731',
messages: [{ role: 'user', content: 'Explain streaming in two sentences.' }],
stream: true,
stream_options: { include_usage: true },
},
{ signal: controller.signal },
);
let usage = null;
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content;
if (delta) process.stdout.write(delta);
if (chunk.usage) usage = chunk.usage;
}
console.log('\n\nusage:', usage);
} catch (err) {
if (err instanceof OpenAI.APIUserAbortError) {
console.log('\n[stopped by user]');
} else if (err instanceof OpenAI.APIError) {
console.error(`\nAPI error ${err.status}: ${err.message}`);
} else {
throw err;
}
}Что дальше#
- Отдавайте фрагменты в браузер через Server-Sent Events со своего эндпоинта, чтобы ключ не покидал сервер.
- Пробуйте другие модели, меняя только строку
model: MiniMax-M2.7 доступна с тем же балансом и тем же базовым URL. - Получите ключ и запустите скрипт: создайте API-ключ CheapTokens и пополните баланс.
Адреса API, id моделей и лимиты — в документации CheapTokens.



