블로그로 돌아가기

API & SDK가이드

OpenAI Python SDK 타임아웃과 재시도: CheapTokens에 맞게 설정하기

OpenAI Python SDK에서 timeout, max_retries, max_tokens를 설정하고, 올바른 오류를 잡고, 멈춘 요청이 10분 동안 실행되지 않도록 하세요.

작성자: CheapTokens Team 게시됨읽는 시간 5분

어두운 배경에 timeout=30.0과 max_retries=2가 있는 Python 코드
이 페이지의 내용
  1. 아무것도 설정하지 않으면 SDK가 하는 일
  2. 클라이언트에 timeout과 max_retries 설정하기
  3. 하나의 요청에 대해 재정의하기
  4. 중요한 오류 잡기
  5. 긴 답변이 폭주하지 않도록 max_tokens 제한하기
  6. 재시도가 청구서에 미치는 영향
  7. 출시 전에

스크립트가 모델을 호출했는데 아무 응답도 오지 않았고, 10분이 지나도 여전히 기다리고 있습니다. 이것이 OpenAI Python SDK의 기본값입니다: 10분 타임아웃과 2회 재시도입니다. 이를 변경하려면 timeout=30.0와 max_retries=2을 OpenAI(...)에 전달하고, 호출별로 client.with_options(...)로 재정의하며, max_tokens로 응답을 제한하세요.

SDK의 README(2026년 10월 27일 확인)에 이 모든 내용이 문서화되어 있습니다. 아래에서는 OpenAI Chat Completions 형식을 사용하는 CheapTokens 엔드포인트 https://api.cheaptokens.dev/v1에 적용했습니다. 해당 설정에 대한 자세한 내용은 CheapTokens로 첫 API 요청하기와 문서에 있습니다.

아무것도 설정하지 않으면 SDK가 하는 일#

두 가지 기본값이 중요합니다. 타임아웃은 10분입니다. 재시도는 2회이며, 짧은 지수 백오프와 함께 연결 오류와 408, 409, 429, 5xx 응답에 대해, 그리고 요청 본문을 안전하게 다시 보낼 수 있을 때만 수행됩니다. 타임아웃된 요청도 재시도됩니다.

따라서 각 시도는 자체 타임아웃을 가지며, 최악의 경우는 대략 타임아웃에 시도 횟수를 곱한 값에 시도 사이의 일시 정지를 더한 것입니다:

  • 기본값: 600 × 3 = 1800초, 30분
  • timeout=30.0, max_retries=2: 30 × 3 = 90초

웹 엔드포인트가 30초 이내에 응답해야 한다고 가정해 봅시다. 기본값으로는 코드가 오류를 보기까지 30분 동안 멈춰 있을 수 있습니다. 계속 읽기 전에 다른 설정으로 시도해 보세요.

숫자 맞히기

클라이언트에 timeout=45.0과 max_retries=3이 있습니다. 예외가 발생하기까지 최악의 대기 시간(초)은 얼마이며, 시도 사이의 일시 정지는 계산하지 않습니다.

정답 보기

정답: 180

45 × (3회 재시도 + 1회 첫 시도) = 180초; 시도 사이의 일시 정지가 그 위에 약간 더해집니다.

클라이언트에 timeout과 max_retries 설정하기#

가장 간단한 해결책은 클라이언트를 생성할 때 두 값을 한 번에 설정하는 것입니다:

python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.cheaptokens.dev/v1",
    api_key=os.environ["CHEAPTOKENS_API_KEY"],
    timeout=30.0,      # seconds per attempt
    max_retries=2,     # 0 turns retries off
)

resp = client.chat.completions.create(
    model="deepseek-ai/DeepSeek-V4-Flash-0731",
    messages=[{"role": "user", "content": "Summarize this ticket in two sentences: ..."}],
    max_tokens=800,
)
print(resp.choices[0].message.content)
print(resp.usage.prompt_tokens, resp.usage.completion_tokens)

max_retries은 음수가 아닌 정수여야 합니다. timeout는 float를 받으며, 연결과 읽기에 별도의 제한을 원하면 httpx Timeout 객체를 받습니다. 현재 main 브랜치의 README는 이를 위해 httpx2을 임포트하고, 이전 버전은 httpx을 사용했으므로, pip show openai를 실행하여 설치 버전이 어느 것을 사용하는지 확인하세요. 일반 float는 임포트가 필요 없고 어느 쪽이든 작동합니다.

timeout=30과 2회 재시도를 사용한 요청 경로
하나의 요청: 시도, 타임아웃, 최대 2회 재시도, 그다음 예외

CheapTokens 문서의 전체 모델 id를 사용하세요, 여기서는 deepseek-ai/DeepSeek-V4-Flash-0731입니다: id는 제공자마다 다릅니다. 이 모델은 답변당 최대 32,768개의 출력 토큰을 받으며 380K 컨텍스트를 가지므로, 위의 max_tokens 줄이 의도적으로 있습니다. 이에 대한 자세한 내용은 아래에, 모델에 대한 내용은 저렴한 DeepSeek에 있습니다.

하나의 요청에 대해 재정의하기#

하나의 클라이언트, 다른 작업들. 채팅 답변은 빠르게 포기해야 하고, 야간 요약은 기다릴 수 있습니다. 두 번째 클라이언트를 만드는 대신, 호출을 위해 하나를 파생시키세요:

python
quick = client.with_options(timeout=5.0, max_retries=0)
resp = quick.chat.completions.create(
    model="deepseek-ai/DeepSeek-V4-Flash-0731",
    messages=[{"role": "user", "content": "Reply with one word: ok"}],
    max_tokens=5,
)

with_options은 새 값으로 복사본을 반환하고 client은 그대로 둡니다. 자체 코드가 이미 재시도하는 경우, 예를 들어 작업을 다시 큐에 넣는 작업 큐라면 max_retries=0를 설정하는 것이 올바른 선택입니다. 그렇지 않으면 두 계층이 곱해집니다: 3회 큐 시도 안의 3회 SDK 시도는 하나의 작업에 9개의 요청입니다.

중요한 오류 잡기#

재시도가 소진된 후 SDK는 예외를 발생시킵니다. 모든 오류 클래스는 openai.APIError을 상속하며, 실패한 요청은 request_id을 가지며, 이것이 로그에 기록하는 것입니다:

python
import openai

def ask(prompt: str) -> str | None:
    try:
        r = client.chat.completions.create(
            model="deepseek-ai/DeepSeek-V4-Flash-0731",
            messages=[{"role": "user", "content": prompt}],
            max_tokens=800,
        )
        return r.choices[0].message.content
    except openai.APITimeoutError:
        print("timed out after retries")
    except openai.RateLimitError as e:
        print("429, slow down:", e.request_id)
    except openai.APIConnectionError:
        print("cannot reach the API")
    except openai.APIStatusError as e:
        print("HTTP", e.status_code, e.request_id)
    return None

순서는 좁은 것에서 넓은 것으로입니다. APIStatusError은 모든 4xx 또는 5xx 응답을 포함하므로, 먼저 오면 RateLimitError를 삼킬 것입니다. 타임아웃도 같은 개념입니다: 일반 연결 오류 위에 위치합니다. 배치 스크립트에서 return None이 핵심입니다: 프롬프트를 기록하고, 다음으로 넘어가고, 마지막에 실패한 것들을 다시 실행하세요.

타임아웃이 발생하고 재시도가 소진되면 예외는 다음과 같습니다:

재시도가 소진된 후의 APITimeoutError 메시지
로그에서 잡힌 타임아웃의 모습

시도 사이의 대기를 포함한 429 사례에 대한 자세한 내용은 AI API의 429 Too Many Requests를 참조하세요.

긴 답변이 폭주하지 않도록 max_tokens 제한하기#

타임아웃은 기다림을 멈춥니다. 모델이 덜 쓰게 만들지는 않습니다. max_tokens 없이는 32,768 토큰 답변 제한을 가진 모델이 매우 긴 답변을 자유롭게 작성할 수 있으며, 긴 답변은 느린 답변입니다. 작업에 맞는 제한을 설정하세요: 티켓 요약에는 800, 예/아니오에는 5.

비용은 답변이 잘릴 수 있다는 것입니다. resp.choices[0].finish_reason를 확인하세요: length은 모델이 끝나기 전에 제한에 도달했음을 의미합니다. AI 답변이 문장 중간에 멈추는 이유는?에서 다룹니다. 대신 답변을 스트리밍하면 첫 단어가 일찍 도착하여 대기가 더 짧게 느껴집니다, Python에서의 스트리밍 응답을 참조하세요.

재시도가 청구서에 미치는 영향#

재시도는 새로운 요청입니다. 시도가 처리되었다면, 자체 토큰을 가진 별도의 요청입니다. 티켓 요약이 입력 1,500 토큰, 출력 400 토큰이라고 가정해 봅시다: 하나의 요청은 오늘 가격으로 $0.000063이며, 전체 가격표에 다른 모델들이 있습니다. 가격은 모델별로 고정되어 있으며 피크 시간이 없으므로, 새벽 3시의 재시도는 정오의 재시도와 같은 비용입니다.

슬라이더를 자신의 배치에 맞게 이동하세요:

비용 계산기

1,000개의 티켓 요약 배치

요청당 입력 토큰: 1,500 · 요청당 출력 토큰: 400 · 일일 요청 수: 1,000

모델일일월간
GLM-5.3-Flash$0.046$1.38
DeepSeek-V4-Flash$0.0625$1.88
MiniMax-M2.7$0.135$4.05

표는 한 번의 패스를 보여줍니다. 배치의 10분의 1이 두 번째 시도를 필요로 하면, 10분의 1을 더하세요. 재시도는 실패가 드물 때는 저렴한 보험이고, 계속 실패하는 엔드포인트에 max_retries=10을 설정할 때는 비싼 습관입니다.

출시 전에#

체크리스트

타임아웃과 재시도, 프로덕션 준비 완료