Saltar al contenido principal
La API de Lotes te permite enviar grupos de solicitudes como un solo trabajo que se procesa de forma asíncrona. Esto es útil cuando necesitas ejecutar muchas solicitudes y no requieres resultados inmediatos — por ejemplo, realizar evaluaciones, generar embeddings para un conjunto de datos grande o clasificar contenido en bloque. Las solicitudes por lotes se ejecutan dentro de una ventana de finalización de 24 horas y tienen límites de velocidad separados y más altos que las llamadas síncronas a la API.

Endpoints soportados

EndpointDescripción
/v1/chat/completionsSolicitudes de completación de chat
/v1/embeddingsGeneración de embeddings
/v1/images/generationsGeneración de imágenes
Todas las solicitudes en un mismo lote deben dirigirse al mismo endpoint.

Ciclo de vida

Un lote pasa por estos estados:
validating → in_progress → finalizing → completed
     ↓            ↓
   failed     cancelling → cancelled
EstadoDescripción
validatingSe está verificando el archivo de entrada por errores de formato y contenido.
failedLa validación falló — el archivo de entrada contiene errores. Consulta batch.errors para más detalles.
in_progressLas solicitudes se están procesando.
finalizingTodas las solicitudes han sido procesadas y se están generando los archivos de salida.
completedEl lote finalizó. Descarga los resultados desde output_file_id.
cancellingSe solicitó una cancelación. Las solicitudes en curso están finalizando.
cancelledEl lote fue cancelado. Puede haber resultados parciales disponibles.
expiredEl lote no se completó dentro de la ventana de 24 horas.

Paso 1 — Prepara el archivo de entrada

Crea un archivo JSONL donde cada línea es una solicitud. Cada línea tiene cuatro campos:
CampoTipoDescripción
custom_idstringTu identificador para esta solicitud. Se usa para emparejar la entrada con la salida. Debe ser único dentro del archivo.
methodstring"POST" — el único método soportado.
urlstringLa ruta del endpoint — debe coincidir con el endpoint que declares al crear el lote.
bodyobjectEl cuerpo de la solicitud — los mismos parámetros que enviarías al endpoint síncrono.
{"custom_id": "request-1", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-5", "messages": [{"role": "user", "content": "Summarize the benefits of batch processing in one sentence."}], "max_tokens": 100}}
{"custom_id": "request-2", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-5", "messages": [{"role": "user", "content": "What is the capital of France?"}], "max_tokens": 100}}
{"custom_id": "request-3", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-5", "messages": [{"role": "user", "content": "Explain embeddings in one paragraph."}], "max_tokens": 100}}
Un solo lote puede contener hasta 10,000 solicitudes.

Paso 2 — Sube el archivo de entrada

Sube el archivo JSONL usando la API de Archivos con purpose: "batch".
import { SDK } from '@meetkai/mka1';

const mka1 = new SDK({
  bearerAuth: `Bearer ${YOUR_API_KEY}`,
});

const file = await mka1.llm.files.upload({
  file: new File([jsonlContent], 'batch_input.jsonl', { type: 'application/jsonl' }),
  purpose: 'batch',
});

console.log(file.id);     // "file_abc123"
console.log(file.status); // "processed"

Paso 3 — Crea el lote

Pasa el ID del archivo subido, el endpoint objetivo y la ventana de finalización.
const batch = await mka1.llm.batches.create({
  inputFileId: file.id,
  endpoint: '/v1/chat/completions',
  completionWindow: '24h',
});

console.log(batch.id);            // "batch_abc123"
console.log(batch.status);        // "validating" or "in_progress"
console.log(batch.requestCounts); // { total: 3, completed: 0, failed: 0 }
También puedes adjuntar metadatos para tu propio seguimiento:
const batch = await mka1.llm.batches.create({
  inputFileId: file.id,
  endpoint: '/v1/chat/completions',
  completionWindow: '24h',
  metadata: {
    description: 'nightly evaluation run',
    run_id: 'eval-2026-03-31',
  },
});

Paso 4 — Consulta el estado del lote

Consulta el lote hasta que alcance un estado final.
const batch = await mka1.llm.batches.get({ batchId: 'batch_abc123' });

console.log(batch.status);                // "completed"
console.log(batch.requestCounts.completed); // 3
console.log(batch.requestCounts.failed);    // 0
console.log(batch.outputFileId);           // "file_xyz789"
Aquí tienes un ayudante para hacer polling y esperar a que el lote finalice:
async function waitForBatch(batchId: string, timeoutMs = 120_000) {
  const terminal = ['completed', 'failed', 'cancelled', 'expired'];
  const start = Date.now();

  while (Date.now() - start < timeoutMs) {
    const batch = await mka1.llm.batches.get({ batchId });
    if (terminal.includes(batch.status)) return batch;
    await new Promise((r) => setTimeout(r, 2000));
  }

  throw new Error(`Batch ${batchId} did not complete within ${timeoutMs}ms`);
}

const completed = await waitForBatch(batch.id);

Paso 5 — Descarga los resultados

Una vez que el lote esté completed, descarga el archivo de salida. Es un archivo JSONL donde cada línea contiene el custom_id que proporcionaste, la respuesta y cualquier error.
const stream = await mka1.llm.files.content({ fileId: completed.outputFileId! });
const reader = stream.getReader();
const chunks: Uint8Array[] = [];
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  chunks.push(value);
}
const text = new TextDecoder().decode(Buffer.concat(chunks));

const results = text
  .split('\n')
  .filter((line) => line.trim())
  .map((line) => JSON.parse(line));

for (const result of results) {
  console.log(`${result.custom_id}: status=${result.response.status_code}`);
  console.log(`  body:`, result.response.body);
}
Cada línea en el archivo de salida tiene esta estructura:
{
  "id": "response_abc123",
  "custom_id": "request-1",
  "response": {
    "status_code": 200,
    "request_id": "req_abc123",
    "body": { "...": "same shape as the synchronous endpoint response" }
  },
  "error": null
}
Si una solicitud falló, response es null y error contiene los detalles:
{
  "id": "response_def456",
  "custom_id": "request-2",
  "response": null,
  "error": {
    "code": "processing_error",
    "message": "The request could not be processed."
  }
}
Si alguna solicitud falló, el lote también proporciona un error_file_id que contiene solo las entradas fallidas.

Cancelar un lote

Cancela un lote que aún está en progreso. Las solicitudes que ya se completaron permanecen en la salida.
const cancelled = await mka1.llm.batches.cancel({ batchId: 'batch_abc123' });
console.log(cancelled.status); // "cancelling"
El lote pasa a cancelling mientras finalizan las solicitudes en curso, y luego a cancelled.

Listar lotes

Recupera todos los lotes de la cuenta actual, del más reciente al más antiguo. Soporta paginación.
const page = await mka1.llm.batches.list({ limit: 20 });

for (const batch of page.data) {
  console.log(`${batch.id}: ${batch.status} (${batch.requestCounts?.completed}/${batch.requestCounts?.total})`);
}
Utiliza el parámetro after con un ID de lote para paginar los resultados.

Ejemplo: embeddings por lote

El mismo flujo funciona para embeddings. Cambia el url en cada línea del JSONL y el endpoint al crear el lote.
{"custom_id": "embed-1", "method": "POST", "url": "/v1/embeddings", "body": {"model": "gpt-5", "input": "The quick brown fox"}}
{"custom_id": "embed-2", "method": "POST", "url": "/v1/embeddings", "body": {"model": "gpt-5", "input": "jumps over the lazy dog"}}
const file = await mka1.llm.files.upload({
  file: new File([jsonlContent], 'embed_batch.jsonl', { type: 'application/jsonl' }),
  purpose: 'batch',
});

const batch = await mka1.llm.batches.create({
  inputFileId: file.id,
  endpoint: '/v1/embeddings',
  completionWindow: '24h',
});

const completed = await waitForBatch(batch.id);
const stream = await mka1.llm.files.content({ fileId: completed.outputFileId! });
// ... leer el stream como se muestra en el Paso 5

Errores de validación

Si el archivo de entrada tiene problemas de formato, el lote pasa a failed inmediatamente. Causas comunes:
  • JSON inválido — una línea no es un JSON válido.
  • Campos faltantes — falta custom_id, method, url o body en una línea.
  • Método incorrectomethod debe ser "POST".
  • URL no coincide — el url de una línea no coincide con el endpoint declarado al crear el lote.
  • custom_id duplicado — cada custom_id debe ser único dentro del archivo.
Consulta batch.errors.data para ver los mensajes de error específicos y los números de línea.
const batch = await mka1.llm.batches.get({ batchId: 'batch_abc123' });

if (batch.status === 'failed' && batch.errors) {
  for (const err of batch.errors.data ?? []) {
    console.log(`Line ${err.line}: [${err.code}] ${err.message}`);
  }
}

Ver también