Voltar ao blog

API e SDKGuia

Timeout e tentativas do SDK Python da OpenAI: defina-os para a CheapTokens

Defina timeout, max_retries e max_tokens no SDK Python da OpenAI, capture os erros certos e evite que uma requisição travada rode por 10 minutos.

Por CheapTokens TeamPublicado em 5 min de leitura

Código Python com timeout=30.0 e max_retries=2 em um fundo escuro
Nesta página
  1. O que o SDK faz quando você não define nada
  2. Defina timeout e max_retries no cliente
  3. Sobrescreva para uma requisição
  4. Capture os erros que importam
  5. Limite max_tokens para que uma resposta longa não fuja
  6. O que as tentativas fazem com a conta
  7. Antes de publicar

Seu script chamou o modelo, nada voltou e, dez minutos depois, ele ainda está esperando. Esse é o padrão do SDK Python da OpenAI: um timeout de 10 minutos e 2 tentativas. Para mudar isso, passe timeout=30.0 e max_retries=2 para OpenAI(...), sobrescreva-os por chamada com client.with_options(...) e limite a resposta com max_tokens.

O README do SDK (verificado em 27 de outubro de 2026) documenta tudo isso. Abaixo, ele é aplicado ao endpoint da CheapTokens, https://api.cheaptokens.dev/v1, que fala o formato OpenAI Chat Completions. Os detalhes dessa configuração estão em Sua primeira requisição de API com a CheapTokens e na documentação.

O que o SDK faz quando você não define nada#

Dois padrões importam. O timeout é de 10 minutos. As tentativas são 2, com um breve backoff exponencial, para erros de conexão e para respostas 408, 409, 429 e 5xx, e apenas quando o corpo da requisição pode ser reenviado com segurança. Uma requisição que atinge o timeout também é repetida.

Assim, cada tentativa tem seu próprio timeout, e o pior caso é aproximadamente o timeout multiplicado pelo número de tentativas, mais as pausas entre elas:

  • padrão: 600 × 3 = 1800 segundos, meia hora
  • timeout=30.0, max_retries=2: 30 × 3 = 90 segundos

Digamos que um endpoint web precise responder em 30 segundos. Com os padrões, ele pode travar por meia hora antes que seu código veja um erro. Teste em outra configuração antes de continuar a leitura.

Adivinhar o número

Um cliente tem timeout=45.0 e max_retries=3. Qual é a espera no pior caso, em segundos, antes da exceção, sem contar as pausas entre tentativas?

Mostrar resposta

Resposta: 180

45 × (3 tentativas + 1 primeira tentativa) = 180 segundos; as pausas entre tentativas adicionam um pouco a mais.

Defina timeout e max_retries no cliente#

A correção mais simples é definir ambos de uma vez, ao criar o cliente:

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 deve ser um inteiro não negativo. timeout aceita um float, ou um objeto httpx Timeout se você quiser limites separados para conexão e leitura. O README do branch main atual importa httpx2 para isso, versões mais antigas usavam httpx, então execute pip show openai e verifique qual deles sua instalação usa. Um float simples não precisa de import e funciona de qualquer forma.

O caminho da requisição com timeout=30 e duas tentativas
Uma requisição: uma tentativa, um timeout, até duas repetições, depois uma exceção

Use o id completo do modelo da documentação da CheapTokens, deepseek-ai/DeepSeek-V4-Flash-0731 aqui: os ids diferem entre provedores. Este modelo aceita até 32.768 tokens de saída por resposta e tem um contexto de 380K, e é por isso que a linha max_tokens acima está lá de propósito. Mais sobre isso abaixo, e sobre o modelo em DeepSeek barato.

Sobrescreva para uma requisição#

Um cliente, trabalhos diferentes. Uma resposta de chat deve desistir rápido, um resumo noturno pode esperar. Em vez de criar um segundo cliente, derive um para a chamada:

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 retorna uma cópia com os novos valores e deixa client intacto. Definir max_retries=0 é a escolha certa quando seu próprio código já repete tentativas, por exemplo uma fila de tarefas que reenfileira a tarefa. Caso contrário, as duas camadas se multiplicam: 3 tentativas do SDK dentro de 3 tentativas da fila são 9 requisições para uma tarefa.

Capture os erros que importam#

Depois que as tentativas se esgotam, o SDK lança uma exceção. Toda classe de erro herda de openai.APIError, e requisições com falha carregam request_id, que é o que você registra:

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

A ordem vai do mais específico ao mais amplo. APIStatusError cobre qualquer resposta 4xx ou 5xx, então, se viesse primeiro, engoliria RateLimitError. A mesma ideia vale para o timeout: ele fica acima do erro de conexão geral. Em um script em lote, return None é o ponto central: registre o prompt, passe para o próximo e reexecute as falhas no final.

Quando o timeout dispara e as tentativas se esgotam, é assim que a exceção se parece:

A mensagem do APITimeoutError depois que as tentativas se esgotam
Como um timeout capturado aparece no log

Para o caso 429 em detalhe, incluindo a espera entre tentativas, veja 429 Too Many Requests de uma API de IA.

Limite max_tokens para que uma resposta longa não fuja#

Um timeout para de esperar. Ele não faz o modelo escrever menos. Sem max_tokens, um modelo com limite de 32.768 tokens de resposta fica livre para escrever uma resposta muito longa, e uma resposta longa é lenta. Defina um limite que caiba no trabalho: 800 para um resumo de ticket, 5 para um sim/não.

O custo é que uma resposta pode ser cortada. Verifique resp.choices[0].finish_reason: length significa que o limite foi atingido antes de o modelo terminar. Por que uma resposta de IA para no meio da frase? cobre isso. Se você transmitir a resposta em vez disso, as primeiras palavras chegam cedo e a espera parece mais curta, veja Respostas em streaming em Python.

O que as tentativas fazem com a conta#

Uma nova tentativa é uma nova requisição. Se uma tentativa foi processada, é uma requisição separada com seus próprios tokens. Digamos que um resumo de ticket tenha 1.500 tokens de entrada e 400 de saída: uma requisição custa $0.000063 nos preços de hoje, e a tabela de preços completa tem os outros modelos. Os preços são fixos por modelo, sem horário de pico, então uma nova tentativa às 3 da manhã custa o mesmo que uma ao meio-dia.

Mova os controles para o seu próprio lote:

Calculadora de custos

Um lote de 1.000 resumos de tickets

Tokens de entrada por solicitação: 1.500 · Tokens de saída por solicitação: 400 · Solicitações por dia: 1.000

ModeloPor diaPor mês
GLM-5.3-Flash$0.046$1.38
DeepSeek-V4-Flash$0.0625$1.88
MiniMax-M2.7$0.135$4.05

A tabela mostra uma passada. Se um décimo do lote precisar de uma segunda tentativa, adicione um décimo. As tentativas são um seguro barato quando as falhas são raras, e um hábito caro quando você define max_retries=10 contra um endpoint que continua falhando.

Antes de publicar#

Lista de verificação

Timeouts e tentativas, prontos para produção