API y SDKGuía
Transmite respuestas de chat en Node.js con el SDK de OpenAI
Transmite una finalización de chat token a token en Node.js: bucle for await, detención a demanda, manejo de errores y uso de tokens con el SDK de OpenAI.

En esta página
El streaming permite que tu aplicación muestre la respuesta a medida que se genera en lugar de esperar la respuesta completa. En esta guía construirás un pequeño script de Node.js que transmite una finalización de chat desde la API de CheapTokens token a token, la detiene a demanda y maneja errores. La versión en Python de este tema está en Respuestas en streaming en Python.
Lo que necesitas#
- Node.js 22 o superior (la versión mínima indicada en el README del paquete
openai, verificada el 22 de octubre de 2026). - Una clave de API de CheapTokens. Si aún no tienes una, sigue Tu primera solicitud a la API con CheapTokens.
- El SDK oficial:
npm install openai.
CheapTokens expone una API compatible con OpenAI en https:/, así que el paquete oficial openai funciona cambiando solo la URL base y la clave. Mantén la clave en una variable de entorno, no en el código:
export CHEAPTOKENS_API_KEY="your-key"Paso 1. Transmite la respuesta con for await#
Crea stream.mjs (la extensión .mjs te permite usar import y await de nivel superior):
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);Ejecútalo con node stream.mjs. El SDK lee los Server-Sent Events de la API y te entrega un fragmento por cada iteración de for await. Tres detalles importan:
- El texto llega en
chunk.. Algunos fragmentos no llevan texto (el primero a menudo solo contiene el rol), así que verifica quechoices[0]. delta. content deltano esté vacío. process.imprime sin salto de línea, así que las palabras aparecen una tras otra.stdout. write - Con
stream_el último fragmento lleva un objetooptions: { include_ usage: true } usagecon los recuentos de tokens de prompt y de finalización, y su arraychoicesestá vacío. Por eso el código usachunk.con el encadenamiento opcional.choices[0]?.
El id del modelo proviene de la documentación de CheapTokens: los proveedores nombran el mismo modelo de forma distinta, así que copia el id desde allí.
Paso 2. Detén el stream a demanda#
Los usuarios pulsan Detener, cierran una pestaña o cambian de chat. Cancela la solicitud para no esperar tokens que nadie leerá. Pasa un AbortSignal en las opciones de la solicitud:
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 },
);El objeto stream también tiene su propio stream., y salir del bucle for await con break también cierra la conexión. Usa el que esté más cerca de tu código: una señal cuando el botón de cancelar está en otro lugar, break cuando la decisión se toma dentro del bucle (por ejemplo, encontraste una palabra de parada).
Paso 3. Maneja errores#
Un stream puede fallar antes del primer token (clave incorrecta, saldo vacío) o en medio (caída de red). Envuelve tanto la solicitud como el bucle en un único 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;
}
}APIUserAbortErrores lo que obtienes después de tu propioabort(). No es un fallo, así que no lo muestres como tal.APIErrortiene elstatusHTTP: 401 significa una clave incorrecta o ausente, 429 significa demasiadas solicitudes, y un mensaje sobre tokens significa que el saldo necesita una recarga. Más causas y soluciones están en Errores de conexión comunes y cómo solucionarlos y error "Not enough tokens".- El texto que ya se imprimió sigue siendo válido después de un error. Consérvalo si quieres mostrar la respuesta parcial.
Paso 4. Lee el uso y estima el costo#
El objeto usage contiene prompt_tokens y completion_. Junto con el precio del modelo por 1M de tokens, te da el costo exacto de una solicitud. Para una estimación aproximada antes de ejecutar nada, usa la tabla en vivo de abajo: muestra el costo de 1.000 solicitudes con 2.000 tokens de entrada y 800 de salida cada una.
| Modelo | Entrada $/1M | Salida $/1M | Por solicitud | 1000 solicitudes | OpenRouter, 1000 solicitudes |
|---|---|---|---|---|---|
| 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 |
2000 tokens de entrada y 800 tokens de salida por solicitud. Los precios por 1M de tokens son actuales y cambian junto con nuestras tarifas; la columna de OpenRouter muestra precios de referencia para la misma carga de trabajo.
Para respuestas de chat en streaming, MiniMax-M2.7 tiene un precio de $0.05 por 1M de tokens de entrada y $0.15 por 1M de tokens de salida, con un contexto de 180K y hasta 8.192 tokens de salida por respuesta. El precio es fijo por modelo a cualquier hora del día, y un solo saldo cubre todos los modelos, así que cambiar la cadena model es suficiente para compararlos con tus propios prompts.
Script completo#
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;
}
}Próximos pasos#
- Envía los fragmentos a un navegador con Server-Sent Events desde tu propio endpoint, para que la clave nunca salga del servidor.
- Prueba otros modelos cambiando solo la cadena
model: MiniMax-M2.7 está disponible con el mismo saldo y la misma URL base. - Obtén una clave y ejecuta el script: crea una clave de API de CheapTokens y recarga el saldo.
Los endpoints, los ids de modelo y los límites de cada modelo están en la documentación de CheapTokens.



