AI Gateway
Chat Completions
POST /v1/chat/completions — el endpoint de chat compatible con OpenAI de Clipia AI Gateway. Respuestas con streaming y normales, parámetros, roles, herramientas y errores.
POST /v1/chat/completions es el endpoint principal de Clipia AI Gateway, compatible con OpenAI Chat Completions. La clave necesita el permiso generate, que tienen todas las claves, así que no necesita una clave aparte para el chat. Recibe un array messages y devuelve la respuesta del asistente en modo sin streaming o con streaming, con soporte para tool calling y salida estructurada mediante esquema JSON.
/v1/chat/completionsRespuesta normal
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["CLIPIA_KEY"],
base_url="https://api.clipia.ai/v1",
)
resp = client.chat.completions.create(
model="claude-opus-5-5",
messages=[
{"role": "system", "content": "You are a concise assistant."},
{"role": "user", "content": "Tell me three facts about Mars."},
],
max_tokens=512,
)
print(resp.choices[0].message.content)
print(resp.usage)import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.CLIPIA_KEY,
baseURL: "https://api.clipia.ai/v1",
});
const resp = await client.chat.completions.create({
model: "claude-opus-5-5",
messages: [
{ role: "system", content: "You are a concise assistant." },
{ role: "user", content: "Tell me three facts about Mars." },
],
max_tokens: 512,
});
console.log(resp.choices[0].message.content);curl https://api.clipia.ai/v1/chat/completions \
-H "Authorization: Bearer $CLIPIA_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-opus-5-5",
"messages": [
{ "role": "system", "content": "You are a concise assistant." },
{ "role": "user", "content": "Tell me three facts about Mars." }
],
"max_tokens": 512
}'Respuesta 200
{
"id": "chatcmpl-3f9a1c7e2b41",
"object": "chat.completion",
"created": 1782300000,
"model": "claude-opus-5-5",
"provider": "Clipia",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "1. ...\n2. ...\n3. ..." },
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 28,
"completion_tokens": 64,
"total_tokens": 92,
"cost": 0.046
}
}usage.cost es el coste de la petición en créditos. El id de la respuesta tiene la forma chatcmpl-…, y provider siempre es "Clipia". finish_reason se normaliza al enum de OpenAI: stop, length, tool_calls, content_filter.
Respuesta con streaming
Pase stream: true: la respuesta llega de forma incremental como Server-Sent Events. Cada evento es un chat.completion.chunk con un delta; el stream termina con el literal data: [DONE].
stream = client.chat.completions.create(
model="claude-opus-5-5",
messages=[{"role": "user", "content": "Write a haiku about the sea."}],
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta
if delta.content:
print(delta.content, end="", flush=True)const stream = await client.chat.completions.create({
model: "claude-opus-5-5",
messages: [{ role: "user", content: "Write a haiku about the sea." }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}curl -N https://api.clipia.ai/v1/chat/completions \
-H "Authorization: Bearer $CLIPIA_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-opus-5-5",
"messages": [{ "role": "user", "content": "Write a haiku about the sea." }],
"stream": true
}'Flujo de eventos
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":1782300000,"model":"claude-opus-5-5","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","model":"claude-opus-5-5","choices":[{"index":0,"delta":{"content":"Waves "},"finish_reason":null}]}
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","model":"claude-opus-5-5","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]Uso final en un stream
Para recibir usage durante el streaming, pase stream_options: { "include_usage": true }. En ese caso se envía un chunk adicional con usage completo y choices: [] vacío justo antes de data: [DONE].
Parámetros de la petición
Prop
Type
Compatibilidad de parámetros
Los campos n, parallel_tool_calls, logprobs, top_logprobs, logit_bias, service_tier y metadata se aceptan, pero no afectan a la respuesta: siempre recibe una sola opción. Los campos desconocidos de nivel superior tampoco provocan un 400, de modo que el código escrito para OpenAI funciona sin cambios.
La compatibilidad de los parámetros depende del modelo: consulte supported_parameters en GET /v1/models. Los modelos actuales pueden ignorar temperature, top_p y las penalizaciones por repetición. DeepSeek V4.1 Flash no llama a herramientas. Para una salida estricta por esquema JSON, elija un modelo con response_format, como GPT-6.1 Sol. Claude 5.5 sigue las instrucciones de salida estructurada sin garantizar una conformidad estricta con el esquema.
Roles de los mensajes
| Rol | Cómo se trata |
|---|---|
system | instrucción de sistema |
developer | se trata como system |
user | mensaje del usuario |
assistant | una respuesta anterior del modelo, incluida una con tool_calls |
tool | el resultado de una llamada a herramienta; requiere tool_call_id |
| cualquier otro | se trata como user |
Un array messages vacío, un mensaje sin role de tipo cadena o una petición sin model devuelven 400 invalid_request_error.
Reintentos de peticiones
Este endpoint no admite la cabecera Idempotency-Key: un reintento tras un fallo de red ejecuta y cobra la petición de nuevo. El chat está sujeto a los límites de frecuencia y de concurrencia comunes.
Llamadas a herramientas
Ciclo completo: usted describe las herramientas en tools, el modelo devuelve tool_calls, usted ejecuta la función por su parte y devuelve el resultado como un mensaje con role: "tool", tras lo cual el modelo genera la respuesta final.
import json
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Current weather in a city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name"},
},
"required": ["city"],
},
},
}
]
messages = [{"role": "user", "content": "What's the weather in Moscow right now?"}]
# 1) The model decides to call a tool
resp = client.chat.completions.create(
model="claude-opus-5-5", messages=messages, tools=tools,
)
msg = resp.choices[0].message
# resp.choices[0].finish_reason == "tool_calls"
# 2) Run the function on your side
call = msg.tool_calls[0]
args = json.loads(call.function.arguments) # {"city": "Moscow"}
result = {"temp_c": 14, "condition": "cloudy"}
# 3) Send the result back and get the final answer
messages.append(msg) # assistant echo with tool_calls
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result),
})
final = client.chat.completions.create(
model="claude-opus-5-5", messages=messages, tools=tools,
)
print(final.choices[0].message.content)Con streaming, los fragmentos de tool_calls llegan agrupados por index: el nombre de la función y el id llegan en el primer delta de ese índice y, después, los fragmentos de function.arguments, que usted concatena hasta formar un JSON válido. Una llamada a herramienta completa se marca con finish_reason: "tool_calls".
Salidas estructuradas
Para obligar a que la respuesta se ajuste a un esquema JSON, pase response_format con type: "json_schema" y strict: true.
resp = client.chat.completions.create(
model="gpt-6.1-sol",
messages=[{"role": "user", "content": "Extract name and age: Anna is 30."}],
response_format={
"type": "json_schema",
"json_schema": {
"name": "person",
"strict": True,
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
},
"required": ["name", "age"],
"additionalProperties": False,
},
},
},
)
print(resp.choices[0].message.content) # {"name": "Anna", "age": 30}json_object frente a json_schema
response_format: { "type": "json_object" } garantiza un JSON válido, pero sin un esquema concreto, y exige que la palabra «json» aparezca en messages. Para una conformidad estructural estricta, utilice json_schema con strict: true.
Errores
Los errores usan el sobre de OpenAI { "error": { "message", "type", "param", "code" } }, siempre con Content-Type: application/json y el estado HTTP correspondiente.
| Estado | code | type | Cuándo |
|---|---|---|---|
400 | invalid_request_error | invalid_request_error | cuerpo mal formado: sin model, messages vacío, esquema JSON no válido en response_format |
400 | context_length_exceeded | invalid_request_error | la entrada supera el contexto del modelo; se comprueba antes de cobrar nada |
401 | invalid_api_key | invalid_request_error | la clave falta, es incorrecta o está revocada |
402 | insufficient_quota | insufficient_quota | no hay créditos suficientes en el saldo |
402 | subscription_required | insufficient_quota | el modelo requiere una suscripción activa |
402 | insufficient_quota | insufficient_quota | se ha agotado el presupuesto de créditos de la clave para el periodo: consulte Límites de frecuencia |
403 | insufficient_scope o permission_error | invalid_request_error | a la clave le falta un permiso o la petición no está permitida |
404 | model_not_found | invalid_request_error | model desconocido o no disponible |
422 | model_input_invalid | invalid_request_error | el modelo rechazó la petición por su contenido; reformule la entrada |
429 | rate_limit_exceeded o concurrency_limit_exceeded | rate_limit_error | se ha superado el límite de peticiones, de tokens o de concurrencia |
500, 502, 503, 504 | null | api_error | fallo temporal del modelo; reintente más tarde |
{
"error": {
"message": "Insufficient credits. Please top up your balance.",
"type": "insufficient_quota",
"param": null,
"code": "insufficient_quota"
}
}En los errores 5xx el mensaje siempre es genérico: "The upstream model is temporarily unavailable. Please retry." para 502, 503 y 504, y "An internal error occurred. Please retry or contact support." para 500.
Errores dentro de un stream
Si el fallo se produce cuando el stream ya ha empezado, el estado HTTP ya es 200. En ese caso, el último chunk antes de data: [DONE] incluye finish_reason: "error" y un objeto error:
{
"id": "chatcmpl-...",
"object": "chat.completion.chunk",
"choices": [{ "index": 0, "delta": {}, "finish_reason": "error" }],
"error": { "message": "The model timed out. Please retry.", "code": "upstream_timeout", "type": "api_error" }
}code | Qué ha ocurrido |
|---|---|
upstream_error | el modelo no respondió |
upstream_timeout | se agotó el tiempo de espera del modelo |
service_unavailable | el modelo no está disponible temporalmente |
empty_response | el modelo no devolvió ninguna salida utilizable |
incomplete_stream | el stream del modelo terminó antes de tiempo |
invalid_structured_output | la salida no se ajusta al response_format solicitado |
model_input_invalid | el modelo rechazó la petición por su contenido |
Compruebe finish_reason en el último chunk: los clientes del SDK de OpenAI no siempre convierten ese chunk en una excepción. Los errores que se producen antes de que empiece el stream, como 402 o 404, llegan como JSON normal con un estado HTTP.
Clipia AI Gateway
Una única API compatible con OpenAI para Claude, GPT, Gemini, DeepSeek y Grok, que se paga con créditos de Clipia. Obtenga una clave y envíe su primera solicitud en un par de minutos.
Modelos y precios
El catálogo de LLM de Clipia AI Gateway en GET /v1/models y los precios en dólares estadounidenses por 1M de tokens de entrada y de salida — Claude, GPT, Gemini, DeepSeek y Grok.