HiAPI
Blog

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.

Jun 26, 2026
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

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/completions y POST /v1/responses están en https://api.hiapi.ai/v1. Apunta el SDK oficial openai o 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/tasks de 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_tokens muy por encima de la longitud de respuesta que esperas.
  • Comprueba qué permite tu clave. GET /v1/models devuelve 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 →

Empieza a crear con artefactos de IA persistentes

Consigue tu API key y llama a modelos de imagen, vídeo y audio a través de una única API basada en tasks.

Leer la documentación de la API