Ir al contenido
Zabio está en beta: los modelos, los precios y las funciones pueden cambiar. Cuéntanos qué falla en hello@zabio.ai.

Referencia de la API

Esta página lista todos los endpoints que Zabio sirve, campo por campo, con un ejemplo ejecutable de cada uno en curl, Python y Node. Las demás páginas explican las ideas: Modelos y enrutado para las tres líneas y cómo se clasifica una tarea, Chat completions para imágenes, herramientas y salida estructurada, Responses API y conversaciones para las conversaciones guardadas, Errores para todos los códigos de error. Ven aquí cuando necesites la forma exacta de una petición o de una respuesta.

Todos los nombres de campo, cabeceras y códigos de esta página salen del código. Una versión legible por máquina de las mismas rutas está en openapi.yaml.

Host Uso
https://api.zabio.ai/v1 por defecto; resuelve a la región de Estados Unidos
https://api-us.zabio.ai/v1 la región de Estados Unidos por su nombre

Los ejemplos de Python y Node de esta página usan el paquete openai con base_url apuntando al primer host. Todo lo demás es una llamada normal del SDK.

from openai import OpenAI
client = OpenAI(base_url="https://api.zabio.ai/v1", api_key="insert your zabio.ai key here")
import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://api.zabio.ai/v1", apiKey: "insert your zabio.ai key here" });

Envía tu API key como bearer token. Las keys tienen la forma zb_live_ seguida de 43 letras y dígitos.

Authorization: Bearer zb_live_...

Zabio también lee la key de una cabecera x-api-key cuando no hay cabecera Authorization, para los SDK que la envían así. Todas las rutas /v1 necesitan una key, incluida GET /v1/models, salvo las públicas: GET /v1/status, POST /v1/public/analyze, GET /v1/public/free-tier, GET /v1/public/plan, GET /v1/public/auto-models y GET /v1/public/catalog-export (las tablas de modelos de esta documentación, tal como las sirve el API).

Estado code Cuándo
401 invalid_api_key la key falta, es incorrecta o está revocada. El mensaje dice cuál de las tres. Si falta, la respuesta también lleva WWW-Authenticate: Bearer realm="zabio"
403 organization_suspended la key es válida pero su organización está suspendida
403 insufficient_scope la key no tiene el scope de este endpoint
503 auth_unavailable la key no se pudo comprobar en este momento

Las keys llevan scopes. Las keys nuevas tienen los tres.

Scope Endpoints
chat POST /v1/chat/completions y, junto con conversations, POST /v1/responses
embeddings POST /v1/embeddings
conversations todo lo que hay bajo /v1/responses y /v1/conversations

La versión es el prefijo /v1 de la ruta. No hay cabecera de versión. Zabio añade campos a los objetos de OpenAI (el objeto zabio en cada respuesta, zabio en los objetos de modelo) en lugar de cambiar los que ya existen. Lee los campos que necesites e ignora el resto.

Cada respuesta lleva una cabecera x-request-id con la forma req_ seguida de 32 caracteres hexadecimales. Lo asigna Zabio; un X-Request-Id que envíes tú no se reutiliza. Cítalo cuando escribas a soporte.

Los dos cupos por minuto de la key se informan en cada respuesta de /v1/chat/completions, /v1/embeddings y /v1/responses:

Cabecera Significado
x-ratelimit-limit-requests peticiones por minuto permitidas a esta key (por defecto 600)
x-ratelimit-remaining-requests peticiones que quedan en el minuto actual
x-ratelimit-reset-requests tiempo hasta que el cupo vuelve a estar lleno, como 12s o 500ms
x-ratelimit-limit-tokens tokens por minuto permitidos a esta key (por defecto 200000)
x-ratelimit-remaining-tokens tokens que quedan. El cupo se cobra con una estimación (tokens de entrada más max_tokens) al admitir la petición y se devuelve con la cifra real tras la llamada
x-ratelimit-reset-tokens tiempo hasta que el cupo de tokens vuelve a estar lleno
Retry-After en 429, segundos enteros a esperar (al menos 1). También 5 en 502, 503 y 504 cuando tiene sentido reintentar

Dos límites más tienen sus propias cabeceras:

Cabecera Significado
x-zabio-quota-scope, x-zabio-quota-limit, x-zabio-quota-remaining presentes cuando se aplica un límite de gasto. scope es key (el límite propio de la key, en su periodo: un día, una semana desde el lunes o un mes desde las 00:00 UTC, o toda la vida de la key) o member (la cuota mensual del miembro que creó la key). Los valores están en micros (millonésimas de dólar)
x-ratelimit-limit-ip, x-ratelimit-remaining-ip, x-ratelimit-reset-ip el límite por dirección, comprobado antes de la autenticación. Solo se envían en el rechazo 429 ip_rate_limit_exceeded, nunca en respuestas normales

El plan Free (consulta Facturación, “El plan Free”) añade las suyas:

Cabecera Significado
x-zabio-plan free en toda llamada servida con los límites de Free; ausente en una llamada de pago
x-ratelimit-limit-requests-day, x-ratelimit-remaining-requests-day, x-ratelimit-reset-requests-day el día del plan Free: 100 peticiones correctas, las que quedan y el tiempo hasta las 00:00 UTC. Solo con x-zabio-plan: free; en esas llamadas x-ratelimit-*-requests es el cupo del minuto de Free (20), más estricto que el propio de la key
x-zabio-free-limit en un 429 o 403 free_tier_limit, qué límite se alcanzó: requests_per_day, requests_per_minute, daily_cost, input_tokens, max_tokens, members o api_keys
Cabecera Significado
x-zabio-vendor el proveedor que sirvió la llamada (anthropic, openai, google, deepseek, xai, mistral, together, typesafe); también se envía en los errores de proveedor para nombrar al que falló. Ausente en una llamada mantenida viva más de 90 segundos (ver Llamadas largas): sus cabeceras salieron antes de elegir el proveedor, y el objeto zabio del cuerpo lleva el proveedor, el modelo y el enrutado
x-zabio-byok true cuando la llamada la sirvió la key propia de tu organización
x-zabio-balance-micros el saldo de créditos de tu organización tras la comprobación de admisión, cuando esa comprobación se ejecutó

Zabio no tiene claves de idempotencia. Un POST repetido es una llamada nueva y se factura otra vez. Reintenta en 429, 502, 503 y 504 tras Retry-After; no reintentes un 200 que no recibiste sin comprobar antes tus propios registros.

Los cuerpos de petición están limitados a 32 MB (413 payload_too_large). El resto de límites aparece junto a cada endpoint.

Cada error es JSON con el formato de OpenAI, así que los SDK lanzan sus excepciones habituales:

{"error": {"message": "'temperature' must be between 0 and 2.", "type": "invalid_request_error", "param": "temperature", "code": "invalid_value"}}

type es invalid_request_error, permission_error, billing_error, rate_limit_error o server_error. param está presente cuando la culpa es de un campo. La lista completa de códigos está en la página de Errores; cada endpoint de abajo lista los códigos que le son propios.

Lista los cuatro modelos auto seguidos del catálogo, con los precios de tu organización. Las keys con lista de permitidos solo ven sus modelos. No hace falta ningún scope más allá de una key válida.

Respuesta: {"object": "list", "data": [Model, ...]}. Un Model es el objeto de modelo de OpenAI más un objeto zabio:

Campo Tipo Notas
id cadena auto, auto/best, auto/balanced, auto/cheap o un id del catálogo
object cadena model
created entero la misma fecha de lanzamiento para todos los modelos
owned_by cadena zabio para los modelos auto; si no, el proveedor: anthropic, openai, google, deepseek, xai, mistral, together, typesafe
zabio.tier cadena la clase del modelo: fast, balanced, powerful, frontier. Una etiqueta para las páginas, no una regla de enrutado. Para los ids auto es la línea
zabio.auto_eligible booleano si auto puede elegirlo hoy: tiene puntuación de habilidad para al menos un tipo de trabajo, o la regla anterior puede elegirlo para una petición de tipo other (ver Modelos y enrutado). Sigue los datos de habilidad diarios, la misma lista que GET /v1/public/auto-models
zabio.named_only booleano lo contrario de auto_eligible: al modelo se llega nombrándolo; se omite cuando es false
zabio.virtual booleano true solo en los modelos auto
zabio.embedding booleano true solo en los modelos de embeddings
zabio.capabilities objeto solo modelos de chat: vision, tools, json_schema (booleanos), context_window, max_output (tokens)
zabio.pricing objeto input, output, cache_read, cache_write en USD por millón de tokens con la comisión de tu plan incluida, currency (USD). cache_write es la tarifa de 5 minutos
zabio.residency cadena dónde puede ejecutarse la inferencia, usado por el enrutado: global en la mayoría de los modelos, us en los modelos servidos solo desde Estados Unidos (hoy los de xAI)
zabio.location cadena dónde procesa la petición el proveedor, tal como se muestra en la página de Modelos (por ejemplo United States, China); junto con zabio.label, el nombre para mostrar cuando el id por sí solo es ambiguo
zabio.free_tier booleano true en los modelos de la lista de modelos gratuitos (consulta GET /v1/public/free-tier)
zabio.availability cadena available o locked: si esta key puede nombrar el modelo ahora mismo. locked solo mientras la organización funciona con los límites de Free y el modelo no está en la lista gratuita; los ids auto siempre están disponibles. Ausente en una organización de pago con saldo

curl

Terminal window
curl https://api.zabio.ai/v1/models \
-H "Authorization: Bearer insert your zabio.ai key here"

Python

models = client.models.list()
for m in models.data:
z = m.model_extra.get("zabio", {})
print(m.id, m.owned_by, z.get("auto_eligible"), (z.get("pricing") or {}).get("input"))

Node

const models = await client.models.list();
for (const m of models.data) console.log(m.id, m.owned_by, m.zabio.auto_eligible, m.zabio.pricing?.input);

Devuelve un objeto de modelo. Los ids virtuales con barra funcionan: GET /v1/models/auto/cheap y la forma codificada GET /v1/models/auto%2Fcheap se aceptan las dos.

Errores: 404 model_not_found cuando el id no existe, está desactivado para tu organización o no está en la lista de permitidos de la key.

curl

Terminal window
curl https://api.zabio.ai/v1/models/claude-sonnet-5 \
-H "Authorization: Bearer insert your zabio.ai key here"

Python

m = client.models.retrieve("auto/cheap")
print(m.id, m.model_extra["zabio"])

Node

const m = await client.models.retrieve("auto/balanced");
console.log(m.id, m.zabio);

Pública, sin key: la lista de modelos gratuitos en vigor y los límites de Free, para una página de precios o un cliente que quiera saber qué recibe una cuenta Free antes de registrarse. Cache-Control: public, max-age=60. El límite de uso razonable se indica como existente, nunca con su importe. Consulta Facturación, “El plan Free”.

{
"models": ["deepseek-flash@together", "gemini-3.5-flash-lite", "gpt-5.4-nano", "gpt-oss-120b", "mistral-small-2603", "qwen3.5-9b"],
"limits": {"requests_per_day": 100, "requests_per_minute": 20, "max_input_tokens": 16000, "max_output_tokens": 2000, "fair_use_daily_limit": true}
}

Errores: 503 free_tier_unavailable cuando la lista no se puede leer. La misma lista marca cada modelo en GET /v1/models con zabio.free_tier: true, y para una key con los límites de Free zabio.availability dice available o locked por modelo.

Terminal window
curl https://api.zabio.ai/v1/public/free-tier

Pública, sin key: los ids del catálogo que auto puede elegir hoy, para una página o un cliente que quiera mostrarlos. Un modelo está en la lista cuando tiene puntuación de habilidad para al menos un tipo de trabajo, o cuando la regla anterior puede elegirlo para una petición de tipo other (ver Modelos y enrutado). La lista sigue los datos de habilidad diarios y los ajustes del propio enrutado; as_of es la fecha de los datos de habilidad de los que se leyó. Cache-Control: public, max-age=600. La misma respuesta es zabio.auto_eligible en GET /v1/models.

{"object": "auto_models", "as_of": "2026-10-06T00:00:00Z", "models": ["claude-haiku-4-5", "deepseek-flash", "gpt-6-luna"]}
Terminal window
curl https://api.zabio.ai/v1/public/auto-models

Un turno de chat sin estado con el formato de OpenAI. Necesita el scope chat. Zabio no guarda nada de estas llamadas salvo que la key tenga activado el registro de cuerpos.

Campo Tipo Obligatorio Notas
model cadena no un id del catálogo o auto, auto/best, auto/balanced, auto/cheap. Si se omite, es auto
messages array sí de 1 a 4096 mensajes con role system, developer, user, assistant o tool. Al menos uno debe ser user, assistant o tool. Los textos system y developer se unen con una línea en blanco en un solo system prompt
messages[].content cadena o array sí los mensajes user aceptan partes text e image_url; el resto de roles solo partes text. Los mensajes assistant pueden llevar content, tool_calls o ambos
messages[].tool_calls array no en mensajes assistant: {"id", "type": "function", "function": {"name", "arguments"}}. Un JSON inválido en arguments se sustituye por {}
messages[].tool_call_id cadena en mensajes tool el id de la llamada que se responde
tools array no hasta 256 herramientas de type function con function.name, function.description, function.parameters (por defecto un esquema de objeto vacío)
tool_choice cadena u objeto no auto, none, required o {"type": "function", "function": {"name": "..."}}. Requiere tools salvo que sea none
parallel_tool_calls booleano no se pasa al proveedor cuando lo soporta
max_tokens, max_completion_tokens entero no max_completion_tokens gana cuando están los dos. Por defecto 16000, limitado al max_output del modelo
temperature número no de 0 a 2
top_p número no de 0 a 1
stop cadena o array no hasta 4 secuencias
stream booleano no server sent events, más abajo
stream_options.include_usage booleano no se acepta; el chunk de uso se envía siempre
response_format objeto no {"type": "text"}, {"type": "json_object"} o {"type": "json_schema", "json_schema": {"name", "schema", "strict"}}; schema es obligatorio
reasoning_effort cadena no none, minimal, low, medium, high o xhigh; se aplica al control propio del modelo servido, consulta Niveles de razonamiento
user cadena no se reenvía como metadato de la petición
metadata objeto no valores de cadena, se reenvían como metadatos de la petición
store booleano no se acepta y no tiene efecto; chat completions no guarda nada
n entero no solo 1; los valores mayores se rechazan
seed, logit_bias, presence_penalty, frequency_penalty no se aceptan y se ignoran; aparecen en zabio.ignored_parameters cuando llevan un valor distinto del predeterminado
logprobs, top_logprobs, audio, prediction, web_search_options, modalities distintas de texto no se rechazan con 400 unsupported_parameter
zabio objeto no las opciones de Zabio, en la siguiente tabla
Campo Tipo Valores Qué hace
session_id cadena hasta 128 caracteres agrupa llamadas para reutilizar la decisión de enrutado; se repite en la respuesta
vendors array anthropic, openai, google, deepseek, xai, mistral, together solo estos proveedores son candidatos
fallback cadena same_vendor, cross_vendor, none qué pasa cuando un modelo nombrado explícitamente no está disponible. Por defecto: el fallback_policy de la key
cache.ttl cadena 5m, 1h, none duración de la caché de prompt cuando el proveedor la ofrece. La caché es automática en cada llamada, a auto o a un modelo nombrado, y none la desactiva. Por defecto: 5m (1h en una sesión cuyos turnos están muy separados), escrito en la segunda aparición de un prompt largo (consulta cache.ttl); dentro de un lote, none, salvo que las líneas que comparten un prefijo largo en un modelo de Anthropic lo leen de una caché que Zabio escribe una vez para el lote
cache.breakpoints cadena auto, none none desactiva la caché para la llamada y fuerza ttl a none
split cadena auto, off si Zabio puede dividir una petición grande con auto en un extracto en un modelo barato y una respuesta en el modelo enrutado, cuando sale más barato. Por defecto auto; off envía la petición entera. Nunca con un modelo con nombre. Ver División y enrutado en entradas grandes
deadline cadena una duración con unidades s, m o h (15m, 1h, 24h), hasta 168h cuánto puede tardar la respuesta. Sin valor o por debajo de 15 minutos, no cambia nada. A partir de 15 minutos, la llamada puede servirse en el nivel flex del proveedor cuando el modelo lo tiene, el mismo modelo a la mitad de precio, más despacio. No garantiza un plazo de entrega. Ver El nivel flex

Los mismos valores pueden viajar como cabeceras, que tienen prioridad sobre el cuerpo: x-zabio-session-id, x-zabio-cache-ttl, x-zabio-fallback, x-zabio-split, x-zabio-deadline. No hay cabecera para vendors ni para cache.breakpoints. Los valores incorrectos devuelven 400 invalid_value con param igual a zabio.<campo>.

Un objeto chat.completion con exactamente una opción:

Campo Tipo Notas
id cadena chatcmpl- seguido de 32 caracteres hexadecimales
object cadena chat.completion
created entero segundos Unix
model cadena el modelo que respondió (igual que zabio.model_served)
choices[0].index entero 0
choices[0].message objeto role assistant, content (cadena, o null cuando solo se produjeron llamadas a herramientas), tool_calls cuando las hay. arguments es siempre una cadena JSON, {} cuando está vacía
choices[0].finish_reason cadena stop, length, tool_calls o content_filter. Un modelo de razonamiento que gasta todo su presupuesto de salida pensando devuelve una respuesta vacía con length: sube max_tokens para que deje sitio a la respuesta por encima del razonamiento, o baja reasoning_effort. Un max_tokens por encima de la salida máxima del modelo servido se baja a ese límite
choices[0].logprobs null siempre null
usage objeto prompt_tokens (todos los tokens de entrada, en caché o no), completion_tokens, total_tokens, prompt_tokens_details.cached_tokens cuando se leyó algún token de la caché, y completion_tokens_details.reasoning_tokens cuando el proveedor informa de su recuento de razonamiento
system_fingerprint null siempre null
zabio objeto siguiente tabla

El mismo objeto lo devuelven /v1/chat/completions (en el cuerpo y en el último chunk del streaming) y /v1/responses. Los campos con valor vacío se omiten.

Campo Tipo Significado
call_id cadena el id de la llamada, call_ y 32 caracteres hexadecimales: el mismo id que muestran Registros y la lista de llamadas, y el mismo hexadecimal que el id chatcmpl- de la respuesta. Cítalo para encontrar la llamada más tarde
vendor cadena quién sirvió la llamada: anthropic, openai, google, deepseek, xai, mistral, together o typesafe
model_served cadena el modelo que respondió; difiere del model que enviaste con auto y con los fallbacks
routing objeto line (el nivel tal como lo pediste: auto, cheap, balanced o best), floor (el mínimo de habilidad aplicado, none, middle o top, después de que una petición difícil o de riesgo alto lo subiera un escalón y de cualquier paso a un modelo más capaz; ausente cuando la petición se enrutó con la regla anterior, ver Modelos y enrutado), task (la tarea tal como la lee el enrutado), task_type (el tipo de trabajo, una de las etiquetas de Tipos de trabajo; ausente cuando el clasificador no nombró ninguno), difficulty (de 1 a 5), band (easy, medium, hard, frontier), stakes (normal o high), biased (true cuando la poca confianza subió la dificultad), cell (el conocimiento usado, tarea/banda), confidence (de 0 a 1; 0 cuando no se ejecutó el clasificador), reason (la frase del clasificador), trigger, classifier (quién decidió: typesafe/jev-1.13.0, o el clasificador de texto al que se recurrió; ausente en modelos explícitos), effort (el ajuste de razonamiento aplicado junto con el modelo), effort_reason (por qué: la evidencia de la banda, riesgo alto, la línea best, sin evidencia, o tu nivel explícito) y sticky (solo presente cuando la caché de prompt caliente de la sesión decidió la elección; nombra el modelo que se mantuvo y el que habría elegido el precio en frío, ver Sesiones y persistencia). Triggers: no_prior_decision, new_human_message, cache_hit, low_confidence_prior, long_idle, tool_failure_streak, customer_feedback, explicit, escalation. Ausente cuando no hay nada que informar
reasoning objeto requested (el nivel que enviaste, omitido si ninguno) y applied (lo que recibió el modelo servido: default, off, el nombre del nivel del proveedor, budget N, on, always on o not supported)
cost_micros entero lo que se te facturó, en millonésimas de dólar
cost objeto vendor_micros (precio de lista del proveedor), service_fee_micros, call_fee_micros (la cantidad fija que paga una vez una petición auto, el precio de la elección automática del modelo; 0 en un modelo con nombre), savings_fee_micros y classifier_micros (ambos siempre 0, se mantienen para que los clientes existentes sigan leyéndolos), direct_micros (la misma llamada en el modelo de referencia de tu key con las mismas lecturas y escrituras de caché a las tarifas de ese modelo; el propio precio del proveedor en un modelo con nombre), saved_micros (direct_micros menos cost_micros, nunca negativo: una llamada que costó más que su referencia indica 0 y saving_note "no saving on this call"; no aparece en una llamada auto mientras baseline.pending es true), baseline (solo en auto: {pending, model} cuando tu key ya tiene referencia, o {pending: true, calls, calls_needed} mientras siga por debajo de las 20 llamadas correctas que necesita la referencia)
byok booleano presente y true cuando la llamada la sirvió tu propia key del proveedor
cache objeto siempre presente: read_tokens, write_tokens, ttl_applied (5m, 1h o "")
session_id cadena repite el id de sesión que enviaste. En /v1/responses es el id de la conversación
escalated_from cadena línea/modelo del modelo cuya respuesta auto sustituyó, por ejemplo cheap/gpt-5.4-nano: en la Responses API cuando ese modelo pidió uno más potente, y en cualquier petición sin streaming cuando su respuesta no pasó la comprobación de Zabio
compacted booleano solo Responses API: el historial guardado se resumió antes de este turno
vendor_stop_reason cadena el motivo de parada propio del proveedor
ignored_parameters array campos de la petición que Zabio aceptó e ignoró
dropped_parameters array parámetros de muestreo que el modelo que respondió no admite (temperature, top_p), omitidos de su petición; también la cabecera x-zabio-dropped-params. Consulta dropped_parameters
attempts entero cuántos proveedores se probaron antes de que uno respondiera (1 cuando el primero funcionó)
decision objeto cuando respondió jev-1.13.0 (nombrado, o elegido por auto para una tarea de clasificación, ver Tareas de clasificación en auto): una entrada por propiedad de tu esquema, en el orden del esquema. Una elección: {"type": "choice", "probabilities": {valor: p, ...}, "confidence"}; un booleano: {"type": "noul", "probability": p} (la probabilidad del sí; true a partir de 0.5); una puntuación: {"type": "score", "value": el nivel ponderado, "probabilities": {nivel: p, ...}, "confidence"}. Ver Modelos de decisión
no_answer string presente cuando la petición terminó sin ninguna respuesta, con el presupuesto de salida del modelo gastado en razonamiento: qué pasó y qué cambiar (max_tokens, el esfuerzo de razonamiento). Esa petición no se cobra: cost_micros es 0
split objeto presente mientras división y enrutado está activo (ver División y enrutado en entradas grandes): applied (booleano, true cuando la respuesta vino de un extracto de tu fuente), pattern (distill, una vez que la división se ejecutó), reason (por qué no se dividió la petición: named_model, opt_out, not_eligible, below_threshold, jev_no, jev_failed, price_check; vacío en una división y en una vuelta atrás), estimate (unsplit_micros, split_micros; presente una vez pasado el filtro), steps (una lista de {role, vendor, model, input_tokens, output_tokens, micros, billed}; roles questions, extract, check, answer; micros es el precio de lista de la llamada; billed es false en los costes propios de Zabio y en cada paso de una vuelta atrás), excerpt (lines_kept, lines_total, tokens, verbatim_rate), check_p (la probabilidad de la comprobación), fallback (la salvaguarda que mandó la petición entera: extractor_declined, empty_extract, extract_too_big, low_verbatim, check_rejected, need_more_source, extract_failed, check_failed), saved_micros (en una división que se mantuvo, lo que ahorró la división: la petición entera en el modelo de la respuesta menos lo que costaron las llamadas de la división, a precio de lista; aparte de cost.saved_micros, y presente también mientras la referencia de tu key está pendiente). Los campos vacíos se omiten
tier objeto el nivel de servicio de la llamada (ver El nivel flex): requested (standard, flex o batch, a qué se tradujo tu deadline), applied (qué sirvió la llamada) y fallback (capacity cuando se pidió flex y el proveedor lo rechazó por falta de capacidad, de modo que el nivel estándar sirvió y fue el facturado; synchronous cuando un plazo de 24 horas o más pedía lotes en una llamada síncrona, así que sirvió flex o estándar; no_batch_tier en una response en segundo plano cuyo modelo no tiene nivel de lotes, y en una línea de lote al precio estándar con zabio.allow_standard_price; decision en una línea de lote que Jev respondió a su propio precio, por debajo del precio por lotes del modelo de texto elegido). Presente en todas las respuestas
plan objeto presente en una llamada servida con los límites de Free (consulta Facturación, “El plan Free”): tier (free), reason (free_plan, la organización nunca ha recargado; zero_balance, una organización de pago en cero que continúa en el nivel gratuito), paid_would_use (con auto, el modelo que habría recibido una cuenta de pago, cuando es distinto del que respondió) y topup_url. Ausente en una llamada de pago. No confundir con tier, el nivel de procesamiento del proveedor

zabio.routing.task_type nombra el tipo de trabajo que el clasificador vio en una petición auto, con una de estas etiquetas. El gasto por tipo de trabajo del panel y el Registro muestran el nombre, y la exportación CSV de tus llamadas desde el panel lleva la etiqueta. La lista puede crecer: trata una etiqueta que no conozcas como un tipo de trabajo nuevo, no como un error.

Etiqueta Nombre
classification_tagging Clasificación
data:extraction Extracción de datos
agent:workflow_execution Ejecución de flujos de trabajo
roleplay_fiction Juego de rol y ficción
content_writing Redacción de contenidos
data:transformation Transformación de datos
code:general_impl Generación de código
agent:multi_step_planning Planificación en varios pasos
qa_knowledge Preguntas y conocimiento
translation Traducción
conversational_reply Conversación
code:debugging Depuración
summarization Resumen
code:file_read_write Lectura y escritura de archivos
customer_support Atención al cliente
code:review_security Revisión de código
agent:tool_dispatch Invocación de herramientas
code:shell_execution Ejecución en shell
agent:memory_extraction Extracción de memoria
code:frontend_ui Frontend e interfaz de usuario
finance_trading Finanzas y trading
research_report Investigación e informes
agent:web_search Búsqueda web
security_audit Auditoría de seguridad
code:repo_scan Exploración de repositorios
math Matemáticas
code:devops_config DevOps y configuración
code:sql_database SQL y bases de datos
devops DevOps
other Otros

Con stream: true el estado es 200 y el cuerpo es text/event-stream. Cada evento es una línea data: con un chat.completion.chunk que lleva el mismo id, created y model que tendría una respuesta normal. Orden:

  1. un chunk con delta: {"role": "assistant", "content": ""}
  2. chunks de contenido con delta.content, y chunks de llamadas a herramientas con delta.tool_calls[] que llevan index, id, type y function.name en el primer chunk de cada llamada y fragmentos de function.arguments después
  3. un chunk con delta vacío y finish_reason
  4. un chunk de uso con choices: [], el bloque usage y el objeto zabio
  5. data: [DONE]

Cada chunk lleva system_fingerprint: null, y cada opción logprobs: null. Si la conexión con el proveedor falla después de empezar el stream, se envía un evento con {"error": {"message", "type": "server_error", "code"}} y después data: [DONE]. Los errores anteriores al primer chunk son errores JSON normales con su estado HTTP.

Un intento con el proveedor puede durar hasta 600 segundos en los modelos powerful y frontier (180 segundos en los demás). Para mantener viva la conexión ese tiempo, una llamada sin respuesta del proveedor a los 90 segundos confirma su 200 y escribe un espacio cada 20 segundos hasta el cuerpo JSON, que los parsers leen sin cambios. Un fallo tras esa confirmación se entrega en el cuerpo como un chat.completion con choices: [] y un objeto error (message, type, code). Los streams envían un comentario SSE (: keepalive) cada 20 segundos mientras el proveedor abre o calla, y un fallo antes de cualquier dato es data: {"error": {...}} y después data: [DONE]. Pon los timeouts del cliente en al menos 600 segundos para los modelos powerful y frontier. Una desconexión cancela la llamada al proveedor y, en una llamada sin streaming, no cuesta nada.

Estado code Cuándo
400 missing_messages falta messages o no tiene ninguna entrada user, assistant o tool
400 too_many_messages, too_many_tools más de 4096 mensajes o 256 herramientas
400 invalid_value un campo está fuera de rango; param lo nombra (temperature, top_p, max_tokens, stop, reasoning_effort, tool_choice, tools[i].type, messages[i].role, messages[i].content, response_format.type, response_format.json_schema, zabio.session_id, zabio.cache.ttl, zabio.cache.breakpoints, zabio.fallback, zabio.split, zabio.deadline)
400 unsupported_parameter n mayor que 1, logprobs, top_logprobs, audio, prediction, web_search_options, una entrada de modalities distinta de text, el rol antiguo function, un tipo de parte de contenido distinto de text e image_url, tools enviadas a un modelo nombrado que no puede recibirlas a través de Zabio (hoy gpt-6-astra)
400 invalid_image_url una imagen no se pudo descargar o no está permitida; param apunta a la parte
400 byok_key_invalid el proveedor rechazó la key propia de tu organización
400 upstream_invalid_request el proveedor rechazó la petición; el mensaje lleva el motivo del proveedor
400 unsupported_for_model el modelo que nombraste no puede hacer lo que pide la petición; param nombra el campo. Con jev-1.13.0: sin json_schema, una propiedad de texto libre o anidada, herramientas, imágenes, resultados de herramientas o stream: true; el mensaje dice qué responde Jev y cómo pedirlo
402 insufficient_credits no quedan créditos; solo un modelo con nombre servido con tu propia key evita esta comprobación, auto nunca
403 insufficient_scope la key no tiene chat
404 model_not_found modelo desconocido o no permitido para la key
429 rate_limit_exceeded, quota_exceeded, upstream_rate_limited consulta las cabeceras de arriba y Errores
502, 503, 504 upstream_error, no_provider_available, billing_unavailable, upstream_timeout consulta Errores

Una llamada normal con auto, y después la lectura de qué modelo la sirvió y cuánto costó.

curl

Terminal window
curl https://api.zabio.ai/v1/chat/completions \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "Content-Type: application/json" \
-d '{
"model": "auto",
"messages": [
{"role": "system", "content": "Responde en una frase."},
{"role": "user", "content": "¿Por qué el cielo es azul?"}
],
"zabio": {"session_id": "docs-demo"}
}'

Python

r = client.chat.completions.create(
model="auto",
messages=[
{"role": "system", "content": "Responde en una frase."},
{"role": "user", "content": "¿Por qué el cielo es azul?"},
],
extra_body={"zabio": {"session_id": "docs-demo"}},
)
print(r.choices[0].message.content)
z = r.model_extra["zabio"]
print(z["model_served"], z["cost_micros"], z["routing"]["line"], z["routing"]["task"])

Node

const r = await client.chat.completions.create({
model: "auto",
messages: [
{ role: "system", content: "Responde en una frase." },
{ role: "user", content: "¿Por qué el cielo es azul?" },
],
zabio: { session_id: "docs-demo" },
});
console.log(r.choices[0].message.content);
console.log(r.zabio.model_served, r.zabio.cost_micros, r.zabio.routing?.line, r.zabio.routing?.task);

La misma llamada en streaming. El objeto zabio llega en el último chunk, el que tiene la lista choices vacía.

curl

Terminal window
curl -N https://api.zabio.ai/v1/chat/completions \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "Content-Type: application/json" \
-d '{"model": "auto/cheap", "messages": [{"role": "user", "content": "Cuenta hasta cinco."}], "stream": true}'

Python

stream = client.chat.completions.create(
model="auto/cheap",
messages=[{"role": "user", "content": "Cuenta hasta cinco."}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
if chunk.usage:
print("\n", chunk.usage.total_tokens, chunk.model_extra.get("zabio"))

Node

const stream = await client.chat.completions.create({
model: "auto/cheap",
messages: [{ role: "user", content: "Cuenta hasta cinco." }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
if (chunk.usage) console.log("\n", chunk.usage.total_tokens, chunk.zabio);
}

Un ciclo completo con herramientas. El asistente responde con tool_calls; tú ejecutas la función y devuelves el resultado en un mensaje tool.

curl

Terminal window
curl https://api.zabio.ai/v1/chat/completions \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "Content-Type: application/json" \
-d '{
"model": "auto",
"messages": [{"role": "user", "content": "¿Qué tiempo hace en Lima? Usa la herramienta."}],
"tools": [{"type": "function", "function": {"name": "get_weather", "description": "Tiempo en una ciudad",
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}}],
"tool_choice": "required"
}'

Python

tool = {"type": "function", "function": {"name": "get_weather", "description": "Tiempo en una ciudad",
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}}
messages = [{"role": "user", "content": "¿Qué tiempo hace en Lima? Usa la herramienta."}]
first = client.chat.completions.create(model="auto", messages=messages, tools=[tool], tool_choice="required")
call = first.choices[0].message.tool_calls[0]
messages.append(first.choices[0].message.model_dump(exclude_none=True))
messages.append({"role": "tool", "tool_call_id": call.id, "content": "Soleado, 22C"})
second = client.chat.completions.create(model="auto", messages=messages, tools=[tool])
print(second.choices[0].message.content)

Node

const tools = [{ type: "function", function: { name: "get_weather", description: "Tiempo en una ciudad",
parameters: { type: "object", properties: { city: { type: "string" } }, required: ["city"] } } }];
const messages = [{ role: "user", content: "¿Qué tiempo hace en Lima? Usa la herramienta." }];
const first = await client.chat.completions.create({ model: "auto", messages, tools, tool_choice: "required" });
const call = first.choices[0].message.tool_calls[0];
messages.push(first.choices[0].message, { role: "tool", tool_call_id: call.id, content: "Soleado, 22C" });
const second = await client.chat.completions.create({ model: "auto", messages, tools });
console.log(second.choices[0].message.content);

Embeddings con el formato de OpenAI. Los modelos son siempre explícitos; no hay auto. Necesita el scope embeddings.

Campo Tipo Obligatorio Notas
model cadena sí text-embedding-3-small, text-embedding-3-large o gemini-embedding-001
input cadena o array sí una cadena o de 1 a 2048 cadenas
dimensions entero no se pasa al proveedor
encoding_format cadena no float (por defecto) o base64 (float32 little endian)
user cadena no se acepta

El objeto zabio de la petición no se aplica aquí. Las cabeceras de límite de tasa y de cuota son las mismas que en chat completions; el cupo de tokens se cobra con una estimación de un token por cada cuatro caracteres.

Campo Tipo Notas
object cadena list
data[] array {"object": "embedding", "index", "embedding"}; embedding es un array de números o una cadena base64
model cadena el modelo que respondió
usage objeto prompt_tokens y total_tokens (iguales)
zabio objeto call_id, vendor, model_served, cost_micros, tokens_estimated. tokens_estimated es true en los modelos de Google, que no informan del número de tokens; Zabio los estima y factura sobre la estimación
Estado code Cuándo
400 missing_model falta model
400 invalid_value input vacío o con más de 2048 elementos, o encoding_format distinto de float y base64
400 byok_key_invalid el proveedor rechazó tu propia key
404 model_not_found no es un modelo de embeddings, o no está permitido para la key
503 no_provider_available el proveedor de este modelo no está configurado en este servidor

curl

Terminal window
curl https://api.zabio.ai/v1/embeddings \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "Content-Type: application/json" \
-d '{"model": "text-embedding-3-small", "input": ["primer texto", "segundo texto"]}'

Python

res = client.embeddings.create(model="text-embedding-3-small", input=["primer texto", "segundo texto"])
print(len(res.data), len(res.data[0].embedding), res.model_extra["zabio"]["cost_micros"])

Node

const res = await client.embeddings.create({ model: "text-embedding-3-small", input: ["primer texto", "segundo texto"] });
console.log(res.data.length, res.data[0].embedding.length, res.zabio.cost_micros);

Un turno de una conversación guardada con el formato de la Responses API de OpenAI. Zabio monta el historial, enruta el turno, guarda la respuesta y la devuelve. Necesita el scope conversations; el enrutado en sí reutiliza el camino de chat.

Campo Tipo Obligatorio Notas
model cadena no un id del catálogo o un modelo auto. Si se omite, es auto
input cadena o array sí una cadena es un mensaje user. Elementos: {"type": "message", "role": "user" o "assistant" o "system" o "developer", "content": cadena o partes} (type puede omitirse), {"type": "function_call_output", "call_id", "output"}, {"type": "function_call", "call_id", "name", "arguments"}. Partes de contenido: input_text, text y output_text (todas texto), input_image con image_url
instructions cadena no se guarda con el turno como elemento developer
previous_response_id cadena no resp_... desde el que continuar. Apuntar a una response antigua ramifica la conversación
conversation cadena u objeto no conv_... o {"id": "conv_..."} para continuar desde el último turno de la conversación. Con previous_response_id, los dos deben pertenecer a la misma conversación
store booleano no por defecto true. false calcula el turno a partir del historial guardado y no guarda nada; el id de la response no se podrá recuperar después
tools array no herramientas de función planas: {"type": "function", "name", "description", "parameters", "strict"}
tool_choice cadena u objeto no auto, none, required o {"type": "function", "name"}; requiere tools salvo que sea none
parallel_tool_calls booleano no se pasa al proveedor cuando lo soporta
max_output_tokens entero no al menos 1; por defecto 4096
temperature número no de 0 a 2
top_p número no mayor que 0 y hasta 1
text.format objeto no {"type": "text"}, {"type": "json_object"} o {"type": "json_schema", "name", "schema", "strict"}
reasoning.effort cadena no none, minimal, low, medium, high o xhigh, la misma escala que reasoning_effort en chat completions
metadata objeto no hasta 16 claves; claves de hasta 64 caracteres, valores de hasta 512
stream booleano no server sent events, más abajo
background booleano no por defecto false. true devuelve la response al momento con estado queued y ejecuta el turno más tarde; consulta GET /v1/responses/{id}. Necesita store true y no admite stream. Con un zabio.deadline de 24 horas o más en un modelo que admite lotes, el turno se suma al siguiente lote del proveedor de tu organización al precio por lotes; si no, se ejecuta como trabajo propio en flex o estándar y zabio.tier dice por qué. Ver Responses en segundo plano
include, truncation, user no se aceptan y se ignoran; aparecen en zabio.ignored_parameters
zabio objeto no siguiente tabla

Todo lo de chat completions (session_id, vendors, fallback, cache, split, deadline) más:

Campo Tipo Valores Qué hace
retention_days entero de 0 a 365 retención de una conversación creada por esta llamada. 0 la borra en cuanto se devuelve la response. Se ignora cuando la conversación ya existe
compaction cadena auto (por defecto), off off falla con context_length_exceeded en lugar de resumir el historial antiguo

session_id se sobrescribe: dentro de una conversación, la sesión es la propia conversación, así que zabio.session_id en la respuesta es conv_.... Las cabeceras x-zabio-* también funcionan aquí.

Campo Tipo Notas
id cadena resp_ seguido de 32 caracteres hexadecimales
object cadena response
created_at entero segundos Unix
status cadena completed, incomplete (alcanzó max_output_tokens; incomplete_details.reason es max_output_tokens) o failed (en streaming, tras un error del proveedor; o una response en segundo plano que falló). Una response en segundo plano es también queued y cancelled
background booleano true cuando el turno se creó con background
error objeto o null en una response en segundo plano fallida o cancelada, {"code", "message"}
model cadena el modelo que respondió
output[] array {"type": "message", "id": "msg_...", "status", "role": "assistant", "content": [{"type": "output_text", "text", "annotations": []}]} y {"type": "function_call", "id": "fc_...", "call_id", "name", "arguments", "status": "completed"}
usage objeto input_tokens (con la caché incluida), input_tokens_details.cached_tokens, output_tokens, output_tokens_details.reasoning_tokens (el recuento de razonamiento del proveedor cuando lo informa, si no 0), total_tokens
previous_response_id cadena o null lo que enviaste
conversation objeto o null {"id": "conv_..."}; null solo cuando store es false y no se indicó conversación
instructions cadena o null lo que enviaste
metadata objeto lo que enviaste, o {}
store booleano lo que se aplicó
max_output_tokens entero o null lo que se aplicó; null en GET /v1/responses/{id}
tools, tool_choice, parallel_tool_calls lo que se aplicó; tool_choice es auto por defecto. GET devuelve [], auto y true
temperature, top_p número presentes cuando los enviaste
incomplete_details objeto o null {"reason": "max_output_tokens"} cuando status es incomplete
error null siempre null en este endpoint; los errores son errores HTTP o un evento error del stream
zabio objeto el objeto zabio de la respuesta, con escalated_from y compacted rellenos cuando aplican

Con stream: true el cuerpo es text/event-stream con líneas event: y data:. Cada carga lleva type y un sequence_number que empieza en 0. La secuencia de una respuesta de texto es response.created, response.in_progress, response.output_item.added, response.content_part.added, response.output_text.delta (repetido), response.output_text.done, response.content_part.done, response.output_item.done, y después response.completed (o response.incomplete, response.failed) con el objeto response completo bajo response. Las llamadas a herramientas usan response.output_item.added, response.function_call_arguments.delta (repetido), response.function_call_arguments.done, response.output_item.done. Un fallo del proveedor después de empezar el stream envía event: error con {"type": "error", "code", "message", "param": null}.

Los eventos delta llevan output_index, item_id, content_index (solo texto) y delta. Los ids de elemento dentro de un stream son msg_<id de la response>_<índice> y fc_<id de la response>_<índice>; los ids guardados que devuelven response.completed y GET son msg_ y fc_ seguidos de 32 caracteres hexadecimales.

El mismo mecanismo que en chat completions: tras 90 segundos sin respuesta, una llamada sin streaming confirma su 200 y escribe un espacio cada 20 segundos hasta el objeto; un fallo posterior es el objeto con status: "failed" y error: {"code", "message"}. Un stream envía comentarios : keepalive cada 20 segundos mientras el proveedor abre o calla, antes de response.created si hace falta; un fallo antes de cualquier salida es un evento response.failed con ese objeto.

Estado code Cuándo
400 missing_field falta input, o el role, content o call_id de un elemento; param lo nombra
400 invalid_value un campo está fuera de rango; incluye previous_response_id y conversation que no son ids, zabio.retention_days, zabio.compaction, un input sin ningún mensaje user ni salida de herramienta, y background con stream o con store: false
400 background_unavailable las responses en segundo plano no están disponibles en este servidor
402 insufficient_credits en una response en segundo plano destinada a un lote: su coste estimado supera el saldo disponible; no se guarda nada
400 conversation_mismatch previous_response_id pertenece a otra conversación
400 conversation_too_large el hilo alcanzó 4000 entradas o 2000000 de tokens estimados
400 context_length_exceeded el historial más este turno no cabe y la compactación está desactivada, es imposible (store: false, sin conversación) o no basta; el mensaje lleva las cifras
403 insufficient_scope, conversations_closed la key no tiene conversations; las conversaciones guardadas están cerradas para la organización
404 previous_response_not_found, conversation_not_found desconocido o borrado
503 conversations_unavailable las conversaciones guardadas no están disponibles en este servidor

Todo lo listado para chat completions se aplica también: model_not_found, insufficient_credits, la familia 429 y los errores de proveedor.

Dos turnos, el segundo continuando desde el primero.

curl

Terminal window
curl https://api.zabio.ai/v1/responses \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "Content-Type: application/json" \
-d '{"model": "auto", "input": "Me llamo Ana. Recuérdalo.", "zabio": {"retention_days": 7}}'
# después, con el id de la primera respuesta:
curl https://api.zabio.ai/v1/responses \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "Content-Type: application/json" \
-d '{"model": "auto", "input": "¿Cómo me llamo?", "previous_response_id": "resp_..."}'

Python

first = client.responses.create(model="auto", input="Me llamo Ana. Recuérdalo.",
extra_body={"zabio": {"retention_days": 7}})
second = client.responses.create(model="auto", input="¿Cómo me llamo?", previous_response_id=first.id)
print(second.output_text)
print(second.conversation.id, second.model_extra["zabio"]["model_served"])

Node

const first = await client.responses.create({ model: "auto", input: "Me llamo Ana. Recuérdalo.", zabio: { retention_days: 7 } });
const second = await client.responses.create({ model: "auto", input: "¿Cómo me llamo?", previous_response_id: first.id });
console.log(second.output_text);
console.log(second.conversation.id, second.zabio.model_served);

Un turno en streaming y la lectura del objeto final.

curl

Terminal window
curl -N https://api.zabio.ai/v1/responses \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "Content-Type: application/json" \
-d '{"model": "auto", "input": "Cuenta hasta tres.", "stream": true}'

Python

with client.responses.stream(model="auto", input="Cuenta hasta tres.") as stream:
for event in stream:
if event.type == "response.output_text.delta":
print(event.delta, end="", flush=True)
final = stream.get_final_response()
print("\n", final.status, final.model_extra["zabio"])

Node

const stream = client.responses.stream({ model: "auto", input: "Cuenta hasta tres." });
for await (const event of stream) {
if (event.type === "response.output_text.delta") process.stdout.write(event.delta);
}
const final = await stream.finalResponse();
console.log("\n", final.status, final.zabio);

Devuelve una response guardada con su salida. model es el modelo que la sirvió. Errores: 404 response_not_found cuando el id está mal formado, no existe, se borró o se creó con store: false.

curl

Terminal window
curl https://api.zabio.ai/v1/responses/resp_... \
-H "Authorization: Bearer insert your zabio.ai key here"

Python

r = client.responses.retrieve("resp_...")
print(r.status, r.output_text)

Node

const r = await client.responses.retrieve("resp_...");
console.log(r.status, r.output_text);

Cancela una response en segundo plano. En cola, o todavía esperando a que se envíe su lote: la response termina cancelled y no se factura nada. Ejecutándose como trabajo: la llamada al proveedor se corta y la response termina cancelled. Ya enviada al proveedor del modelo dentro de un lote: 409 not_cancellable, la respuesta llega y se factura. Ya terminada: el objeto tal cual. Devuelve el objeto response. Errores: 400 not_background cuando la response no se creó con background; 404 response_not_found.

curl

Terminal window
curl -X POST https://api.zabio.ai/v1/responses/resp_.../cancel \
-H "Authorization: Bearer inserta aquí tu key de zabio.ai"

Python

r = client.responses.cancel("resp_...")
print(r.status) # cancelled

Node

const r = await client.responses.cancel("resp_...");
console.log(r.status); // cancelled

Borra la response con su turno (las entradas que guardó y sus salidas) y todo lo guardado después en el mismo hilo. La conversación continúa desde donde empezaba ese turno. Devuelve {"id": "resp_...", "object": "response", "deleted": true}. Errores: 404 response_not_found.

curl

Terminal window
curl -X DELETE https://api.zabio.ai/v1/responses/resp_... \
-H "Authorization: Bearer insert your zabio.ai key here"

Python

client.responses.delete("resp_...")

Node

await client.responses.delete("resp_...");

Crea una conversación vacía o con contenido inicial. El cuerpo puede ir vacío.

Campo Tipo Obligatorio Notas
metadata objeto no hasta 16 claves de cadena
items array no elementos con las mismas formas que input en POST /v1/responses; se guardan sin turno de modelo
zabio.retention_days entero no de 0 a 365; por defecto la retención de la organización

Respuesta, que también devuelven GET y POST /v1/conversations/{id}:

Campo Tipo Notas
id cadena conv_ seguido de 32 caracteres hexadecimales
object cadena conversation
created_at entero segundos Unix
metadata objeto
zabio.entries entero elementos guardados, todos los hilos
zabio.token_estimate entero tokens estimados de los elementos guardados
zabio.expires_at cadena RFC 3339; cuándo se borra la conversación salvo que un turno nuevo reinicie el reloj
zabio.last_turn_at cadena RFC 3339
zabio.retention_days entero presente cuando la conversación tiene su propia retención

Errores: 400 invalid_json, 400 invalid_value en metadata o zabio.retention_days, 400 missing_field o invalid_value en un elemento incorrecto.

curl

Terminal window
curl https://api.zabio.ai/v1/conversations \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "Content-Type: application/json" \
-d '{"metadata": {"customer": "acme"}, "items": [{"role": "user", "content": "Hola"}], "zabio": {"retention_days": 30}}'

Python

conv = client.conversations.create(metadata={"customer": "acme"},
items=[{"role": "user", "content": "Hola"}],
extra_body={"zabio": {"retention_days": 30}})
print(conv.id, conv.model_extra["zabio"]["expires_at"])

Node

const conv = await client.conversations.create({
metadata: { customer: "acme" },
items: [{ role: "user", content: "Hola" }],
zabio: { retention_days: 30 },
});
console.log(conv.id, conv.zabio.expires_at);

Devuelve el objeto conversación de arriba. Errores: 404 conversation_not_found.

curl

Terminal window
curl https://api.zabio.ai/v1/conversations/conv_... \
-H "Authorization: Bearer insert your zabio.ai key here"

Python

conv = client.conversations.retrieve("conv_...")
print(conv.model_extra["zabio"]["entries"])

Node

const conv = await client.conversations.retrieve("conv_...");
console.log(conv.zabio.entries);

Sustituye los metadatos de la conversación con {"metadata": {...}} (hasta 16 claves) y devuelve el objeto conversación. Errores: 400 invalid_json, 400 invalid_value, 404 conversation_not_found.

curl

Terminal window
curl https://api.zabio.ai/v1/conversations/conv_... \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "Content-Type: application/json" \
-d '{"metadata": {"customer": "acme", "ticket": "4711"}}'

Python

conv = client.conversations.update("conv_...", metadata={"customer": "acme", "ticket": "4711"})

Node

const conv = await client.conversations.update("conv_...", { metadata: { customer: "acme", ticket: "4711" } });

Borra la conversación y todo lo que contiene, de inmediato y de forma permanente. Devuelve {"id": "conv_...", "object": "conversation.deleted", "deleted": true}. Errores: 404 conversation_not_found.

curl

Terminal window
curl -X DELETE https://api.zabio.ai/v1/conversations/conv_... \
-H "Authorization: Bearer insert your zabio.ai key here"

Python

client.conversations.delete("conv_...")

Node

await client.conversations.delete("conv_...");

Lista todos los elementos guardados en orden de creación, de todos los hilos, incluidos los resúmenes de compactación.

Parámetro de consulta Por defecto Notas
limit 20 de 1 a 100
after un id de elemento (msg_..., fc_..., fco_..., cmp_...) a partir del cual continuar
order asc asc o desc

Respuesta: {"object": "list", "data": [...], "has_more", "first_id", "last_id"}. Elementos:

type Forma
message {"id": "msg_...", "role", "status": "completed", "content": [...]}. El contenido de user, system y developer son partes input_text e input_image; el del asistente son partes output_text
function_call {"id": "fc_...", "call_id", "name", "arguments", "status"}
function_call_output {"id": "fco_...", "call_id", "output", "status": "completed"}
compaction {"id": "cmp_...", "summary"}: el resumen que sustituyó al historial antiguo

Errores: 400 invalid_value en limit o after, 404 conversation_not_found.

curl

Terminal window
curl "https://api.zabio.ai/v1/conversations/conv_.../items?limit=50&order=asc" \
-H "Authorization: Bearer insert your zabio.ai key here"

Python

page = client.conversations.items.list("conv_...", limit=50)
for item in page.data:
print(item.type, getattr(item, "role", ""))

Node

const page = await client.conversations.items.list("conv_...", { limit: 50 });
for (const item of page.data) console.log(item.type, item.role ?? "");

Añade elementos sin ejecutar un turno de modelo, por ejemplo para cargar historial de otro sistema. Cuerpo: {"items": [...]} con al menos un elemento en las formas de input. Devuelve {"object": "list", "data": [los elementos guardados], "has_more": false}. Errores: 400 missing_field (items), 400 conversation_too_large, 404 conversation_not_found.

curl

Terminal window
curl https://api.zabio.ai/v1/conversations/conv_.../items \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "Content-Type: application/json" \
-d '{"items": [{"role": "user", "content": "Pregunta anterior"}, {"role": "assistant", "content": "Respuesta anterior"}]}'

Python

client.conversations.items.create("conv_...", items=[
{"role": "user", "content": "Pregunta anterior"},
{"role": "assistant", "content": "Respuesta anterior"},
])

Node

await client.conversations.items.create("conv_...", { items: [
{ role: "user", content: "Pregunta anterior" },
{ role: "assistant", content: "Respuesta anterior" },
] });

Sube un archivo con el formato de OpenAI, multipart/form-data. Hoy el único purpose es batch: un archivo JSONL de peticiones de chat completions para Lotes. Los archivos se cifran por organización y se conservan durante el periodo de retención de la organización desde la subida, o hasta que se borran, o hasta la caducidad que fijes. Necesita el scope chat.

Parte Obligatoria Notas
file sí el archivo; 100 MiB como máximo
purpose sí batch
expires_after[anchor] no created_at
expires_after[seconds] no de 3600 a 2592000; acorta la retención

Una entrada de lote se comprueba línea por línea al subirla: cada línea es un objeto JSON con un custom_id único, method POST, una url y un objeto body; una línea puede ocupar hasta 10 MiB.

Campo Tipo Notas
id cadena file_...
object cadena file
bytes entero el tamaño
created_at, expires_at entero segundos Unix; expires_at es la retención o la caducidad que fijaste, lo que ocurra antes
filename, purpose cadena como se subieron
status cadena processed
Estado code Cuándo
400 invalid_multipart, missing_field no es multipart, falta la parte file o falta purpose
400 invalid_value un purpose distinto de batch, o un expires_after no válido
400 invalid_file el JSONL no es válido; el mensaje nombra la línea
413 file_too_large más de 100 MiB

Lista tus archivos, los más recientes primero. purpose filtra; limit (de 1 a 10000) y after (un id de archivo) paginan. Devuelve {"object": "list", "data": [archivo...], "first_id", "last_id", "has_more"}.

Devuelve el objeto de archivo, o 404 not_found.

Borra el contenido de inmediato y de forma permanente; devuelve {"id", "object": "file", "deleted": true}. Un archivo que un lote en marcha todavía necesita responde 409 file_in_use.

Envía los bytes del archivo en streaming, con Content-Disposition nombrando el archivo.

curl

Terminal window
curl https://api.zabio.ai/v1/files \
-H "Authorization: Bearer inserta aquí tu key de zabio.ai" \
-F purpose=batch -F file=@requests.jsonl
curl https://api.zabio.ai/v1/files/file_.../content \
-H "Authorization: Bearer inserta aquí tu key de zabio.ai" -o output.jsonl

Python

f = client.files.create(file=open("requests.jsonl", "rb"), purpose="batch")
print(client.files.content(f.id).text)

Crea un lote a partir de un archivo subido: cada línea se enruta a los precios por lotes de los proveedores y se responde en menos de 24 horas. Consulta Lotes para el flujo completo, los modelos y el dinero. Necesita el scope chat.

Campo Tipo Obligatorio Notas
input_file_id cadena sí una subida procesada con purpose batch, de 50,000 líneas como máximo
endpoint cadena sí /v1/chat/completions
completion_window cadena no 24h, el único valor
metadata objeto no hasta 16 pares de cadenas; claves de 64 caracteres como máximo, valores de 512 como máximo
zabio.allow_standard_price booleano no por defecto false. true ejecuta las líneas que nombran un modelo sin nivel de lotes como llamadas síncronas al precio estándar en lugar de hacer fallar el lote; cada una de esas líneas dice zabio.tier.applied: "standard" con fallback: "no_batch_tier". Ver Líneas que Zabio responde directamente

El objeto de lote:

Campo Tipo Notas
id cadena batch_...
object cadena batch
endpoint, input_file_id, completion_window cadena como se crearon
status cadena validating, failed, in_progress, finalizing, completed, expired, cancelling, cancelled
output_file_id, error_file_id cadena o null rellenos cuando el lote termina; el archivo de errores solo cuando alguna línea falló
errors objeto en un lote fallido: {"object": "list", "data": [{"code", "message", "param", "line"}]}
created_at, expires_at, in_progress_at, finalizing_at, completed_at, failed_at, expired_at, cancelling_at, cancelled_at entero segundos Unix; expires_at es 24 horas después de la creación
request_counts objeto total, completed, failed (líneas fallidas, caducadas y canceladas)
metadata objeto como se creó
zabio objeto tier (batch) y cost: vendor_micros, billed_micros, direct_micros, saved_micros, classifier_micros (siempre 0), los totales acumulados de las líneas facturadas hasta el momento. saved_micros suma el saved_micros de cada línea, así que una línea auto en una key con la referencia pendiente no suma nada
Estado code Cuándo
400 missing_field falta input_file_id o endpoint
400 invalid_value endpoint distinto de /v1/chat/completions, completion_window distinto de 24h, metadata demasiado grande, el archivo no es una subida de lote procesada
400 batch_too_large más de 50,000 líneas
404 not_found no hay archivo con ese id
429 too_many_open_batches la organización ya tiene 20 lotes abiertos

Un lote ya creado puede fallar antes de ejecutarse, con status: failed y los motivos en errors: model_not_batchable (una línea nombra un modelo sin nivel de lotes y zabio.allow_standard_price no está activado; el mensaje lista los modelos con nivel de lotes y la opción), model_not_found, no_batch_model, insufficient_credits (la estimación y el saldo disponible van en el mensaje), api_key_revoked, empty_file, input_file_missing, o un código de petición de una línea (invalid_url, invalid_request, missing_messages, invalid_json). En un lote fallido no se factura nada.

Lista tus lotes, los más recientes primero; limit (de 1 a 100, 20 por defecto) y after (un id de lote) paginan. Devuelve {"object": "list", "data": [lote...], "first_id", "last_id", "has_more"}.

Devuelve el objeto de lote, o 404 not_found.

Cancela las líneas que aún no están en un proveedor y pide a los proveedores que paren el resto; lo que terminen se conserva y se factura. Devuelve el objeto de lote con estado cancelling. Un lote que ya ha terminado responde 400 batch_not_cancellable.

curl

Terminal window
curl https://api.zabio.ai/v1/batches \
-H "Authorization: Bearer inserta aquí tu key de zabio.ai" \
-H "Content-Type: application/json" \
-d '{"input_file_id": "file_...", "endpoint": "/v1/chat/completions", "completion_window": "24h"}'
curl https://api.zabio.ai/v1/batches/batch_... -H "Authorization: Bearer inserta aquí tu key de zabio.ai"

Python

b = client.batches.create(input_file_id=f.id, endpoint="/v1/chat/completions", completion_window="24h")
b = client.batches.retrieve(b.id)
print(b.status, b.request_counts, b.model_extra["zabio"]["cost"])

Node

const b = await client.batches.create({ input_file_id: f.id, endpoint: "/v1/chat/completions", completion_window: "24h" });
console.log((await client.batches.retrieve(b.id)).status);

Lee una petición de chat completions y dice cuánto cuesta hoy, cuánto costaría a través de Zabio y cómo reducirla, sin ejecutarla. Es el motor del analizador de zabio.ai y de los consejos que el Playground muestra tras cada llamada. Pública: sin API key. Con límite de tasa por dirección, además del límite general por IP, y con un tope diario para todos.

La única llamada a un proveedor es el clasificador de enrutado de Zabio, que lee el mismo extracto que lee en cualquier llamada auto (ver Privacidad); ningún modelo responde a la petición y Zabio paga la clasificación. La petición nunca se guarda: Zabio registra su tamaño, la tarea que nombró el clasificador y un hash de la dirección del cliente, nada más.

Campo Tipo Obligatorio Notas
request objeto sí un cuerpo de chat completions, exactamente como lo enviarías a POST /v1/chat/completions, con la misma validación
calls_per_month entero no proyecta el mes en monthly y se informa en el consejo de lotes

Un cuerpo de más de 1 MB responde 413 payload_too_large. Los campos desconocidos se ignoran.

Campo Tipo Notas
job objeto la lectura del clasificador: task, difficulty (1 a 5), stakes (normal o high)
tokens objeto input (estimado, caracteres entre cuatro), output (lo que asume cada precio) y output_basis: typical para la tarea, o max_tokens cuando el tope de la petición es menor
today objeto o null la petición en el modelo que nombra, al precio de lista del proveedor: model, vendor, usd. Null cuando la petición nombra auto o un modelo que Zabio no conoce
lines[] array una fila por modelo virtual: line, el model y vendor que elegiría (con la misma elección que hace una petición auto real, ver Modelos y enrutado), provider_usd, fee_usd (la comisión de servicio), call_fee_usd (la cantidad fija de una petición auto, el precio de la elección automática del modelo), classifier_usd (siempre 0), total_usd y saving_usd frente a today, o frente a la elección de auto cuando no hay today
normal_stakes objeto solo cuando el clasificador leyó la tarea como de alto riesgo y eso movió las líneas. Una tarea de alto riesgo sube cada línea un escalón, así que las filas de arriba muestran lo que haría una llamada real. Este campo dice qué elegiría auto/cheap y cuánto costaría por llamada si el riesgo fuera normal: model, vendor, total_usd, con los mismos tokens y frente a la misma referencia que las filas. Ausente cuando el riesgo no cambió las elecciones
switches[] array las seis palancas, siempre en este orden: split, cache, effort, jev, flex, batch. Cada una con id, applicable (si la palanca puede cambiar el precio de esta petición), default (el del producto: split, cache, effort y jev activadas, flex y batch desactivadas) y status (active o coming_soon)
scenarios objeto las 64 combinaciones de las palancas. La clave es un 0 o 1 por palanca en el orden de switches, así que 111100 son los valores por defecto y 000000 todas desactivadas. Cada valor es {"total_usd", "saving_usd"}: la factura por llamada a través de Zabio en la elección de auto con esas palancas activas, calculadas juntas para que un ahorro solapado nunca se cuente dos veces, y el ahorro frente a today, o frente a 000000 cuando no hay today. Con flex y batch activas a la vez gana batch. Los lotes se calculan con el nivel de lotes del catálogo, el precio que cobra la API de lotes del proveedor. effort no cambia hoy ningún precio, porque los tokens de razonamiento aún no tienen precio, así que sus escenarios coinciden con los que no lo tienen
tips[] array cada consejo solo cuando aplica: id, status (active o coming_soon), saving_usd (por llamada, o null cuando el ahorro no se puede poner en precio) y params, solo números y nombres, listados abajo
monthly objeto o null con calls_per_month: calls, today_usd (null sin today) y zabio_usd en la línea auto

Todos los importes son USD por llamada a precios de lista, con seis decimales.

El sitio y el Playground redactan cada consejo a partir de sus parámetros; la API no envía frases. Significado y unidad de cada parámetro:

id Cuándo Parámetro Tipo Significado
line una línea más barata cuya elección la base de conocimiento no considera probablemente peor que la de la línea best, con ahorro line cadena la línea a la que enviar la petición: auto/cheap, auto/balanced o auto/best
model cadena el modelo que elegiría esa línea
vendor cadena su proveedor
total_usd número la factura por llamada en esa línea, USD
max_tokens el max_tokens de la petición es al menos cuatro veces la salida típica de la tarea y al menos 1.000; saving_usd es null, la factura sigue a la salida real max_tokens entero el tope que envió la petición, tokens
typical_output entero la salida que suele necesitar la tarea, tokens
cache el system prompt más el historial anterior al último mensaje llega al prefijo mínimo cacheable del modelo elegido, en un proveedor con caché; saving_usd es por llamada repetida model cadena el modelo en el que se cachearía el prefijo
vendor cadena su proveedor
prefix_tokens entero el prefijo estable, tokens
min_prefix_tokens entero el prefijo mínimo que ese proveedor cachea, tokens
ttl cadena la vida de la caché calculada (5m)
first_call_extra_usd número lo que paga de más la primera llamada por escribir la caché, USD (0 cuando el proveedor no cobra recargo de escritura)
jev el json_schema encaja en las preguntas de Jev y Jev sale más barato que el modelo elegido model cadena jev-1.13.0
fields entero los campos tipados del esquema
jev_usd número el precio de Jev para la petición, USD
pick_model cadena el modelo de chat que usaría la línea en su lugar
pick_usd número el precio de ese modelo con la salida del esquema, USD
split una fuente grande en texto plano con una pregunta, en una tarea que no es trabajo de documento entero, donde la puerta y la comprobación de precio de Smart Splitting (la división inteligente de peticiones de Zabio) pasan con la estimación model cadena el modelo que responde sobre el extracto
extractor cadena el modelo barato que copia las líneas relevantes
source_tokens entero el último mensaje del usuario, tokens
unsplit_usd número la petición enviada entera al modelo elegido, USD
split_usd número el extracto, la comprobación y la respuesta sobre el extracto, USD
flex el modelo elegido tiene nivel flex model cadena el modelo elegido
vendor cadena su proveedor
deadline cadena el zabio.deadline más corto que pide flex (15m)
standard_usd número la llamada en el nivel estándar, USD
flex_usd número la misma llamada en el nivel flex, USD
batch active. El modelo elegido tiene nivel de lotes; el ahorro es la petición a su precio por lotes frente a su precio estándar (consulta Lotes) model cadena el modelo elegido
vendor cadena su proveedor
discount número el descuento por lotes como fracción del precio estándar (0.5 es el 50 por ciento)
window cadena la ventana de finalización del proveedor (24h)
standard_usd número la llamada en el nivel estándar, USD
calls_per_month entero el volumen que traía la petición, 0 si ninguno
effort el modelo nombrado razona, la tarea es fácil o media con riesgo normal, y el nivel en vigor está por encima del más bajo del modelo; saving_usd es null porque los tokens de razonamiento aún no tienen precio model cadena el modelo nombrado
current cadena el nivel de razonamiento en vigor, como lo informa zabio.reasoning.applied
suggested cadena el nivel más bajo del modelo, en la misma forma
suggested_level cadena el valor de reasoning_effort que lo selecciona, cuando el nivel está en la escala
requested cadena el reasoning_effort que envió la petición, vacío si ninguno
Estado code Cuándo
400 missing_parameter, invalid_value y los errores de una petición de chat completions el cuerpo o la petición que contiene no es válida
413 payload_too_large el cuerpo supera 1 MB
429 ip_rate_limit_exceeded demasiados análisis desde una dirección; espera el Retry-After
503 analyzer_daily_cap el tope diario para todos se ha agotado; regístrate para ejecutar la llamada de verdad
503 analyzer_unavailable el analizador no puede funcionar ahora mismo

curl

Terminal window
curl https://api.zabio.ai/v1/public/analyze \
-H "Content-Type: application/json" \
-d '{
"request": {
"model": "claude-opus-5-5",
"messages": [{"role": "user", "content": "Hola, tengo una pregunta sobre un cargo duplicado en mi factura."}]
},
"calls_per_month": 2000
}'

La respuesta tiene la misma forma que en la versión en inglés de esta página; los valores reales de la comprobación en vivo están en la nota de construcción 32.

El estado público del servicio, el mismo documento que muestra la página zabio.ai/es/status. No hace falta API key; es una de las dos rutas públicas bajo /v1, junto con POST /v1/public/analyze. El límite de tasa por IP sigue aplicándose.

Respuesta: {"status", "updated_at", "source", "components": [Component, ...]}.

Campo Tipo Notas
status cadena operational, degraded, major_outage, o unknown cuando source es unavailable. Gana el peor componente, salvo que un proveedor cuenta como degraded como máximo, porque las llamadas pasan a otro proveedor
updated_at cadena Hora RFC 3339 en que se calcularon los números. El documento se recalcula cada 30 segundos
source cadena live; stale cuando no se pudo consultar el backend de métricas y se sirve el último documento bueno (de hasta 5 minutos); unavailable cuando no hay nada utilizable
components[].id cadena api, vendor:anthropic, vendor:openai, vendor:google, vendor:deepseek, vendor:xai, vendor:mistral, vendor:together, vendor:typesafe, web
components[].name cadena Nombre para mostrar
components[].status cadena operational con menos del 2 por ciento de peticiones fallidas en los últimos 15 minutos, degraded con menos del 20 por ciento, major_outage por encima. Sin peticiones en la ventana es operational con tasa null
components[].success_rate_15m número o null Proporción de peticiones sin fallo en los últimos 15 minutos (0.995 es el 99.5 por ciento). No está en la fila web
components[].success_rate_24h número o null Lo mismo en las últimas 24 horas
components[].p95_latency_ms entero o null Percentil 95 de la duración de las peticiones en los últimos 15 minutos. No está en la fila web

El documento no contiene nada sobre tu organización ni sobre tus llamadas.

curl

Terminal window
curl https://api.zabio.ai/v1/status
{
"status": "operational",
"updated_at": "2026-09-21T12:00:00Z",
"source": "live",
"components": [
{"id": "api", "name": "API", "status": "operational", "success_rate_15m": 0.999, "success_rate_24h": 0.998, "p95_latency_ms": 420},
{"id": "vendor:anthropic", "name": "Anthropic", "status": "operational", "success_rate_15m": 0.998, "success_rate_24h": 0.997, "p95_latency_ms": 2100},
{"id": "vendor:openai", "name": "OpenAI", "status": "operational", "success_rate_15m": null, "success_rate_24h": 0.999, "p95_latency_ms": null},
{"id": "vendor:google", "name": "Google", "status": "operational", "success_rate_15m": 1, "success_rate_24h": 1, "p95_latency_ms": 1300},
{"id": "web", "name": "Web app", "status": "operational"}
]
}

Todo lo que Zabio añade sobre los formatos de OpenAI, con un ejemplo de cada una. El SDK de Python rechaza los argumentos que no conoce, así que zabio viaja en extra_body; el SDK de Node deja pasar los campos desconocidos.

auto nombra una línea, cuánta habilidad debe tener el modelo: auto a secas (o sin modelo) y auto/cheap toman el modelo más barato con habilidad suficiente para el tipo de trabajo de la tarea, auto/balanced el más barato cuya habilidad para él está en el tercio central de los candidatos o por encima, y auto/best el más barato del tercio superior; una petición difícil o de riesgo alto sube un escalón. Todas las líneas clasifican primero la tarea (tipo de trabajo, dificultad, riesgo) y aplican su mínimo a los modelos activos para la key (ver Modelos y enrutado). Estos cuatro son los únicos ids virtuales. zabio.routing.trigger es explicit con un modelo nombrado.

curl

Terminal window
curl https://api.zabio.ai/v1/chat/completions \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "Content-Type: application/json" \
-d '{"model": "auto/balanced", "messages": [{"role": "user", "content": "Refactoriza esta función para quitar los bucles anidados: ..."}]}'

Python

r = client.chat.completions.create(model="auto/balanced", messages=[{"role": "user", "content": "Refactoriza esta función: ..."}])
print(r.model_extra["zabio"]["routing"]) # {'line': 'balanced', 'task': 'conversation', 'difficulty': 1, ...}

Node

const r = await client.chat.completions.create({ model: "auto/balanced", messages: [{ role: "user", content: "Refactoriza esta función: ..." }] });
console.log(r.zabio.routing); // { line: 'balanced', floor: 'middle', task: 'conversation', difficulty: 1, ... }

Líneas: auto/best, auto/balanced, auto/cheap

Section titled “Líneas: auto/best, auto/balanced, auto/cheap”

Nombra la línea en model. No hay nada más que ajustar: el clasificador nombra la tarea y la línea aplica su mínimo. Una tarea difícil, o de riesgo alto (una respuesta equivocada puede causar daño legal, médico, financiero o de seguridad, o cambia un sistema en producción), sube un escalón: cheap al tercio central, balanced al tercio superior. zabio.routing.line muestra la línea que nombraste y zabio.routing.floor el mínimo aplicado. La línea va en la petición: no hay ningún ajuste de la key para ella.

Terminal window
curl https://api.zabio.ai/v1/chat/completions -H "Authorization: Bearer $ZABIO_API_KEY" -H "Content-Type: application/json" \
-d '{"model": "auto/best", "messages": [{"role": "user", "content": "Resume esta cláusula del contrato: ..."}]}'
r = client.chat.completions.create(model="auto/cheap", messages=[{"role": "user", "content": "Resume esta cláusula del contrato: ..."}])
print(r.model_extra["zabio"]["routing"]["line"], r.model_extra["zabio"]["model_served"])
const r = await client.chat.completions.create({ model: "auto/cheap", messages: [{ role: "user", content: "Resume esta cláusula del contrato: ..." }] });
console.log(r.zabio.routing.line, r.zabio.model_served);

Envía el mismo session_id (hasta 128 caracteres) en las llamadas que van juntas. Lo que se reutiliza es la tarea que el clasificador nombró para el mensaje de usuario actual de la sesión: los turnos de resultado de herramienta de un bucle de agente informan trigger: "cache_hit" y no se clasifican de nuevo; cada petición sigue pagando una vez la cantidad por la elección automática del modelo. Un mensaje nuevo del usuario se clasifica de nuevo (trigger: "new_human_message"), igual que cualquier turno tras una señal (low_confidence_prior, long_idle, tool_failure_streak, customer_feedback). Sin session_id, Zabio basa la reutilización en tu prompt de sistema y el último mensaje del usuario, así que un bucle de agente sigue clasificando una vez por mensaje de usuario. El estado de sesión son solo metadatos: línea, modelo, confianza, unos pocos contadores y los últimos 200 caracteres del último mensaje del usuario para la señal de feedback. Vive una hora desde la última llamada. Los prompts nunca se guardan para ello. La respuesta repite el id en zabio.session_id.

curl

Terminal window
curl https://api.zabio.ai/v1/chat/completions \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "x-zabio-session-id: soporte-4711" \
-H "Content-Type: application/json" \
-d '{"model": "auto", "messages": [{"role": "user", "content": "Segunda pregunta en la misma sesión"}]}'

Python

for question in ["Primera pregunta", "Segunda pregunta"]:
r = client.chat.completions.create(model="auto", messages=[{"role": "user", "content": question}],
extra_body={"zabio": {"session_id": "soporte-4711"}})
print(r.model_extra["zabio"]["routing"]["trigger"]) # no_prior_decision, después cache_hit

Node

for (const question of ["Primera pregunta", "Segunda pregunta"]) {
const r = await client.chat.completions.create({ model: "auto", messages: [{ role: "user", content: question }], zabio: { session_id: "soporte-4711" } });
console.log(r.zabio.routing.trigger); // no_prior_decision, después cache_hit
}

Caché de prompt donde el proveedor la ofrece. cache.ttl es 5m, 1h o none; cache.breakpoints es auto o none.

Proveedor Qué hacen los valores
Anthropic auto coloca marcadores de caché en el system prompt, en la última herramienta, en el último mensaje y en el turno anterior del usuario, de modo que el historial de la conversación se lee de la caché en cada turno y solo se escribe la parte nueva. ttl elige la caché de 5 minutos o la de 1 hora; la de 1 hora cuesta más escribirla (2 veces el precio de entrada frente a 1.25 veces). Anthropic no guarda en caché prefijos más cortos que el mínimo del modelo (de 512 a 4096 tokens según el modelo), así que los prompts cortos informan 0
OpenAI la caché es automática en prefijos de 1024 tokens o más; ttl se acepta y se ignora
Google la caché es automática (implícita) en prompts de 4096 tokens o más, sin coste de escritura; las lecturas de caché se cobran al precio de caché de Google y se informan en zabio.cache.read_tokens; ttl y breakpoints se aceptan y se ignoran
Otros proveedores cuando el modelo tiene un precio de caché publicado (DeepSeek, Mistral y los modelos de DeepSeek en Together, por ejemplo), la caché es automática y las lecturas se cobran a ese precio; ttl se acepta y se ignora

breakpoints: "none" desactiva la caché para la llamada y fuerza ttl a none. La caché es automática en cada llamada, nombre auto o un modelo; una llamada la desactiva con ttl: "none", breakpoints: "none" o la cabecera. El ttl por defecto es 5m, la duración que se usa cuando Zabio escribe (las keys ya no tienen un valor por defecto propio). Un prompt largo (1,024 tokens o más) se escribe en la caché en su segunda aparición, no en la primera: la primera vez se envía sin escritura, y cuando el mismo prompt, o el mismo system prompt con las mismas herramientas, vuelve dentro de la vida de la caché, se escribe y las llamadas siguientes lo leen. Un prompt enviado una sola vez nunca paga la escritura. Las llamadas de una session_id, las peticiones que llevan turnos anteriores de la conversación y las peticiones que fijan cache.ttl se escriben enseguida. En la Responses API una respuesta guardada (lo habitual), una conversation y un previous_response_id se escriben enseguida. La regla importa en Anthropic, que cobra las escrituras; los demás proveedores guardan en caché por su cuenta. Con ese valor por defecto, una llamada de una session_id cuyo turno anterior fue hace más de cinco minutos recibe 1h, porque una caché de 5 minutos habría caducado y se reescribiría en cada turno, lo que cuesta más que no usar caché. Lee el resultado en zabio.cache: read_tokens, write_tokens, ttl_applied. La cabecera x-zabio-cache-ttl lleva ttl; no hay cabecera para breakpoints. Una línea dentro de un lote, y una respuesta en segundo plano servida desde el pool, recibe none salvo que la propia línea fije ttl: nada vuelve a leer la entrada de caché de una línea de lote, y la escritura cuesta más que la entrada normal.

curl

Terminal window
curl https://api.zabio.ai/v1/chat/completions \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "Content-Type: application/json" \
-d '{"model": "claude-sonnet-5", "messages": [{"role": "system", "content": "<un system prompt largo>"}, {"role": "user", "content": "Pregunta"}], "zabio": {"cache": {"ttl": "1h", "breakpoints": "auto"}}}'

Python

r = client.chat.completions.create(model="claude-sonnet-5",
messages=[{"role": "system", "content": long_system_prompt}, {"role": "user", "content": "Pregunta"}],
extra_body={"zabio": {"cache": {"ttl": "1h", "breakpoints": "auto"}}})
print(r.model_extra["zabio"]["cache"]) # {'read_tokens': 0, 'write_tokens': 5120, 'ttl_applied': '1h'} en la primera llamada

Node

const r = await client.chat.completions.create({ model: "claude-sonnet-5",
messages: [{ role: "system", content: longSystemPrompt }, { role: "user", content: "Pregunta" }],
zabio: { cache: { ttl: "1h", breakpoints: "auto" } } });
console.log(r.zabio.cache); // { read_tokens: 5120, write_tokens: 0, ttl_applied: '1h' } en la segunda llamada

vendors limita los candidatos a los proveedores que listes (anthropic, openai, google, deepseek, xai, mistral, together). Se aplica a auto y al conjunto de fallback de un modelo explícito.

fallback se aplica a los modelos nombrados explícitamente cuando el modelo no está sano, no está disponible o no pasa una comprobación de capacidades: same_vendor (por defecto, del fallback_policy de la key) prueba otros modelos del mismo proveedor y clase, cross_vendor cualquier modelo de la misma clase, none falla con 503 no_provider_available. Pase lo que pase, zabio.model_served y zabio.vendor te dicen qué respondió y zabio.attempts cuántos intentos hicieron falta.

curl

Terminal window
curl https://api.zabio.ai/v1/chat/completions \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-5.4", "messages": [{"role": "user", "content": "Hola"}], "zabio": {"vendors": ["openai", "anthropic"], "fallback": "cross_vendor"}}'

Python

r = client.chat.completions.create(model="gpt-5.4", messages=[{"role": "user", "content": "Hola"}],
extra_body={"zabio": {"vendors": ["openai", "anthropic"], "fallback": "cross_vendor"}})
z = r.model_extra["zabio"]
print(z["vendor"], z["model_served"], z.get("attempts"))

Node

const r = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hola" }],
zabio: { vendors: ["openai", "anthropic"], fallback: "cross_vendor" } });
console.log(r.zabio.vendor, r.zabio.model_served, r.zabio.attempts);

cost_micros es lo que se te facturó; cost lo desglosa. direct_micros es lo que habría costado la misma llamada en tu modelo de referencia con la caché que tuvo: para un modelo con nombre, ese mismo modelo, así que es igual al precio del proveedor; para auto, el modelo de referencia de tu key con los mismos tokens, lecturas de caché y escrituras de caché a las tarifas propias de ese modelo, calculado sin caché donde su proveedor no tiene caché de prompt (Google, Together AI). Un descuento de caché nunca cuenta como ahorro: nunca nos quedamos con una parte de tus descuentos de caché ni de batch. El modelo de referencia es el modelo que tu key usó en el percentil 95 del precio de lista por token en los últimos 30 días, recalculada cada noche: el 5 por ciento de llamadas más caras se aparta y la siguiente la fija, así que un pico no la sube. La regla necesita 20 llamadas correctas antes de decir nada, porque por debajo de 20 el 5 por ciento apartado son cero llamadas y una sola llamada cara fijaría la referencia ella sola. Hasta entonces las llamadas de una key nueva son su propia referencia y no ahorran nada: saved_micros no aparece y cost.baseline muestra el progreso; la referencia se fija normalmente en la hora siguiente a la llamada 20. Esas llamadas siguen sin ahorro una vez fijada la referencia: el panel, Uso, la exportación CSV, la CLI y los totales de lotes no cuentan ninguno para ellas, así que cada total suma las llamadas tal como se mostró cada una. classifier_micros siempre vale 0: el clasificador que lee una petición auto nunca se repercute. Antes de la versión 1.2.6 llevaba el precio del proveedor de esa llamada. saved_micros es direct_micros menos cost_micros, y nunca es negativo: cuando una llamada costó más que su referencia (un modelo más caro en la línea best, o la cantidad fija por llamada en una llamada diminuta), indica 0 con saving_note: "no saving on this call", y todos los totales la cuentan como 0; direct_micros frente a cost_micros muestra la diferencia. service_fee_micros es el porcentaje del plan sobre vendor_micros, y call_fee_micros es la cantidad fija que paga una vez una petición auto, el precio de la elección automática del modelo (consulta Facturación). savings_fee_micros siempre vale 0 desde la versión 1.2.7: Zabio no se queda con ninguna parte del ahorro. Divide entre 1000000 para tener dólares.

curl

Terminal window
curl -s https://api.zabio.ai/v1/chat/completions \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "Content-Type: application/json" \
-d '{"model": "auto", "messages": [{"role": "user", "content": "Di hola."}]}' | jq .zabio.cost

Python

r = client.chat.completions.create(model="auto", messages=[{"role": "user", "content": "Di hola."}])
z = r.model_extra["zabio"]
print(f"facturado ${z['cost_micros'] / 1e6:.6f}, directo ${z['cost']['direct_micros'] / 1e6:.6f}, ahorrado ${z['cost'].get('saved_micros', 0) / 1e6:.6f}")

Node

const r = await client.chat.completions.create({ model: "auto", messages: [{ role: "user", content: "Di hola." }] });
const { cost_micros, cost } = r.zabio;
console.log(`facturado $${cost_micros / 1e6}, directo $${cost.direct_micros / 1e6}, ahorrado $${(cost.saved_micros ?? 0) / 1e6}`);

Con auto, una petición que lleva una fuente grande (un documento, un log, un hilo, un conjunto de registros) y una pregunta que solo necesita una parte de ella puede responderse en dos pasos cuando sale más barato: un modelo barato copia las líneas relevantes de forma literal, una comprobación confirma que el extracto contiene lo que la pregunta nombra, y el modelo enrutado responde sobre el extracto. La respuesta lleva entonces zabio.split.applied: true, zabio.split.steps lista cada llamada con su modelo y su precio de lista, y zabio.cost cubre la petición entera (vendor_micros son las dos llamadas, cost_micros la petición completa). Cuando falla alguna salvaguarda, la petición va entera al modelo enrutado, zabio.split.fallback nombra la salvaguarda, y pagas exactamente el precio de la llamada entera. Un modelo con nombre nunca se divide. zabio.split: "off" (o la cabecera x-zabio-split: off) envía una petición entera; no hay valor por defecto a nivel de key. Detalles en Modelos y enrutado y Facturación.

curl

Terminal window
curl -s https://api.zabio.ai/v1/chat/completions \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "Content-Type: application/json" \
-d @peticion.json | jq '.zabio.split'

peticion.json contiene "model": "auto" y un mensaje de usuario largo con la fuente y la pregunta; añádele "zabio": {"split": "off"}, o la cabecera -H "x-zabio-split: off", para enviar la petición entera.

Python

r = client.chat.completions.create(model="auto", messages=[{"role": "user", "content": contrato + "\n\nPREGUNTA: ¿cuál es el plazo de preaviso para la resolución?"}])
s = r.model_extra["zabio"].get("split") or {}
print(s.get("applied"), s.get("reason") or s.get("fallback"), [(st["role"], st["model"], st["micros"]) for st in s.get("steps", [])])
# La misma petición enviada entera:
r = client.chat.completions.create(model="auto", messages=[{"role": "user", "content": contrato + "\n\nPREGUNTA: ¿cuál es el plazo de preaviso para la resolución?"}],
extra_body={"zabio": {"split": "off"}})
print(r.model_extra["zabio"]["split"]["reason"]) # opt_out

Node

const r = await client.chat.completions.create({ model: "auto", messages: [{ role: "user", content: `${contrato}\n\nPREGUNTA: ¿cuál es el plazo de preaviso para la resolución?` }] });
const s = r.zabio.split ?? {};
console.log(s.applied, s.reason ?? s.fallback, (s.steps ?? []).map((st) => [st.role, st.model, st.micros]));
// La misma petición enviada entera:
const entera = await client.chat.completions.create({ model: "auto", messages: [{ role: "user", content: `${contrato}\n\nPREGUNTA: ¿cuál es el plazo de preaviso para la resolución?` }],
zabio: { split: "off" } });
console.log(entera.zabio.split.reason); // opt_out

Cuando tu petición puede esperar, dilo con zabio.deadline (o la cabecera x-zabio-deadline), una duración de 15 minutos o más. Zabio sirve entonces la llamada en el nivel flex del proveedor cuando el modelo lo tiene (OpenAI gpt-5.4-nano, gpt-5-mini, gpt-5.4-mini, gpt-5.4, gpt-5.5, gpt-6-astra; Google gemini-3.5-flash-lite, gemini-3.8-flash, gemini-3.5-flash, gemini-3.1-pro-preview): el mismo modelo a la mitad de precio, más o menos, más despacio y con la capacidad que haya. Con auto, los candidatos se comparan a su precio flex, así que auto puede elegir un modelo porque sale más barato en flex. Cuando el proveedor rechaza una petición flex por falta de capacidad, Zabio reintenta al momento el mismo modelo en el nivel estándar y factura el precio que se usó de verdad; una petición flex nunca falla por ese motivo. zabio.tier indica requested, applied y, tras un rechazo, fallback: "capacity". Un intento flex puede tardar hasta 900 segundos, así que ajusta el timeout de tu cliente. El ahorro se mide contra el precio flex del modelo de referencia de tu key, o contra su precio estándar cuando ese modelo no tiene nivel flex: Zabio nunca se queda una parte del descuento del proveedor. Un plazo de 24 horas o más pide el nivel de lotes, que solo una response en segundo plano o un lote pueden esperar; una llamada síncrona con ese plazo se sirve en flex (o estándar) y zabio.tier indica fallback: "synchronous". Detalles en Chat completions y Facturación.

curl

Terminal window
curl -s https://api.zabio.ai/v1/chat/completions \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "Content-Type: application/json" \
-H "x-zabio-deadline: 30m" \
-d '{"model": "auto", "messages": [{"role": "user", "content": "Resume el informe adjunto."}]}' | jq '.zabio.tier'

Python

r = client.chat.completions.create(model="auto", messages=[{"role": "user", "content": "Resume el informe adjunto."}],
extra_body={"zabio": {"deadline": "30m"}}, timeout=900)
tier = r.model_extra["zabio"]["tier"]
print(tier["requested"], tier["applied"], tier.get("fallback")) # flex flex None, o flex standard capacity

Node

const r = await client.chat.completions.create({ model: "auto", messages: [{ role: "user", content: "Resume el informe adjunto." }],
zabio: { deadline: "30m" } }, { timeout: 900_000 });
console.log(r.zabio.tier.requested, r.zabio.tier.applied, r.zabio.tier.fallback); // flex flex undefined, o flex standard capacity

Cuando tu organización ha añadido su propia key del proveedor que sirvió la llamada, la respuesta lo dice: zabio.byok es true y la cabecera x-zabio-byok: true está presente. zabio.cost.vendor_micros es 0 porque Zabio no tiene coste de proveedor (el proveedor te factura directamente) y cost_micros es 0 en un modelo con nombre y la cantidad por la elección automática del modelo en una petición auto; direct_micros sigue relleno para que las cifras sean comparables. Las llamadas que tus propias keys pueden servir por completo no se rechazan por falta de créditos. Si el proveedor rechaza tu key, la llamada falla con 400 byok_key_invalid y Zabio nunca recurre a su propia key. Consulta Claves propias.

curl

Terminal window
curl -si https://api.zabio.ai/v1/chat/completions \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "Content-Type: application/json" \
-d '{"model": "claude-sonnet-5", "messages": [{"role": "user", "content": "Hola"}]}' | grep -i x-zabio-byok

Python

r = client.chat.completions.create(model="claude-sonnet-5", messages=[{"role": "user", "content": "Hola"}])
print(r.model_extra["zabio"].get("byok", False))

Node

const r = await client.chat.completions.create({ model: "claude-sonnet-5", messages: [{ role: "user", content: "Hola" }] });
console.log(r.zabio.byok ?? false);

En los turnos auto de POST /v1/responses con tool_choice auto o ausente, Zabio da al modelo una herramienta oculta. Cuando el modelo la llama para decir que la tarea le supera, Zabio vuelve a ejecutar el mismo turno en un modelo más potente, con todo el historial, y devuelve esa respuesta: el modelo más barato un escalón más arriba en el mínimo de habilidad para el tipo de trabajo, o, en una petición enrutada con la regla anterior, el siguiente modelo de la curva que su conocimiento dice que es probablemente mejor. La respuesta lleva entonces zabio.escalated_from como línea/modelo (por ejemplo cheap/gpt-5.4-nano), zabio.routing.trigger es escalation, y los turnos siguientes de la conversación se quedan en el modelo más potente. Cuando no hay un modelo más potente al que ir (el mínimo superior, o nada probablemente mejor con la regla anterior), la herramienta no se ofrece. La llamada oculta nunca aparece en output. El escalado no se ofrece en /v1/chat/completions, en modelos explícitos ni cuando tool_choice es none, required o una función concreta.

curl

Terminal window
curl -s https://api.zabio.ai/v1/responses \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "Content-Type: application/json" \
-d '{"model": "auto", "input": "Demuestra que hay infinitos números primos y explica cada paso."}' | jq '.zabio | {model_served, escalated_from, routing}'

Python

r = client.responses.create(model="auto", input="Demuestra que hay infinitos números primos y explica cada paso.")
z = r.model_extra["zabio"]
if z.get("escalated_from"):
print("escalado desde", z["escalated_from"], "a", z["model_served"])

Node

const r = await client.responses.create({ model: "auto", input: "Demuestra que hay infinitos números primos y explica cada paso." });
if (r.zabio.escalated_from) console.log("escalado desde", r.zabio.escalated_from, "a", r.zabio.model_served);

Cuando el historial guardado más un turno nuevo no cabría en la ventana de contexto del modelo que sirve el turno, Zabio resume la parte más antigua con un modelo rápido, guarda el resumen como elemento compaction y continúa; la respuesta lleva zabio.compacted: true y la llamada del resumen aparece en la facturación de la conversación. El resumen se ejecuta con la key de Zabio, así que un turno que requiere compactación necesita crédito aunque nombre un modelo servido con tu propia key; con saldo cero recibe 402 insufficient_credits antes de que se haga ningún resumen. zabio.compaction: "off" lo desactiva y la petición falla con 400 context_length_exceeded. zabio.retention_days (de 0 a 365) fija la retención de una conversación creada por la llamada; el reloj se reinicia en cada turno.

curl

Terminal window
curl https://api.zabio.ai/v1/responses \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "Content-Type: application/json" \
-d '{"model": "auto", "input": "Continúa", "conversation": "conv_...", "zabio": {"compaction": "off"}}'

Python

r = client.responses.create(model="auto", input="Continúa", conversation="conv_...",
extra_body={"zabio": {"compaction": "off"}})
print(r.model_extra["zabio"].get("compacted", False))

Node

const r = await client.responses.create({ model: "auto", input: "Continúa", conversation: "conv_...", zabio: { compaction: "off" } });
console.log(r.zabio.compacted ?? false);

Algunos modelos no admiten todos los parámetros de muestreo: los modelos de razonamiento de OpenAI no admiten temperature ni top_p, los modelos Claude recientes tampoco, y los modelos Gemini ignoran ambos. Zabio nunca hace fallar una petición por eso. Omite el parámetro en la petición al modelo que responde, lo nombra en zabio.dropped_parameters y en la cabecera x-zabio-dropped-params, y la respuesta llega como siempre. Con auto depende del modelo elegido, así que lee el campo en lugar de suponerlo. stop nunca se omite: donde el proveedor del modelo no lo admite, Zabio corta la respuesta en la primera secuencia, con streaming o sin él. La lista por modelo está en la página de Modelos.

Los campos que Zabio acepta para que los valores por defecto de los SDK no fallen, pero sobre los que no actúa, aparecen en zabio.ignored_parameters. En chat completions: seed, logit_bias, presence_penalty y frequency_penalty, solo cuando llevan un valor distinto del predeterminado. En la Responses API: include, truncation y user, siempre que estén presentes. La lista se omite cuando está vacía.

curl

Terminal window
curl -s https://api.zabio.ai/v1/chat/completions \
-H "Authorization: Bearer insert your zabio.ai key here" \
-H "Content-Type: application/json" \
-d '{"model": "auto", "messages": [{"role": "user", "content": "Hola"}], "seed": 42, "presence_penalty": 0.5}' | jq .zabio.ignored_parameters

Python

r = client.chat.completions.create(model="auto", messages=[{"role": "user", "content": "Hola"}], seed=42, presence_penalty=0.5)
print(r.model_extra["zabio"].get("ignored_parameters")) # ['seed', 'presence_penalty']

Node

const r = await client.chat.completions.create({ model: "auto", messages: [{ role: "user", content: "Hola" }], seed: 42, presence_penalty: 0.5 });
console.log(r.zabio.ignored_parameters); // [ 'seed', 'presence_penalty' ]

Se rechazan con 400 unsupported_parameter:

  • n mayor que 1; Zabio sirve exactamente una opción
  • logprobs y top_logprobs
  • salida audio y cualquier entrada de modalities distinta de text
  • prediction y web_search_options
  • el rol de mensaje antiguo function (usa tool)
  • partes de contenido distintas de text e image_url en chat completions; elementos de entrada distintos de message, function_call y function_call_output, y partes de contenido distintas de input_text, text, output_text e input_image en la Responses API
  • herramientas distintas de type: "function" (400 invalid_value)

Se aceptan y se ignoran:

  • seed, logit_bias, presence_penalty, frequency_penalty en chat completions (aparecen en zabio.ignored_parameters)
  • stream_options.include_usage: el chunk de uso se envía siempre
  • store en chat completions: no se guarda nada en ningún caso
  • include, truncation, user en la Responses API (aparecen en zabio.ignored_parameters); background sí se atiende (ver Responses en segundo plano)
  • image_url.detail: las imágenes se envían siempre como bytes
  • cache.ttl en modelos de OpenAI: allí la caché es automática

Se comportan distinto:

  • model puede ser auto, auto/best, auto/balanced o auto/cheap; el model de la respuesta es el modelo que respondió, no el que enviaste
  • cada respuesta lleva un objeto zabio, y los objetos de modelo llevan zabio con clase, capacidades y precios
  • los mensajes system y developer se unen en un solo system prompt
  • usage.prompt_tokens incluye los tokens en caché; prompt_tokens_details.cached_tokens es la parte en caché
  • response_format: json_object en modelos de Anthropic es un mecanismo de mejor esfuerzo mediante prompting, consulta Chat completions
  • la Responses API guarda por defecto y crea una conversación cuando no se indica ninguna; store: false sigue leyendo el historial guardado pero no conserva nada
  • max_output_tokens en la Responses API es 4096 por defecto; max_tokens en chat completions es 16000 por defecto
  • reasoning_effort y reasoning.effort usan una sola escala, de none a xhigh, aplicada al control propio de cada modelo; zabio.reasoning indica lo que se aplicó, y con auto sin nivel Zabio elige el ajuste junto con el modelo
  • los ids de elemento de la Responses API en streaming son msg_<id de la response>_<índice> hasta que response.completed informa los ids guardados
  • Zabio descarga las imágenes y las envía al proveedor como bytes; las direcciones privadas e internas se rechazan
  • los errores mantienen el formato de OpenAI pero usan los códigos de Zabio (model_not_found, insufficient_credits, quota_exceeded, upstream_error y el resto en Errores); el cuerpo de error del proveedor nunca te llega
  • el id de petición va en la cabecera x-request-id, nunca en el cuerpo, y uno que envíes tú no se reutiliza
  • no hay claves de idempotencia, ni fine tuning, ni assistants, ni endpoints de generación de audio o imágenes; /v1/files acepta solo el purpose batch y /v1/batches solo el endpoint /v1/chat/completions