OpenAI·OpenRouter에서 이전하기
기존 OpenAI 라이브러리를 SpicyAPI에 연결하는 기본 URL·키·모델 ID 변경, 요청 차이와 OpenRouter 전용 필드 정리입니다.
공식 OpenAI 라이브러리나 ‘OpenAI-compatible’ 설정 도구로 채팅 모델을 호출하고 있다면 기본 URL, API 키와 모델 ID만 바꾸어 SpicyAPI로 이전할 수 있습니다. 이 페이지는 세 변경 사항과 동작 차이를 설명하여 이전 후 예상하지 못한 400을 방지합니다. 완전한 프로그램 예시는 채팅 구현 예시를 참고하세요.
변경할 세 가지
| 설정 | SpicyAPI 값 |
|---|---|
| 기본 URL | https://api.spicyapi.ai/v1 |
| API 키 | sk-spicy-로 시작하는 SpicyAPI 키를 Authorization: Bearer로 전달 |
| 모델 | 최신 카탈로그의 정확한 텍스트 모델 ID. 예: SPICY_MODEL에 저장(모델 ID 참조) |
import os
from openai import OpenAI
# Before: client = OpenAI() (reads OPENAI_API_KEY, calls api.openai.com)
client = OpenAI(api_key=os.environ["SPICY_API_KEY"], base_url="https://api.spicyapi.ai/v1")
reply = client.chat.completions.create(
model=os.environ["SPICY_MODEL"],
messages=[{"role": "user", "content": "Say hello in one sentence."}],
)
print(reply.choices[0].message.content)OpenAI 라이브러리는 환경의 OPENAI_BASE_URL과 OPENAI_API_KEY도 읽습니다. 이를 설정하면 기존 코드를 그대로 실행할 수 있지만 같은 프로세스의 다른 모든 OpenAI 호출까지 변경됩니다. 따라서 위처럼 값을 명시적으로 전달하는 편을 권장합니다.
모델 ID
텍스트 모델 ID는 publisher/model/chat 형식입니다. 다른 곳에서 아는 이름을 변환하지 말고 최신 카탈로그에서 복사하세요.
GET /api/v1/models?modality=text는 가격을 포함한 텍스트 모델만 반환하며includeSchema=1이면 각 모델의inputSchema도 제공합니다. 모델 카탈로그와 스키마 참조.GET /v1/models는 도구가 읽는 OpenAI 형식 목록입니다. 필터 없이는 이미지·영상·오디오도 포함되지만/v1/chat/completions에서 호출할 수 없습니다.?modality=text를 추가하세요. OpenAI 기본 네 필드 외에modality,task가 있고 채팅 모델은context_length,max_output_tokens,supported_parameters(모델이 받는 요청 필드)도 제공합니다. OpenAI 라이브러리는 추가 필드를 무시합니다.
존재하지 않는 모델 ID는 404, code: "model_not_found", param: "model"을 반환합니다. 메시지는 ID 형식과 가능한 경우 가장 가까운 호출 가능한 채팅 모델을 안내합니다. 채팅 엔드포인트에 이미지·영상 모델을 호출하면 채팅 모델이 아니라는 400입니다. 다른 서비스 ID는 비슷해도 호환되지 않습니다. 개발사 표기가 다르고 :free 같은 접미사가 없으며 텍스트 모델은 /chat으로 끝납니다.
요청 필드
각 모델은 inputSchema에 허용 필드를 공개하며 /v1/models의 supported_parameters에도 이름을 제공합니다.
- 모델에 없는 샘플링 필드는 무시됩니다.
temperature,top_p,top_k,min_p,top_a,frequency_penalty,presence_penalty,repetition_penalty는 답변의 무작위성·반복성만 바꾸며 많은 클라이언트가 기본으로 보내므로 이를 받지 않는 모델은 적용 없이 생성합니다.n: 1, 빈stop, 중지 시퀀스 없는 모델의stop, 추론 단계 없는 모델의reasoning_effort도 같습니다. 응답X-Spicy-Ignored-Params에 무시된 필드가 표시됩니다(예:frequency_penalty, presence_penalty). - 허용 범위 밖 값은 가장 가까운 허용값으로 조정됩니다. 샘플링 값은 범위 경계로 제한하고, 너무 긴
stop목록은 앞쪽 항목을 유지하며, 지원하지 않는reasoning_effort는 가장 가까운 단계로 바꿉니다(거리가 같으면 낮은 단계). 변경은name=value로X-Spicy-Adjusted-Params에 표시합니다(예:reasoning_effort=low, temperature=1). - 그 외 모델에 없는 필드는 거절됩니다.
400,code: "unsupported_parameter"와 해당 필드를 가리키는param을 반환합니다. 메시지도 이름을 표시합니다(예:logprobs: is not a supported field). 이 필드는 결과의 내용을 바꾸므로 보냈다고 생각한 기능 없이 생성된 답변에 비용을 지불하지 않게 합니다.
| 필드 | SpicyAPI 동작 |
|---|---|
messages | 같은 구조. 역할은 system, developer, user, assistant, tool. 텍스트는 모든 모델, image_url 부품은 스키마에 있는 모델만 가능 |
max_tokens, max_completion_tokens | 둘 다 같은 의미로 허용. 추론 토큰 포함. 기본값·상한은 모델별 |
stream, stream_options | 지원. OpenAI 사용량 규칙에 따라 include_usage가 true일 때만 스트림에 사용량 포함(응답과 스트림) |
tools, tool_choice | 모든 텍스트 모델 지원. 일부 모델의 tool_choice는 auto, none만 허용하여 특정 도구 강제 시 400 |
parallel_tool_calls | 필드를 명시한 모델만 가능 |
reasoning_effort | 대부분 제공하지만 허용값은 모델마다 다름. none, minimal, xhigh, max가 추가되거나 medium이 없을 수 있음. 미지원 값은 가장 가까운 단계로 조정하므로 정확한 단계가 중요하면 모델 enum 확인 |
temperature, top_p, top_k, frequency_penalty, presence_penalty | 명시한 모델에 적용하며 범위로 제한. 나머지는 무시하고 X-Spicy-Ignored-Params에 표시. 샘플링 필드를 전혀 받지 않는 모델도 있음 |
seed | 필드를 명시한 모델만 가능 |
stop | 명시한 모델에서 최대 항목 수로 잘라 사용. 빈 값이나 중지 시퀀스 없는 모델에서는 무시 |
response_format | 필드를 명시한 모델의 json_object. 나머지는 불가 |
user, metadata, store | 받아서 무시 |
n | 1은 무시. 명시하지 않은 모델에서 다른 값은 400 |
logprobs, top_logprobs, logit_bias 및 모델에 없는 기타 필드 | 400으로 거절 |
전송 전 필드와 허용값은 채팅 구현 예시: 시작 전의 명령으로 확인하세요.
응답과 스트림
비스트리밍 답변은 일반 chat.completion 객체이므로 reply.choices[0].message.content와 reply.usage를 그대로 사용합니다. 차이는 다음과 같습니다.
reasoning_content. 답변 전 추론하는 모델은content옆의message.reasoning_content(스트리밍은delta.reasoning_content)에 추론 텍스트를 반환합니다. 이미usage.completion_tokens에 포함됩니다.- ID는 요청 ID입니다.
id는chatcmpl-req_...형식이고 응답x-request-id에는 같은req_...가 있습니다. 지원 문의에 포함하세요. 별도X-Spicy-Task-Id는 과금을 담는 작업을 식별합니다(비용과 사용량). system_fingerprint와logprobs는 없습니다.
스트림은 같은 chat.completion.chunk 이벤트를 사용하고 data: [DONE]으로 끝납니다. 사용량도 OpenAI 규칙을 따릅니다.
- 요청한 경우에만 사용량 제공.
stream_options.include_usage가 없으면usage는 없고 모든 청크의choices는 비어 있지 않습니다."include_usage": true이면[DONE]직전에 빈choices와 최종usage의 추가 청크가 오며 나머지는usage: null입니다. OpenAI 스트리밍 예시 코드는 그대로 작동합니다. 마지막 청크에서 안전하려면choices[0]전에choices를 확인하세요. - 선택 항목별
finish_reason하나. 어떤 모델이든 각 선택은finish_reason을 가진 정확히 한 청크로 종료됩니다. - 연결 유지 줄. 추론 중
: keep-alive주석을 보내 연결을 유지합니다. OpenAI 라이브러리는 건너뛰며 직접 만든 파서도 그래야 합니다.
긴 답변이나 높은 reasoning_effort에는 스트리밍을 사용하세요. 비스트리밍이 약 30초를 넘으면 JSON 본문 앞 공백과 X-Spicy-Keepalive: 1 헤더로 연결을 유지하여 OpenAI 라이브러리가 파싱합니다. 다만 그 이후 실패하면 200 응답 본문에 오류 객체가 도착하여 라이브러리가 예외를 발생시키지 않습니다. 시간 초과와 스트리밍을 참고하세요.
오류
오류는 OpenAI 형식 {"error": {"message", "type", "param", "code"}}와 일반 HTTP 상태를 사용하여 OpenAI 라이브러리의 기존 예외가 발생합니다. param은 temperature나 messages.1.content.0 같은 점 경로로 관련 필드를 가리킵니다. code는 같은 상태의 오류를 구분합니다. unsupported_parameter는 미지원 필드·메시지 부품, invalid_request는 누락·미허용 값, content_rejected는 콘텐츠 정책 거절(역시 400), model_not_found는 404입니다. OpenAI와 달리 잔액·지출 한도가 요청을 충당하지 못하면 402입니다. 전체 대응표는 채팅 구현 예시: 오류 대응에 있습니다.
OpenAI 라이브러리는 429와 5xx를 자체 재시도합니다. 실패 요청은 과금되지 않으므로 안전합니다. Idempotency-Key를 보내면 호출별로 전달하고 먼저 텍스트 엔드포인트의 멱등성 키를 읽으세요.
비용과 사용량
응답 usage는 가격이 아닌 토큰 수입니다. 입력·캐시 입력·출력의 백만 토큰당 가격은 모델 페이지와 카탈로그에 나옵니다. 실제 비용은 스트리밍 여부와 관계없이 승인 요청에 포함되는 X-Spicy-Task-Id 헤더(job_…)로 GET /api/v1/jobs/recordInfo?taskId=<job_…>를 조회하세요. settled가 true이면 cost가 확정입니다. GET /api/v1/jobs 항목도 생성 호출의 requestId(X-Request-Id 헤더)를 담으며 콘솔 → 로그에 같은 작업이 나옵니다. OpenAI 라이브러리에서 헤더를 읽는 방법은 채팅 구현 예시: 실제 비용 확인, 추가 설명은 견적과 과금을 참고하세요.
실행 전에 메시지와 전체 max_tokens 출력(생략하면 모델 기본값)을 충당하는 금액을 잔액에서 선점합니다. 실제 사용량만 결제하지만 선점액을 먼저 충당해야 하므로 작은 잔액에 큰 max_tokens이면 402입니다. 현실적인 max_tokens나 키 지출 한도를 설정하세요. 과금 참조.
제공하지 않는 엔드포인트
텍스트 생성은 OpenAI Chat Completions·Responses, Anthropic Messages와 Google Gemini로 제공됩니다. 텍스트와 스트리밍을 참고하세요. 다음 OpenAI 엔드포인트는 없으며 404입니다: /v1/embeddings, 기존 /v1/completions, /v1/images/*, /v1/audio/*, /v1/moderations, /v1/files, /v1/batches, /v1/assistants, 파인튜닝. 이미지·영상·오디오는 작업 API로 호출하며 영상은 /v1/videos도 가능합니다.
OpenRouter에서 이전하는 경우
OpenRouter도 OpenAI 형식이므로 같은 세 변경 사항이 적용됩니다. 기본 URL https://openrouter.ai/api/v1을 https://api.spicyapi.ai/v1로 바꾸고 키·모델 ID도 바꾸세요. 이후 OpenRouter 전용 필드는 제거하세요. 여기서는 400을 반환합니다.
| OpenRouter 필드 | 처리 |
|---|---|
대체 모델 목록 models와 route | 제거. 모델 ID 하나를 보내면 SpicyAPI가 실행 방식을 정함 |
provider | 제거. 요청별 실행 서비스 선택 없음 |
reasoning(예: {"effort": "high"} 객체) | 최상위 reasoning_effort로 변환(enabled: false는 모델이 제공할 때 none). 나머지 내부 키는 무시. 직접 reasoning_effort를 보내는 편이 명확함 |
usage({"include": true}) | 제거. 비스트리밍은 항상 usage 포함, 스트리밍은 stream_options: {"include_usage": true} 사용 |
transforms, plugins | 제거 |
HTTP-Referer와 X-Title 헤더는 문제 없이 무시됩니다.
확인 목록
- 기본 URL
https://api.spicyapi.ai/v1과 SpicyAPI 키를 사용합니다. - 최신 카탈로그에서
/chat으로 끝나는 ID를 복사합니다. inputSchema로 요청 필드를 확인하고 미지원 비샘플링 필드를 제거합니다.X-Spicy-Ignored-Params와X-Spicy-Adjusted-Params를 한 번 확인하여 변경 내용을 파악합니다.stream_options.include_usage를 보내면 빈choices의 마지막 청크를 스트림 루프에서 건너뜁니다.- 현실적인
max_tokens를 설정하거나 모델 기본값을 사용하고 키 지출 한도를 둡니다. - 긴 요청이나 높은 추론 강도에는
stream: true를 사용합니다.
이어서 읽기
채팅 구현 예시 · 텍스트와 스트리밍 · 모델 카탈로그와 스키마 · 인증 · 과금

