Saltar al contenido
Proyecto IA

Documentación de la API

La API de Proyecto IA es compatible con la de OpenAI. Si ya usas su SDK o cualquier biblioteca que hable ese protocolo, solo tienes que cambiar la URL base y la clave.

URL base

Todas las rutas cuelgan de:

https://151-115-80-103.sslip.io/v1

Las peticiones y respuestas van en JSON con codificación UTF-8. Las rutas /health y /metrics existen para supervisión y no requieren clave.

Autenticación

Cada petición lleva tu clave en la cabecera Authorization con el esquema Bearer. Las claves empiezan por sk-ia- y se muestran una sola vez al crearlas: guardamos únicamente un resumen SHA-256, así que si la pierdes hay que revocarla y emitir otra.

Authorization: Bearer sk-ia-TU_CLAVE

No incluyas la clave en código que llegue al navegador ni la subas a un repositorio. Guárdala en una variable de entorno del servidor.

Listar modelos

GET /v1/models

Devuelve los modelos disponibles en tu plan. Los identificadores cambian con cada versión publicada; consúltalos antes de fijarlos en tu configuración.

curl https://151-115-80-103.sslip.io/v1/models \
  -H "Authorization: Bearer $CLAVE"

Respuesta:

{
  "object": "list",
  "data": [
    { "id": "qwen2.5:7b", "object": "model", "owned_by": "Proyecto IA" },
    { "id": "gemma3:4b",  "object": "model", "owned_by": "Proyecto IA" }
  ]
}

Chat

POST /v1/chat/completions

Recibe una conversación y devuelve la siguiente respuesta del modelo. Si no indicas model, se usa el modelo por defecto de tu plan.

model
Identificador del modelo, tal como lo devuelve /v1/models.
messages
Lista de mensajes con role (system, user o assistant) y content.
max_tokens
Máximo de tokens de salida. Con modelos que razonan internamente, un valor bajo puede devolver texto vacío.
temperature
De 0 a 2. Valores bajos dan respuestas más deterministas.
stream
true para recibir la respuesta por fragmentos (ver más abajo).
curl https://151-115-80-103.sslip.io/v1/chat/completions \
  -H "Authorization: Bearer $CLAVE" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen2.5:7b",
    "messages": [
      { "role": "system", "content": "Responde en español, de forma breve." },
      { "role": "user", "content": "Resume en una frase qué es la fotosíntesis." }
    ],
    "max_tokens": 200
  }'

Respuesta:

{
  "id": "chatcmpl-7f3a",
  "object": "chat.completion",
  "created": 1762080000,
  "model": "qwen2.5:7b",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "La fotosíntesis es el proceso por el que las plantas convierten luz, agua y CO2 en azúcares y oxígeno."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": { "prompt_tokens": 31, "completion_tokens": 27, "total_tokens": 58 }
}

Chat con streaming

Con "stream": true la respuesta llega como eventos server-sent events: una línea data: por fragmento y una última línea data: [DONE]. Usa -N en curl para que no almacene la salida.

curl -N https://151-115-80-103.sslip.io/v1/chat/completions \
  -H "Authorization: Bearer $CLAVE" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen2.5:7b",
    "stream": true,
    "stream_options": { "include_usage": true },
    "messages": [{ "role": "user", "content": "Cuenta hasta cinco en español." }]
  }'

Fragmentos recibidos:

data: {"id":"chatcmpl-7f3b","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","content":"Uno"},"finish_reason":null}]}

data: {"id":"chatcmpl-7f3b","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":", dos"},"finish_reason":null}]}

data: {"id":"chatcmpl-7f3b","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":14,"completion_tokens":11,"total_tokens":25}}

data: [DONE]

El campo usage llega en el último fragmento si pides stream_options.include_usage; es lo que se contabiliza para la factura.

Python

Instala el SDK oficial de OpenAI (pip install openai) y apunta base_url a nuestra API.

import os
from openai import OpenAI

cliente = OpenAI(
    base_url="https://151-115-80-103.sslip.io/v1",
    api_key=os.environ["CLAVE_IA"],
)

respuesta = cliente.chat.completions.create(
    model="qwen2.5:7b",
    messages=[
        {"role": "system", "content": "Eres un asistente que responde en español."},
        {"role": "user", "content": "¿Qué plazo tiene una pyme para presentar el IVA trimestral?"},
    ],
    max_tokens=300,
)
print(respuesta.choices[0].message.content)

Con streaming:

flujo = cliente.chat.completions.create(
    model="qwen2.5:7b",
    messages=[{"role": "user", "content": "Escribe un saludo breve."}],
    stream=True,
)
for fragmento in flujo:
    texto = fragmento.choices[0].delta.content if fragmento.choices else None
    if texto:
        print(texto, end="", flush=True)
print()

JavaScript

Con el paquete openai en Node 18 o superior. Nunca ejecutes este código en el navegador: la clave quedaría expuesta.

import OpenAI from "openai";

const cliente = new OpenAI({
  baseURL: "https://151-115-80-103.sslip.io/v1",
  apiKey: process.env.CLAVE_IA,
});

const respuesta = await cliente.chat.completions.create({
  model: "qwen2.5:7b",
  messages: [{ role: "user", content: "Explica en dos frases qué es un contrato de encargo de tratamiento." }],
  max_tokens: 200,
});
console.log(respuesta.choices[0].message.content);

Sin dependencias, con fetch:

const res = await fetch("https://151-115-80-103.sslip.io/v1/chat/completions", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.CLAVE_IA}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "qwen2.5:7b",
    messages: [{ role: "user", content: "Hola, ¿qué puedes hacer?" }],
  }),
});
const datos = await res.json();
console.log(datos.choices[0].message.content);

Errores

Los errores usan el mismo formato que OpenAI:

{ "error": { "message": "Clave inválida o revocada.", "type": "authentication_error" } }
Códigos de estado
CódigoSignificadoQué hacer
401Clave ausente, mal formada, inválida o revocada.Comprueba la cabecera Authorization y que la clave sigue activa.
429Has superado las peticiones por minuto de tu clave. La respuesta incluye la cabecera Retry-After con los segundos de espera.Espera lo que indica Retry-After y reintenta con retroceso exponencial. Si lo necesitas de forma habitual, pide un límite mayor.
503Ningún backend de inferencia responde en este momento.Reintenta pasados unos segundos. Si persiste, escríbenos a erspagos@gmail.com.
400Petición mal formada: JSON inválido, mensajes vacíos o modelo inexistente.Revisa el cuerpo de la petición con los ejemplos de esta página.

Límites

Residencia de datos