Integração segura com APIs de terceiros: retentativas, timeouts, circuit breakers
Integração segura com APIs de terceiros para manter seu app funcionando durante quedas. Aprenda sobre timeouts, retentativas, circuit breakers e verificações rápidas.

Por que APIs de terceiros podem travar seus fluxos principais
Uma API de terceiros pode falhar de maneiras que não parecem um evento claro de "queda". O problema mais comum é lentidão: requisições ficam pendentes, respostas chegam atrasadas e seu app continua esperando. Se essas chamadas estiverem no caminho crítico, um pequeno problema fora do seu controle se acumula dentro do sistema.
É assim que uma lentidão local vira um outage completo. Threads ou workers ficam presos esperando, filas crescem, transações no banco ficam abertas por mais tempo e novas requisições começam a estourar o timeout. Logo, até páginas que não usam a API externa parecem quebradas porque o sistema está sobrecarregado por trabalho em espera.
O impacto é concreto. Um provedor de identidade instável bloqueia cadastros e logins. Um timeout no gateway de pagamento congela o checkout, deixando o usuário incerto sobre se foi cobrado. Um atraso na entrega de mensagens interrompe reset de senha e confirmações de pedido, o que provoca uma segunda onda de retentativas e chamados ao suporte.
O objetivo é simples: isolar falhas externas para que os fluxos principais continuem funcionando. Isso pode significar permitir que o usuário finalize um pedido enquanto você confirma o pagamento depois, ou permitir o cadastro mesmo se o e-mail de boas-vindas falhar.
Uma métrica prática de sucesso: quando um provedor está lento ou fora, seu app ainda deve responder rápido e claramente, e a área afetada deve permanecer pequena. Por exemplo, a maioria das requisições principais ainda termina dentro do seu orçamento de latência normal, falhas ficam confinadas a recursos que realmente dependem daquela API, os usuários veem um status claro (enfileirado, pendente, tente mais tarde) e a recuperação acontece automaticamente quando o provedor volta.
Os modos de falha para os quais você deve se preparar
A maioria das falhas é previsível, mesmo que o momento não seja. Nomeie-as logo no começo e você poderá decidir o que retentar, o que parar e o que mostrar ao usuário.
As categorias comuns:
- Picos de latência (requisições que de repente ficam 10x mais lentas)
- Erros transitórios de servidor ou rede (timeouts, 502/503, reset de conexão)
- Limites de taxa e esgotamento de cota (429s, limites diários)
- Problemas de autenticação e permissão (chaves expiradas, acesso revogado)
- Dados ruins ou inesperados (campos faltando, formatos errados, respostas parciais)
Nem todos os erros significam a mesma coisa. Problemas transitórios costumam valer a pena retentar porque a próxima chamada pode dar certo (problemas de rede, timeouts, 502/503 e alguns 429s depois de esperar). Problemas permanentes normalmente não se auto corrigem (credenciais inválidas, endpoints errados, requisições malformadas, negações de permissão).
Tratar todo erro da mesma forma transforma um pequeno incidente em downtime. Retentar falhas permanentes desperdiça tempo, atinge limites de taxa mais rápido e cria um acúmulo que desacelera todo o resto. Nunca retentar falhas transitórias força usuários a repetirem ações e perde trabalho que poderia ter sido concluído poucos instantes depois.
Preste atenção extra a fluxos onde uma pausa parece uma quebra: checkout, login, redefinição de senha e notificações (email/SMS/push). Um pico de 2 segundos em uma API de marketing é irritante. Um pico de 2 segundos na autorização de pagamento bloqueia receita.
Um teste útil é: "Essa chamada precisa terminar para que o usuário conclua a tarefa principal agora?" Se sim, você precisa de timeouts apertados, retentativas cuidadosas e um caminho de falha claro. Se não, mova para uma fila e mantenha o app responsivo.
Timeouts: escolha um limite e mantenha
Timeout é o tempo máximo que você está disposto a esperar antes de parar e seguir em frente. Sem um limite claro, um provedor lento pode acumular requisições em espera e bloquear trabalho importante.
Ajuda separar dois tipos de espera:
- Connect timeout: quanto tempo você vai tentar estabelecer a conexão.
- Read timeout: quanto tempo você vai esperar por uma resposta depois de conectar.
Escolher números não é sobre perfeição. É sobre combinar com a paciência humana e seu fluxo de trabalho.
- Se um usuário está olhando para um spinner, normalmente você precisa de uma resposta rápida e um próximo passo claro.
- Se é um job em background (como sincronizar faturas durante a noite), você pode permitir mais tempo, mas ainda precisa de um teto para que não fique pendurado para sempre.
Uma forma prática de escolher timeouts é trabalhar de trás para frente a partir da experiência:
- Quanto tempo um usuário pode esperar antes que você precise mostrar uma mensagem clara?
- Se essa chamada falhar agora, você pode retentar depois ou usar um fallback?
- Quantas dessas chamadas são executadas em pico?
O trade-off é real. Tempo demais prende threads, workers e conexões de banco. Tempo de menos cria falsos erros e dispara retentativas desnecessárias.
Retentativas que não tornam o outage pior
Retentativas ajudam quando uma falha provavelmente é temporária: um breve problema de rede, um hiccup de DNS ou um 500/502/503 pontual. Nesses casos, uma segunda tentativa pode dar certo e o usuário nem nota.
O risco é uma tempestade de retentativas. Quando muitos clientes falham ao mesmo tempo e todos retentam juntos, podem sobrecarregar o provedor (e seus próprios workers). Backoff e jitter evitam isso.
Um orçamento de retentativas mantém as coisas honestas. Mantenha tentativas baixas e limite o tempo total para que fluxos principais não fiquem presos esperando por terceiros.
Receita padrão segura para retentativas
- Retentar apenas poucas vezes (frequentemente 1–3 tentativas no total, dependendo do fluxo).
- Use backoff exponencial (por exemplo 200ms, 500ms, 1s) mais jitter aleatório.
- Limite o tempo total gasto em retentativas (geralmente alguns segundos em fluxos com usuário esperando).
- Use um timeout por tentativa em vez de um único timeout longo para todas as tentativas.
Não retente erros previsíveis do cliente como 400/422 (validação), 401/403 (auth) ou 404. Eles quase sempre falharão de novo e só aumentam a carga.
Mais uma proteção: só retente operações de escrita (POST/PUT) quando você tiver idempotência em prática; caso contrário corre o risco de cobranças duplicadas ou registros duplicados.
Idempotência: torne retentativas seguras para fluxos reais
Idempotência significa que você pode executar a mesma requisição duas vezes e acabar com o mesmo resultado final. Isso importa porque retentativas são normais: redes caem, servidores reiniciam e clientes estouram timeout. Sem idempotência, uma retentativa “útil” cria duplicados e problemas de dinheiro real.
Imagine um checkout: a API de pagamento está lenta, seu app dá timeout e você retenta. Se a primeira chamada realmente teve sucesso, a retentativa pode gerar uma segunda cobrança. O mesmo risco acontece ao criar um pedido, iniciar uma assinatura, enviar um email/SMS, emitir um estorno ou criar um ticket de suporte.
A solução é anexar uma chave de idempotência (ou ID de requisição) a toda chamada que "faz algo". Ela deve ser única por ação do usuário, não por tentativa. O provedor (ou seu próprio serviço) usa essa chave para detectar duplicatas e retornar o mesmo resultado em vez de executar a ação novamente.
Trate a chave de idempotência como parte do modelo de dados, não como um header que você espera que ninguém esqueça.
Um padrão que resiste em produção
Gere uma chave quando o usuário inicia a ação (por exemplo, quando clica em Pagar) e então armazene-a com seu registro local.
A cada tentativa:
- Envie a mesma chave.
- Armazene o resultado final que recebeu (resposta de sucesso, código de falha, ID da cobrança).
- Se você já tiver um resultado registrado, retorne esse resultado em vez de repetir a ação.
Se você for o "provedor" para chamadas internas, aplique o mesmo comportamento no servidor.
Circuit breakers: pare de chamar a API quando ela estiver falhando
Um circuit breaker é um interruptor de segurança. Quando um serviço externo começa a falhar, você para de chamá-lo por um curto período em vez de acumular mais requisições que provavelmente vão estourar timeout.
Circuit breakers geralmente têm três estados:
- Fechado (Closed): as requisições fluem normalmente.
- Aberto (Open): chamadas são bloqueadas por uma janela de cooldown.
- Meio-aberto (Half-open): depois do cooldown, um pequeno número de chamadas de teste verifica se o serviço voltou.
Quando o breaker está aberto, seu app deve fazer algo previsível. Se uma API de validação de endereço estiver offline durante o cadastro, aceite o endereço e marque para revisão posterior. Se uma verificação de risco de pagamento cair, enfileire o pedido para revisão manual ou desative temporariamente essa opção e explique.
Escolha thresholds que casem com o impacto ao usuário:
- erros consecutivos (por exemplo 5 falhas seguidas)
- alta taxa de falha em uma janela curta
- muitas respostas lentas (timeouts)
- códigos de status específicos (como 503 repetidos)
Mantenha cooldowns curtos (segundos até um minuto) e limite as sondas half-open. O objetivo é proteger os fluxos principais primeiro e recuperar rapidamente.
Fallbacks e filas: mantenha o app utilizável
Quando uma API externa está lenta ou fora, seu objetivo é manter o usuário em movimento. Isso significa ter um Plano B honesto sobre o que aconteceu.
Fallbacks: escolha uma experiência “boa o suficiente”
Um fallback é o que seu app faz quando a API não responde a tempo. Opções incluem usar dados em cache, mudar para um modo degradado (ocultar widgets não essenciais, desativar ações opcionais), pedir entrada do usuário em vez de chamar a API (entrada manual de endereço) ou mostrar uma mensagem clara com o próximo passo.
Seja honesto: não diga que algo foi concluído se não foi.
Filas: faça depois quando "agora" não for necessário
Se o trabalho não precisa terminar dentro da requisição do usuário, empurre para uma fila e responda rápido. Candidatos comuns: envio de emails, sincronização com CRM, geração de relatórios e postagem de eventos de analytics.
Falhe rápido para ações críticas. Se uma API não é necessária para concluir o checkout (ou criação de conta), não bloqueie a requisição. Aceite o pedido, enfileire a chamada externa e reconcilie depois. Se a API for necessária (por exemplo, autorização de pagamento), falhe rapidamente com uma mensagem clara e não mantenha o usuário esperando.
O que o usuário vê deve corresponder ao que acontece por trás: um status claro (concluído, pendente, falhou), uma promessa que você pode cumprir (recibo agora, confirmação depois), um jeito de retentar e um registro visível na UI (log de atividades, badge de pendente).
Limites de taxa e carga: evite falhas auto-infligidas
Rate limits são a forma do provedor dizer: "Você pode nos chamar, mas não com muita frequência." Você os atingirá mais cedo do que imagina: picos de tráfego, jobs em background que disparam juntos ou um bug que entra em loop sobre erros.
Comece controlando quantas requisições você gera. Agrupe quando possível, faça cache de respostas mesmo por 30–60 segundos quando for seguro, e limite no cliente para que seu app nunca dispare mais rápido do que o provedor permite.
Quando receber um 429 Too Many Requests, trate como um sinal para desacelerar:
- Respeite
Retry-Afterquando for fornecido. - Adicione jitter para que muitos workers não retentem ao mesmo tempo.
- Limite retentativas para 429s para evitar loops infinitos.
- Faça backoff mais agressivo em 429s repetidos.
- Registre isso como métrica para notar padrões antes dos usuários.
Também limite concorrência. Um único fluxo (como sincronizar contatos) não deve consumir todas as vagas de worker e deixar fluxos críticos como login ou checkout sem recursos. Pools separados ou limites por recurso ajudam.
Passo a passo: receita padrão segura de integração
Toda chamada de terceiros precisa de um plano para falhas. Você não precisa de perfeição. Precisa de comportamento previsível quando o provedor tiver um dia ruim.
1) Classifique a chamada (essencial vs. pode esperar)
Decida o que acontece se a chamada falhar agora. Um cálculo de imposto durante o checkout pode ser essencial. Sincronizar um contato de marketing geralmente pode esperar. Essa escolha dirige o resto.
2) Defina timeouts e um orçamento de retentativas
Escolha timeouts por tipo de chamada e mantenha consistência. Depois configure um orçamento de retentativas para não ficar martelando uma API lenta.
- Essencial, usuário aguardando: timeout curto, 0–1 retentativa.
- Pode esperar, job em background: timeout maior, algumas retentativas com backoff.
- Nunca retentar para sempre: limite o tempo total por tarefa.
3) Torne retentativas seguras com idempotência e rastreamento
Se uma requisição pode criar algo ou cobrar dinheiro, adicione chaves de idempotência e armazene um registro da requisição. Se um pedido de pagamento expira, uma retentativa não deve cobrar em dobro. O rastreamento também ajuda o suporte a responder: "Isso foi processado?"
4) Adicione um circuit breaker e comportamento de fallback
Quando os erros aumentarem, pare de chamar o provedor por um curto período. Para chamadas essenciais, mostre um caminho claro de "Tentar novamente". Para chamadas que podem esperar, enfileire o trabalho e processe depois.
5) Monitore o básico
Monitore latência, taxa de erros e eventos de breaker aberto/fechado. Alarme em mudanças sustentadas, não em piscadas únicas.
Erros comuns que transformam um problema pequeno em downtime
A maioria dos outages de API não começa grande. Eles viram grandes porque seu app reage da pior forma: espera tempo demais, retenta agressivamente e consome os mesmos workers que mantêm todo o resto funcionando.
Esses padrões causam cascatas:
- Retentar toda falha, incluindo 4xx como requisições inválidas, auth expirado ou falta de permissão.
- Configurar timeouts muito longos "para ter certeza", que silenciosamente consomem threads, conexões de BD ou workers até acabar a capacidade.
- Retentar ações de criação sem chaves de idempotência, levando a cobranças duplicadas, envios duplicados ou registros repetidos.
- Circuit breakers mal configurados que nunca se recuperam ou que abrem e fecham constantemente.
- Tratar outages parciais como falha total em vez de degradar apenas a funcionalidade afetada.
Pequenas correções previnem grandes outages: retente apenas erros provavelmente temporários (timeouts, alguns 429s, alguns 5xx) e limite tentativas com backoff e jitter; mantenha timeouts curtos e intencionais; exija idempotência para qualquer operação que crie ou cobre; e projete para falhas parciais.
Checklist rápido antes de enviar para produção
Antes de colocar uma integração em produção, faça uma verificação rápida com mentalidade de falha. Se você não consegue responder "sim" a um item, trate como bloqueador de release para fluxos principais como cadastro, checkout ou envio de mensagens.
- Limites de tempo são explícitos (connect timeout e read/response timeout).
- Retentativas são limitadas (pequeno orçamento de retentativas, backoff, jitter e limite de tempo total).
- Retentativas são seguras para ações reais (chaves de idempotência ou verificações claras de deduplicação).
- Existe um breaker e um plano B (fallback, modo degradado ou fila).
- Você consegue enxergar problemas cedo (latência, taxa de erro e saúde da dependência por provedor e endpoint).
Se um provedor de pagamento começa a ficar lento, o comportamento certo é "o checkout ainda carrega, o usuário vê uma mensagem clara e você não fica preso para sempre", não "tudo fica travado até estourar o timeout."
Exemplo: protegendo o checkout quando um provedor está instável
Imagine um checkout que chama três serviços: uma API de pagamento para cobrar o cartão, uma API de impostos para calcular tributos e uma API de email para enviar o recibo.
A chamada de pagamento é a única que precisa ser síncrona. Problemas em imposto ou email não devem travar a compra.
Quando a API de impostos está lenta
Suponha que a API de impostos às vezes leve 8 a 15 segundos. Se o checkout esperar, os usuários abandonam o carrinho e seu app prende workers.
Um fluxo mais seguro:
- Defina um timeout rígido (por exemplo 800ms a 2s) e falhe rápido.
- Retente no máximo uma vez, apenas se for seguro, com jitter.
- Se o timeout ocorrer, use uma tabela em cache ou taxa conhecida para a região do comprador.
- Se você não puder usar taxas em cache por questões legais, coloque o pedido como "pendente de imposto" e enfileire uma recalculação.
Resultado: menos carrinhos abandonados e menos pedidos travados quando o provedor de impostos está lento.
Quando a API de email está fora
O email de recibo importa, mas nunca deve bloquear a captura do pagamento. Se a API de email falhar, o circuit breaker deve abrir após algumas falhas rápidas e impedir chamadas por uma janela de cooldown.
Em vez de enviar o email de forma síncrona, enfileire um job "enviar recibo" com uma chave de idempotência (por exemplo order_id + email_type). Se o provedor estiver fora, a fila retenta em background e o cliente ainda vê uma compra bem-sucedida.
Resultado: menos chamados ao suporte por confirmações ausentes e nenhuma perda de receita por falhas não relacionadas ao pagamento.
Próximos passos: aplicar isso com segurança no seu app
Escolha um fluxo que mais te prejudica quando quebra (checkout, cadastro, faturamento) e faça dele sua integração de referência. Depois copie os mesmos padrões para todo lugar.
Uma ordem simples de rollout:
- Defina timeouts e falhe rápido com uma mensagem clara.
- Adicione retentativas com backoff, mas apenas para erros retryable.
- Adicione idempotência para que retentativas não dupliquem cobranças ou registros.
- Adicione circuit breakers para que um provedor ruim não trave seu fluxo principal.
Escreva seus defaults e mantenha-os simples: um connect timeout, um request timeout, contagem máxima de retentativas, faixa de backoff, cooldown do breaker e as regras do que é retryable.
Faça um exercício de falha antes de expandir para o próximo fluxo. Force timeouts (ou bloqueie o provedor em um ambiente de teste) e então confirme que o usuário vê uma mensagem útil, os fallbacks funcionam e retentativas enfileiradas não acumulam para sempre.
Se você está construindo produtos novos rapidamente, vale transformar esses defaults de confiabilidade em um template reaproveitável. Para times que usam a Koder.ai (koder.ai), isso frequentemente significa definir timeout, retentativas, idempotência e regras de breaker uma vez, e então aplicar o mesmo padrão em novos serviços conforme você gera e itera.
Perguntas frequentes
Por que uma API de terceiros lenta pode derrubar meu app?
Defina timeouts explícitos de conexão e leitura para cada chamada. Escolha um limite curto para fluxos voltados ao usuário e, em seguida, retorne um resultado claro ou envie tarefas não essenciais para uma fila.
Quais erros de API devo tentar novamente?
Tente novamente em caso de timeouts, redefinições de conexão e alguns erros 5xx, pois eles podem ser resolvidos rapidamente. Não tente novamente solicitações inválidas, credenciais incorretas, erros de permissão ou endpoints inexistentes.
Quantas vezes devo tentar novamente uma solicitação de API?
Mantenha poucas tentativas, geralmente de uma a três tentativas no total. Use backoff exponencial com jitter aleatório e limite o tempo total gasto nas novas tentativas.
Como evito cobranças duplicadas quando uma solicitação de pagamento expira?
Use uma chave de idempotência para ações que criam registros, enviam mensagens, emitem reembolsos ou fazem cobranças. Reutilize a mesma chave em cada tentativa originada de uma única ação do usuário.
O que faz um disjuntor?
Um disjuntor interrompe temporariamente as chamadas após falhas ou timeouts repetidos. Ele protege seus workers e permite que o provedor se recupere antes de você enviar novamente solicitações de teste limitadas.
O que devo mover para uma fila em segundo plano?
Mantenha o checkout e o login responsivos isolando chamadas opcionais. Coloque em fila os e-mails de recibo, atualizações de CRM e análises, em vez de esperar por eles durante a solicitação do usuário.
Como meu app deve lidar com uma resposta 429 de limite de taxa?
Respeite o valor Retry-After do provedor quando ele estiver presente, reduza a concorrência e adicione jitter antes de tentar novamente. Armazene em cache ou agrupe leituras seguras para que seu app faça menos chamadas.
O que os usuários devem ver quando um serviço externo falha?
Mostre o estado real: concluído, pendente ou com falha. Dê aos usuários um próximo passo prático, como tentar o pagamento novamente ou verificar um pedido pendente mais tarde.
O que devo monitorar em APIs de terceiros?
Acompanhe a latência, a taxa de erros, a taxa de timeouts, as respostas de limite de taxa e os eventos do disjuntor para cada provedor e endpoint. Crie alertas para mudanças persistentes, assim você identifica problemas cedo.
Como testo uma integração de API antes do lançamento?
Teste timeouts, respostas parciais, 429s, erros 5xx e solicitações de escrita repetidas. Confirme que seu fallback funciona, os trabalhos em fila permanecem limitados e as novas tentativas não duplicam o trabalho.