HiAPI
블로그

튜토리얼

Python으로 OpenAI API 프록시 구축하기: 먼저 직접 작성하고, 그다음 모든 모델에 연결하기

인증, 속도 제한, 스트리밍을 갖춘 실사용 가능한 FastAPI 프록시; 그리고 HiAPI의 OpenAI 호환 API가 동일한 프록시로 DeepSeek, Claude, GPT 등의 모델을 동시에 서비스하는 방법.

Jun 26, 2026
Python으로 OpenAI API 프록시 구축하기: 먼저 직접 작성하고, 그다음 모든 모델에 연결하기

TL;DR

  • OpenAI API 프록시는 애플리케이션과 모델 API 사이에 있는 작은 서버입니다. 실제 키를 보관하고 API 자체가 제공하지 않는 기능을 보완합니다: 고객별 키, 속도 제한, 로그, 키 교체. 아래는 약 50줄짜리 실사용 가능한 FastAPI 버전입니다.
  • HiAPI의 텍스트 API는 OpenAI와 호환됩니다: POST /v1/chat/completions와 POST /v1/responses가 https://api.hiapi.ai/v1에 있습니다. 공식 openai SDK나 여러분의 프록시를 이 base URL로 지정하면 하나의 키로 DeepSeek, Claude, GPT, Kimi, GLM, Grok 텍스트 모델을 호출할 수 있습니다.
  • 이미지와 비디오는 다릅니다: 몇 초에서 몇 분이 걸리므로 HiAPI의 비동기 POST /v1/tasks에서 실행됩니다. 이런 요청을 동기 프록시에 넣지 마세요.

1단계: 최소한의 패스스루 프록시

프록시는 클라이언트가 보낸 내부 키를 검증하고, 실제 업스트림 키로 교체한 뒤, 응답을 스트리밍으로 되돌려줍니다.

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))

실행합니다:

UPSTREAM_KEY=your-upstream-key INTERNAL_KEYS=tenant-a,tenant-b \
uvicorn proxy:app --host 0.0.0.0 --port 8000

클라이언트는 평소처럼 OpenAI SDK를 사용하되 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)

대화, 스트리밍, 임베딩 모두 동일한 핸들러를 거칩니다. 프록시는 요청 본문 내부를 전혀 파싱하지 않기 때문입니다.

2단계: 업스트림 API가 제공하지 않는 기능 보완하기

테넌트별 속도 제한.이것이 없으면 바쁜 고객 한 명이 전체 할당량을 소진할 수 있습니다.

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)

auth() 직후에 호출하세요. 서버 프로세스가 여러 개라면 카운터를 Redis로 옮기세요(INCR와 EXPIRE).

로그.테넌트, 경로, 상태 코드, 지연 시간, 토큰 수를 기록하세요. Authorization 헤더는 절대 기록하지 마세요.

키 교체.키 관리 서비스에서 UPSTREAM_KEY를 로드하고 주기적으로 갱신하세요. 업스트림 키가 백엔드에서 교체되어도 클라이언트는 계속 각자의 테넌트 키를 사용합니다.

비용 규칙.요청 본문을 파싱하여 정책을 초과하는 요청을 거부하세요. 예를 들어 무료 요금제에서 비싼 모델을 사용하거나 max_tokens가 상한을 초과하는 경우입니다.

3단계: 여러 모델 제공자를 하나의 프록시 뒤에 두기

프록시를 한 제공자에만 연결하는 것은 쉽습니다. Anthropic, DeepSeek, Moonshot을 추가하려면 인증 헤더, 요청 구조, 오류 형식이 달라지므로 얇은 프록시가 번역 계층이 됩니다.

HiAPI의 텍스트 API는 모든 텍스트 모델에 OpenAI의 요청 구조를 사용하므로 위의 프록시가 수정 없이 작동합니다. 환경 변수 두 개를 설정하세요:

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

또는 프록시를 건너뛰고 SDK를 직접 사용하세요:

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)

공식 openai Python 패키지(3.35.1)로 이 코드를 실행했습니다. 한 번은 일반 호출, 한 번은 stream=True로 호출했으며, 두 호출 모두 오류 없이 완료되었고 SDK도 평소처럼 응답을 파싱했습니다. 이 테스트에는 함정이 하나 있었습니다: max_tokens=20일 때 응답이 비어 있었는데, deepseek-v4-flash가 20개 토큰을 모두 추론에 사용했기 때문입니다(usage.completion_tokens_details.reasoning_tokens가 22). 추론 모델에는 수백 개 토큰의 여유를 두세요.

HiAPI의 텍스트 모델 일부:

모델 ID 라우팅 입력 / 출력, 100만 토큰당 미국 달러
deepseek-v4-flash Chat Completions $0.14 / $0.28
deepseek-v4.1-flash Chat Completions 및 Responses $0.44 / $1.32
gpt-5.6-luna Responses 전용 $0.05 / $0.30
grok-4.6 Chat Completions $0.84 / $2.52
deepseek-v4-pro Chat Completions 및 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

HiAPI 실시간 가격, 2026년 10월 20일 조회. GET https://api.hiapi.ai/v1/models는 해당 키로 사용 가능한 모든 모델을 나열합니다.

gpt-5.6-luna 같은 GPT 모델은 Responses API에서만 제공되므로(client.responses.create), /v1/{path}를 포워딩하는 프록시가 둘 다 커버할 수 있습니다.

알 수 없는 모델 ID는 OpenAI 오류 스타일의 HTTP 400으로 반환되며, 지원 문의용 request_id가 포함됩니다:

{"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": "..."}}

4단계: 이미지와 비디오는 동기 프록시에 넣지 말아야 합니다

동기 프록시는 답이 돌아올 때까지 연결을 계속 열어 둡니다. 텍스트에는 문제없습니다. 이미지는 수십 초, 비디오는 수 분이 걸리는데, 이는 대부분의 로드 밸런서와 브라우저가 기다리려는 시간보다 깁니다.

HiAPI는 이미지와 비디오 모델을 비동기 엔드포인트에서 실행합니다: 작업을 제출하고 task id를 받은 뒤, 폴링하거나 콜백을 받습니다.

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-..."}}

여러분의 프록시는 여전히 제출 호출을 포워딩할 수 있으며, 이는 즉시 반환됩니다. 결과는 이후 콜백 URL이나 GET /v1/tasks/{taskId}를 통해 반환됩니다. 출력 링크는 약 7일 후 만료되므로 파일을 저장하세요.

프로덕션 환경 체크리스트

  • 로그에서 키를 제거하세요.들어오든 나가든 Authorization은 절대 기록하지 마세요.
  • 미디어 작업에는 콜백을 사용하세요.수천 개 작업을 폴링하면 요청이 낭비됩니다. 콜백 처리를 멱등적으로 작성하고 taskId를 키로 사용하세요. 전달이 재시도될 수 있기 때문입니다.
  • 폴링 전에 task id를 저장하세요.프로세스가 충돌하면 다시 제출하여 두 번 과금되지 말고 폴링을 재개하세요.
  • 추론 토큰을 위한 공간을 두세요.max_tokens를 예상 답변 길이보다 훨씬 높게 설정하세요.
  • 키로 무엇을 할 수 있는지 확인하세요.GET /v1/models는 해당 키에 대응하는 목록을 반환합니다.

자주 묻는 질문

HiAPI에서 OpenAI Python SDK를 사용할 수 있나요?

네, 텍스트 모델에 사용할 수 있습니다. base_url="https://api.hiapi.ai/v1"을 설정하고 HiAPI 키를 사용하세요. 이미지와 비디오는 /v1/tasks를 사용하며 OpenAI SDK는 이 엔드포인트를 지원하지 않습니다. requests나 httpx로 호출하세요.

여전히 자체 프록시가 필요한가요?

다른 모든 제공자와 같은 이유로만 필요합니다: 테넌트별 키, 속도 제한, 감사 로그입니다. 프록시 내부에서 제공자별 변환은 더 이상 필요하지 않습니다.

스트리밍이 작동하나요?

네. stream=True는 data: [DONE]으로 끝나는 서버 전송 이벤트를 반환하며, 위의 프록시는 이를 버퍼링 없이 스트리밍으로 포워딩합니다.

서비스 중단 없이 HiAPI 키를 교체하려면 어떻게 하나요?

콘솔에서 새 키를 만들고 프록시나 키 저장소에 배포한 뒤 이전 키를 폐기하세요. 전환 중에는 두 키 모두 사용할 수 있습니다.

자세한 내용: Chat Completions 문서, Responses 문서, 그리고 OpenAI API 프록시 대안으로서의 HiAPI.

영구 AI 아티팩트로 빌드 시작하기

API 키를 받아 태스크 기반의 하나의 API로 이미지, 비디오, 오디오 모델을 호출하세요.

API 문서 보기