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.
Conceptos básicos
Section titled “Conceptos básicos”URL base
Section titled “URL base”| 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" });Autenticación
Section titled “Autenticación”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 |
Versiones
Section titled “Versiones”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.
Ids de petición
Section titled “Ids de petición”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.
Cabeceras de límite de tasa
Section titled “Cabeceras de límite de tasa”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 |
Otras cabeceras en cada llamada
Section titled “Otras cabeceras en cada llamada”| 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ó |
Idempotencia
Section titled “Idempotencia”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.
Límites
Section titled “Límites”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.
Formato de error
Section titled “Formato de error”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.
GET /v1/models
Section titled “GET /v1/models”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
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);GET /v1/models/{id}
Section titled “GET /v1/models/{id}”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
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);GET /v1/public/free-tier
Section titled “GET /v1/public/free-tier”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.
curl https://api.zabio.ai/v1/public/free-tierGET /v1/public/auto-models
Section titled “GET /v1/public/auto-models”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"]}curl https://api.zabio.ai/v1/public/auto-modelsPOST /v1/chat/completions
Section titled “POST /v1/chat/completions”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.
Petición
Section titled “Petición”| 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 |
El objeto zabio de la petición
Section titled “El objeto zabio de la petición”| 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>.
Respuesta
Section titled “Respuesta”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 objeto zabio de la respuesta
Section titled “El objeto zabio de la respuesta”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 |
Tipos de trabajo
Section titled “Tipos de trabajo”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 |
Streaming
Section titled “Streaming”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:
- un chunk con
delta: {"role": "assistant", "content": ""} - chunks de contenido con
delta.content, y chunks de llamadas a herramientas condelta.tool_calls[]que llevanindex,id,typeyfunction.nameen el primer chunk de cada llamada y fragmentos defunction.argumentsdespués - un chunk con
deltavacío yfinish_reason - un chunk de uso con
choices: [], el bloqueusagey el objetozabio 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.
Llamadas largas
Section titled “Llamadas largas”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.
Errores propios de este endpoint
Section titled “Errores propios de este endpoint”| 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 |
Ejemplos
Section titled “Ejemplos”Una llamada normal con auto, y después la lectura de qué modelo la sirvió y cuánto costó.
curl
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
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
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);POST /v1/embeddings
Section titled “POST /v1/embeddings”Embeddings con el formato de OpenAI. Los modelos son siempre explícitos; no hay auto. Necesita el scope embeddings.
Petición
Section titled “Petición”| 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.
Respuesta
Section titled “Respuesta”| 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 |
Errores propios de este endpoint
Section titled “Errores propios de este endpoint”| 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 |
Ejemplos
Section titled “Ejemplos”curl
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);POST /v1/responses
Section titled “POST /v1/responses”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.
Petición
Section titled “Petición”| 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 |
El objeto zabio de la petición
Section titled “El objeto zabio de la petición”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í.
Respuesta
Section titled “Respuesta”| 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 |
Streaming
Section titled “Streaming”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.
Llamadas largas
Section titled “Llamadas largas”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.
Errores propios de este endpoint
Section titled “Errores propios de este endpoint”| 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.
Ejemplos
Section titled “Ejemplos”Dos turnos, el segundo continuando desde el primero.
curl
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
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);GET /v1/responses/{id}
Section titled “GET /v1/responses/{id}”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
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);POST /v1/responses/{id}/cancel
Section titled “POST /v1/responses/{id}/cancel”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
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) # cancelledNode
const r = await client.responses.cancel("resp_...");console.log(r.status); // cancelledDELETE /v1/responses/{id}
Section titled “DELETE /v1/responses/{id}”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
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_...");POST /v1/conversations
Section titled “POST /v1/conversations”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
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);GET /v1/conversations/{id}
Section titled “GET /v1/conversations/{id}”Devuelve el objeto conversación de arriba. Errores: 404 conversation_not_found.
curl
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);POST /v1/conversations/{id}
Section titled “POST /v1/conversations/{id}”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
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" } });DELETE /v1/conversations/{id}
Section titled “DELETE /v1/conversations/{id}”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
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_...");GET /v1/conversations/{id}/items
Section titled “GET /v1/conversations/{id}/items”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
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 ?? "");POST /v1/conversations/{id}/items
Section titled “POST /v1/conversations/{id}/items”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
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" },] });POST /v1/files
Section titled “POST /v1/files”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.
Petición
Section titled “Petición”| 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.
Respuesta
Section titled “Respuesta”| 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 |
Errores específicos de este endpoint
Section titled “Errores específicos de este endpoint”| 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 |
GET /v1/files
Section titled “GET /v1/files”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"}.
GET /v1/files/{id}
Section titled “GET /v1/files/{id}”Devuelve el objeto de archivo, o 404 not_found.
DELETE /v1/files/{id}
Section titled “DELETE /v1/files/{id}”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.
GET /v1/files/{id}/content
Section titled “GET /v1/files/{id}/content”Envía los bytes del archivo en streaming, con Content-Disposition nombrando el archivo.
Ejemplos
Section titled “Ejemplos”curl
curl https://api.zabio.ai/v1/files \ -H "Authorization: Bearer inserta aquí tu key de zabio.ai" \ -F purpose=batch -F file=@requests.jsonlcurl https://api.zabio.ai/v1/files/file_.../content \ -H "Authorization: Bearer inserta aquí tu key de zabio.ai" -o output.jsonlPython
f = client.files.create(file=open("requests.jsonl", "rb"), purpose="batch")print(client.files.content(f.id).text)POST /v1/batches
Section titled “POST /v1/batches”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.
Petición
Section titled “Petición”| 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 |
Respuesta
Section titled “Respuesta”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 |
Errores específicos de este endpoint
Section titled “Errores específicos de este endpoint”| 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.
GET /v1/batches
Section titled “GET /v1/batches”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"}.
GET /v1/batches/{id}
Section titled “GET /v1/batches/{id}”Devuelve el objeto de lote, o 404 not_found.
POST /v1/batches/{id}/cancel
Section titled “POST /v1/batches/{id}/cancel”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.
Ejemplos
Section titled “Ejemplos”curl
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);POST /v1/public/analyze
Section titled “POST /v1/public/analyze”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.
Petición
Section titled “Petición”| 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.
Respuesta
Section titled “Respuesta”| 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.
Los consejos y sus parámetros
Section titled “Los consejos y sus parámetros”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
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.
GET /v1/status
Section titled “GET /v1/status”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
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"} ]}Extensiones de Zabio
Section titled “Extensiones de Zabio”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.
Los modelos auto
Section titled “Los modelos auto”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
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.
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);session_id
Section titled “session_id”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
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_hitNode
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}cache.ttl y cache.breakpoints
Section titled “cache.ttl y cache.breakpoints”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 |
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
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 llamadaNode
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 llamadavendors y fallback
Section titled “vendors y fallback”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
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);Leer zabio.cost
Section titled “Leer zabio.cost”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
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.costPython
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}`);División y enrutado en entradas grandes
Section titled “División y enrutado en entradas grandes”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
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_outNode
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_outEl nivel flex
Section titled “El nivel flex”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
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 capacityNode
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 capacitybyok en las respuestas
Section titled “byok en las respuestas”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
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-byokPython
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);Escalado en la Responses API
Section titled “Escalado en la Responses API”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
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);Compactación y retención
Section titled “Compactación y retención”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
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);dropped_parameters
Section titled “dropped_parameters”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.
ignored_parameters
Section titled “ignored_parameters”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
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_parametersPython
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' ]Diferencias con OpenAI
Section titled “Diferencias con OpenAI”Se rechazan con 400 unsupported_parameter:
nmayor que 1; Zabio sirve exactamente una opciónlogprobsytop_logprobs- salida
audioy cualquier entrada demodalitiesdistinta detext predictionyweb_search_options- el rol de mensaje antiguo
function(usatool) - partes de contenido distintas de
texteimage_urlen chat completions; elementos de entrada distintos demessage,function_callyfunction_call_output, y partes de contenido distintas deinput_text,text,output_texteinput_imageen la Responses API - herramientas distintas de
type: "function"(400invalid_value)
Se aceptan y se ignoran:
seed,logit_bias,presence_penalty,frequency_penaltyen chat completions (aparecen enzabio.ignored_parameters)stream_options.include_usage: el chunk de uso se envía siemprestoreen chat completions: no se guarda nada en ningún casoinclude,truncation,useren la Responses API (aparecen enzabio.ignored_parameters);backgroundsí se atiende (ver Responses en segundo plano)image_url.detail: las imágenes se envían siempre como bytescache.ttlen modelos de OpenAI: allí la caché es automática
Se comportan distinto:
modelpuede serauto,auto/best,auto/balancedoauto/cheap; elmodelde la respuesta es el modelo que respondió, no el que enviaste- cada respuesta lleva un objeto
zabio, y los objetos de modelo llevanzabiocon clase, capacidades y precios - los mensajes
systemydeveloperse unen en un solo system prompt usage.prompt_tokensincluye los tokens en caché;prompt_tokens_details.cached_tokenses la parte en cachéresponse_format: json_objecten 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: falsesigue leyendo el historial guardado pero no conserva nada max_output_tokensen la Responses API es 4096 por defecto;max_tokensen chat completions es 16000 por defectoreasoning_effortyreasoning.effortusan una sola escala, denoneaxhigh, aplicada al control propio de cada modelo;zabio.reasoningindica lo que se aplicó, y conautosin 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 queresponse.completedinforma 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_errory 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/filesacepta solo el purposebatchy/v1/batchessolo el endpoint/v1/chat/completions