Tutorial
Cómo construir un proxy de la API de OpenAI con Python: primero escribe uno a mano y luego hazlo compatible con todos los modelos
Un proxy FastAPI funcional con autenticación, limitación de velocidad y streaming; y cómo la API compatible con OpenAI de HiAPI permite que el mismo proxy sirva a la vez a modelos como DeepSeek, Claude y GPT.
TL;DR
- Un proxy de la API de OpenAI es un servidor pequeño que se sitúa entre tu aplicación y la API del modelo. Custodia las claves reales y añade capacidades que la propia API no ofrece: claves por cliente, limitación de velocidad, registros y rotación de claves. Abajo tienes una versión funcional en FastAPI de unas 50 líneas.
- La API de texto de HiAPI es compatible con OpenAI:
POST /v1/chat/completionsyPOST /v1/responsesestán enhttps://api.hiapi.ai/v1. Apunta el SDK oficialopenaio tu proxy a esta URL base y podrás llamar con una sola clave a los modelos de texto DeepSeek, Claude, GPT, Kimi, GLM y Grok. - Las imágenes y los vídeos son distintos: tardan de segundos a minutos, así que funcionan sobre el endpoint asíncrono
POST /v1/tasksde HiAPI. No metas este tipo de peticiones en un proxy síncrono.
Paso 1: un proxy de reenvío mínimo
El proxy valida la clave interna que envía el cliente, la sustituye por la clave real de origen y luego devuelve la respuesta en streaming.
pip install fastapi uvicorn httpx
# proxy.py
import os
import httpx
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import StreamingResponse
from starlette.background import BackgroundTask
UPSTREAM = os.environ.get("UPSTREAM_BASE", "https://api.openai.com/v1")
UPSTREAM_KEY = os.environ["UPSTREAM_KEY"]
INTERNAL_KEYS = set(os.environ["INTERNAL_KEYS"].split(",")) # comma-separated
app = FastAPI()
client = httpx.AsyncClient(timeout=120.0)
def auth(request: Request) -> str:
header = request.headers.get("authorization", "")
key = header.removeprefix("Bearer ").strip()
if key not in INTERNAL_KEYS:
raise HTTPException(401, "invalid internal key")
return key
@app.post("/v1/{path:path}")
async def forward(path: str, request: Request):
tenant = auth(request)
body = await request.body()
upstream_req = client.build_request(
"POST", f"{UPSTREAM}/{path}", content=body,
headers={"Authorization": f"Bearer {UPSTREAM_KEY}",
"Content-Type": request.headers.get("content-type", "application/json")},
)
upstream = await client.send(upstream_req, stream=True)
# Stream back so long answers and SSE don't time out; close the upstream stream when done
return StreamingResponse(upstream.aiter_raw(), status_code=upstream.status_code,
media_type=upstream.headers.get("content-type"),
background=BackgroundTask(upstream.aclose))
Ejecútalo:
UPSTREAM_KEY=your-upstream-key INTERNAL_KEYS=tenant-a,tenant-b \
uvicorn proxy:app --host 0.0.0.0 --port 8000
El cliente usa el SDK de OpenAI como de costumbre, configurando la dirección del proxy como URL base:
from openai import OpenAI
client = OpenAI(api_key="tenant-a", base_url="http://localhost:8000/v1")
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Hello"}],
)
print(resp.choices[0].message.content)
Las conversaciones, el streaming y las incrustaciones pasan por el mismo manejador, porque el proxy nunca analiza el interior del cuerpo de la petición.
Paso 2: añadir capacidades que la API de origen no ofrece
Limitación de velocidad por inquilino. Sin ella, un cliente con mucho tráfico puede agotar toda tu cuota.
import time
from collections import defaultdict
_window: dict[str, list[float]] = defaultdict(list)
LIMIT = 60 # requests per 60 s
def check_rate_limit(tenant: str):
now = time.time()
_window[tenant] = [t for t in _window[tenant] if now - t < 60]
if len(_window[tenant]) >= LIMIT:
raise HTTPException(429, "rate limit exceeded")
_window[tenant].append(now)
Llámalo justo después de auth(). Si hay varios procesos de servidor, traslada el contador a Redis (INCR más EXPIRE).
Registros. Registra el inquilino, la ruta, el código de estado, la latencia y el número de tokens. Nunca registres la cabecera Authorization.
Rotación de claves. Carga UPSTREAM_KEY desde un servicio de gestión de secretos y actualízala periódicamente. Cuando la clave de origen se cambia por debajo, los clientes siguen usando sus propias claves de inquilino.
Reglas de coste. Analiza el cuerpo de la petición y rechaza las peticiones que infrinjan la política, por ejemplo usar un modelo caro en el plan gratuito o un max_tokens que supere tu límite.
Paso 3: poner varios proveedores de modelos detrás del mismo proxy
Un proxy que solo se conecta a un proveedor es fácil. Añadir Anthropic, DeepSeek o Moonshot implica cabeceras de autenticación, estructuras de petición y formatos de error distintos, y el proxy ligero se convierte en una capa de traducción.
La API de texto de HiAPI adopta la estructura de petición de OpenAI para todos sus modelos de texto, así que el proxy de arriba funciona sin cambios. Configura dos variables de entorno:
UPSTREAM_BASE=https://api.hiapi.ai/v1 UPSTREAM_KEY=your-hiapi-key INTERNAL_KEYS=tenant-a,tenant-b \
uvicorn proxy:app --host 0.0.0.0 --port 8000
O sáltate el proxy y usa el SDK directamente:
import os
from openai import OpenAI
client = OpenAI(api_key=os.environ["HIAPI_KEY"], base_url="https://api.hiapi.ai/v1")
resp = client.chat.completions.create(
model="deepseek-v4-flash",
messages=[{"role": "user", "content": "Explain sparse attention in two sentences."}],
max_tokens=400,
)
print(resp.choices[0].message.content)
Ejecutamos este código con el paquete oficial openai de Python (3.35.1), una llamada normal y otra con stream=True; ambas llamadas se completaron sin errores y el SDK analizó las respuestas como de costumbre. Hubo una trampa en esta prueba: con max_tokens=20, la respuesta quedó vacía porque deepseek-v4-flash gastó los 20 tokens en el razonamiento (usage.completion_tokens_details.reasoning_tokens fue 22). Deja un margen de varios cientos de tokens para los modelos de razonamiento.
Algunos modelos de texto en HiAPI:
| ID del modelo | Ruta | Entrada / salida, USD por millón de tokens |
|---|---|---|
deepseek-v4-flash |
Chat Completions | $0.14 / $0.28 |
deepseek-v4.1-flash |
Chat Completions y Responses | $0.44 / $1.32 |
gpt-5.6-luna |
Solo Responses | $0.05 / $0.30 |
grok-4.6 |
Chat Completions | $0.84 / $2.52 |
deepseek-v4-pro |
Chat Completions y Responses | $1.50 / $4.50 |
claude-opus-5-5 |
Chat Completions | $2.02 / $10.08 |
kimi-k3 |
Chat Completions | $2.32 / $11.60 |
claude-sonnet-4-6 |
Chat Completions | $3.00 / $15.00 |
Precios en tiempo real de HiAPI, consultados el 20 de octubre de 2026. GET https://api.hiapi.ai/v1/models enumera todos los modelos disponibles para tu clave.
Los modelos GPT como gpt-5.6-luna solo se ofrecen en la API Responses (client.responses.create), así que un proxy que reenvía /v1/{path} cubre ambos casos.
Un ID de modelo desconocido devuelve un HTTP 400 al estilo de los errores de OpenAI, con un request_id para contactar con el soporte:
{"error": {"code": "model_not_found", "type": "hiapi_error",
"message": "The requested model does not exist or is not available. Check the exact model ID with GET /v1/models and try again. ...",
"request_id": "..."}}
Paso 4: las imágenes y los vídeos no deben ir en un proxy síncrono
Un proxy síncrono mantiene la conexión abierta hasta que llega la respuesta. Eso está bien para texto. Las imágenes tardan decenas de segundos y los vídeos minutos, más de lo que la mayoría de los balanceadores de carga y navegadores están dispuestos a esperar.
HiAPI ejecuta los modelos de imagen y vídeo en un endpoint asíncrono: envía una tarea, obtén un id de tarea y luego consulta su estado o recibe una devolución de llamada.
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "z-image",
"input": {"prompt": "a glass data center at golden hour, isometric", "aspect_ratio": "16:9"},
"callback": {"url": "https://your-app.example.com/hiapi/callback", "when": "final"}
}'
# => {"code":200,"message":"success","data":{"taskId":"tk-hiapi-..."}}
Tu proxy puede seguir reenviando la llamada de envío, que devuelve el control de inmediato. El resultado llega después mediante tu URL de devolución de llamada o con GET /v1/tasks/{taskId}. Los enlaces de salida caducan a los 7 días aproximadamente, así que guarda los archivos.
Lista de comprobación para producción
- Elimina las claves de los registros. Nunca registres
Authorization, ni en la entrada ni en la salida. - Usa devoluciones de llamada para las tareas de medios. Consultar el estado de miles de tareas desperdicia peticiones. Escribe el manejador de la devolución de llamada de forma idempotente y con clave basada en
taskId, porque la entrega puede reintentarse. - Guarda el id de tarea antes de consultar su estado. Si el proceso se cae, reanuda la consulta en lugar de reenviar la tarea y pagar dos veces.
- Deja espacio para los tokens de razonamiento. Configura
max_tokensmuy por encima de la longitud de respuesta que esperas. - Comprueba qué permite tu clave.
GET /v1/modelsdevuelve la lista correspondiente a esa clave.
Preguntas frecuentes
¿Puedo usar el SDK de Python de OpenAI con HiAPI?
Sí, para los modelos de texto. Configura base_url="https://api.hiapi.ai/v1" y usa tu clave de HiAPI. Las imágenes y los vídeos usan /v1/tasks, un endpoint que el SDK de OpenAI no cubre; llámalo con requests o httpx.
¿Sigo necesitando mi propio proxy?
Solo por los mismos motivos que con cualquier proveedor: claves por inquilino, limitación de velocidad y registros de auditoría. Dentro del proxy ya no hace falta convertir entre proveedores.
¿Funciona el streaming?
Sí. stream=True devuelve eventos enviados por el servidor que terminan con data: [DONE], y el proxy de arriba los reenvía en streaming sin búfer.
¿Cómo roto una clave de HiAPI sin interrumpir el servicio?
Crea una clave nueva en la consola, despliégala en tu proxy o almacén de claves y luego revoca la clave antigua. Durante la transición ambas claves funcionan.
Más detalles: documentación de Chat Completions, documentación de Responses y HiAPI como alternativa a un proxy de la API de OpenAI.
Artículos relacionados
Ver todos los artículos →-
Tutorial
Cómo llamar a hailuo-2.3-fast/image-to-video a través de la API de HiAPI: práctica con curl y Python
La primera solicitud ejecutable del nivel rápido y de bajo coste de imagen a vídeo: del ID del modelo a la descarga del MP4
-
Tutorial
Cómo llamar a hailuo-2.3/text-to-video mediante la API de HiAPI: curl, Python y ejemplos de solicitud ejecutables
-
Tutorial
Uso de Kandinsky 6.0 Pro texto a video en HiAPI: tutorial con curl y Python
Demostración práctica centrada en la solicitud: construir la llamada de prompt a clip de video, enviar la solicitud, sondear el estado de finalización y descargar el resultado