Tutorial
Construa um proxy da API OpenAI com Python: escreva um à mão primeiro e depois faça-o conectar-se a todos os modelos
Um proxy FastAPI funcional, com autenticação, limitação de taxa e streaming; e como a API compatível com OpenAI da HiAPI permite que o mesmo proxy sirva simultaneamente modelos como DeepSeek, Claude, GPT e outros.
TL;DR
- Um proxy da API OpenAI é um pequeno servidor entre a sua aplicação e a API de modelos. Ele guarda a chave real e acrescenta capacidades que a própria API não oferece: chaves por cliente, limitação de taxa, registos e rotação de chaves. Abaixo está uma versão FastAPI funcional com cerca de 50 linhas.
- A API de texto da HiAPI é compatível com OpenAI:
POST /v1/chat/completionsePOST /v1/responsesficam emhttps://api.hiapi.ai/v1. Aponte o SDK oficialopenaiou o seu proxy para este base URL e poderá chamar os modelos de texto DeepSeek, Claude, GPT, Kimi, GLM e Grok com uma única chave. - Imagem e vídeo são diferentes: levam de segundos a minutos, por isso funcionam no
POST /v1/tasksassíncrono da HiAPI. Não enfie esse tipo de pedido num proxy síncrono.
Passo 1: um proxy de passagem mínima
O proxy valida a chave interna enviada pelo cliente, substitui-a pela chave real a montante e depois devolve a resposta em 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))
Execute-o:
UPSTREAM_KEY=your-upstream-key INTERNAL_KEYS=tenant-a,tenant-b \
uvicorn proxy:app --host 0.0.0.0 --port 8000
O cliente usa o SDK OpenAI normalmente, definindo o endereço do proxy como base URL:
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)
Conversas, streaming e embeddings passam todos pelo mesmo handler, porque o proxy nunca analisa o interior do corpo do pedido.
Passo 2: acrescente capacidades que a API a montante não oferece
Limitação de taxa por tenant. Sem isto, um cliente muito ativo pode esgotar toda a sua quota.
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)
Chame-o logo após auth(). Se houver vários processos de servidor, mova o contador para o Redis (INCR mais EXPIRE).
Registos. Registe o tenant, o caminho, o código de estado, a latência e o número de tokens. Nunca registe o cabeçalho Authorization.
Rotação de chaves. Carregue UPSTREAM_KEY a partir de um serviço de gestão de segredos e atualize-a periodicamente. Quando a chave a montante é trocada nos bastidores, os clientes continuam a usar as suas próprias chaves de tenant.
Regras de custo. Analise o corpo do pedido e rejeite pedidos que violem a política, por exemplo usar um modelo caro no plano gratuito, ou max_tokens acima do seu limite.
Passo 3: coloque vários fornecedores de modelos atrás do mesmo proxy
Um proxy ligado a um único fornecedor é fácil. Acrescentar Anthropic, DeepSeek ou Moonshot significa cabeçalhos de autenticação, estruturas de pedido e formatos de erro diferentes, e o proxy fino transforma-se numa camada de tradução.
A API de texto da HiAPI adota a estrutura de pedido da OpenAI para todos os seus modelos de texto, por isso o proxy acima funciona sem alterações. Defina duas variáveis de ambiente:
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
Ou salte o proxy e use o SDK diretamente:
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)
Executámos este código com o pacote Python oficial openai (3.35.1), uma chamada normal e uma com stream=True; ambas terminaram sem erros e o SDK analisou as respostas como habitualmente. Uma armadilha neste teste: com max_tokens=20, a resposta veio vazia, porque deepseek-v4-flash gastou os 20 tokens todos em raciocínio (usage.completion_tokens_details.reasoning_tokens era 22). Deixe uma margem de algumas centenas de tokens para modelos de raciocínio.
Alguns modelos de texto na HiAPI:
| ID do modelo | Rota | Entrada / saída, USD por 1 milhão de tokens |
|---|---|---|
deepseek-v4-flash |
Chat Completions | $0.14 / $0.28 |
deepseek-v4.1-flash |
Chat Completions e Responses | $0.44 / $1.32 |
gpt-5.6-luna |
Apenas Responses | $0.05 / $0.30 |
grok-4.6 |
Chat Completions | $0.84 / $2.52 |
deepseek-v4-pro |
Chat Completions e 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 |
Preços em tempo real da HiAPI, consultados em 20 de outubro de 2026. GET https://api.hiapi.ai/v1/models lista todos os modelos disponíveis para a sua chave.
Modelos GPT como gpt-5.6-luna só são oferecidos na API Responses (client.responses.create), por isso um proxy que reencaminha /v1/{path} cobre ambos ao mesmo tempo.
IDs de modelo desconhecidos devolvem HTTP 400 no estilo de erro da OpenAI, com um request_id para contactar o suporte:
{"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": "..."}}
Passo 4: imagem e vídeo não devem entrar num proxy síncrono
Um proxy síncrono mantém a ligação aberta até a resposta chegar. Para texto, tudo bem. Imagem leva dezenas de segundos, vídeo leva minutos, mais do que a maioria dos balanceadores de carga e navegadores está disposta a esperar.
A HiAPI executa modelos de imagem e vídeo num endpoint assíncrono: submeta a tarefa, obtenha o task id e depois faça polling ou receba um callback.
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-..."}}
O seu proxy pode continuar a reencaminhar a chamada de submissão, que devolve imediatamente. O resultado chega depois através do seu URL de callback ou de GET /v1/tasks/{taskId}. Os links de saída expiram ao fim de cerca de 7 dias, por isso guarde os ficheiros.
Checklist para produção
- Retire as chaves dos registos. Nunca registe
Authorization, nem à entrada nem à saída. - Use callbacks para tarefas de media. Fazer polling de milhares de tarefas desperdiça pedidos. Escreva o handler de callback de forma idempotente e indexado por
taskId, porque as entregas podem ser repetidas. - Guarde o task id antes de fazer polling. Se o processo falhar, retome o polling em vez de submeter de novo e pagar duas vezes.
- Deixe espaço para os tokens de raciocínio. Defina
max_tokensbem acima do comprimento de resposta que espera. - Verifique o que a chave pode usar.
GET /v1/modelsdevolve a lista correspondente a essa chave.
Perguntas frequentes
Posso usar o SDK Python da OpenAI com a HiAPI?
Sim, para modelos de texto. Defina base_url="https://api.hiapi.ai/v1" e use a sua chave HiAPI. Imagem e vídeo usam /v1/tasks, que o SDK OpenAI não cobre; chame-o com requests ou httpx.
Ainda preciso do meu próprio proxy?
Só pelos mesmos motivos que precisaria com qualquer fornecedor: chaves por tenant, limitação de taxa e registos de auditoria. Dentro do proxy já não precisa de conversão por fornecedor.
O streaming funciona?
Funciona. stream=True devolve eventos enviados pelo servidor terminados em data: [DONE], e o proxy acima reencaminha-os em streaming sem buffering.
Como rodo uma chave HiAPI sem interromper o serviço?
Crie uma nova chave na consola, implemente-a no seu proxy ou cofre de segredos e depois revogue a antiga. Durante a transição, ambas as chaves funcionam.
Mais detalhes: documentação de Chat Completions, documentação de Responses e HiAPI como alternativa de proxy da API OpenAI.
Artigos relacionados
Ver todos os artigos →-
Tutorial
Como chamar hailuo-2.3-fast/image-to-video pela API HiAPI: curl e Python na prática
A primeira requisição executável do nível rápido e econômico de geração de vídeo a partir de imagem — do ID do modelo ao download do MP4
-
Tutorial
Como chamar hailuo-2.3/text-to-video pela API HiAPI: curl, Python e exemplos de requisição executáveis
-
Tutorial
Usar Kandinsky 6.0 Pro text-to-video na HiAPI: tutorial de curl e Python
Demonstração prática com foco na requisição: montar a chamada de prompt para clipe de vídeo, enviar a requisição, consultar o status de conclusão e baixar o resultado