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.

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.
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:
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.
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:
Escolher números não é sobre perfeição. É sobre combinar com a paciência humana e seu fluxo de trabalho.
Uma forma prática de escolher timeouts é trabalhar de trás para frente a partir da experiência:
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 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.
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 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.
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:
Se você for o "provedor" para chamadas internas, aplique o mesmo comportamento no servidor.
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:
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:
Mantenha cooldowns curtos (segundos até um minuto) e limite as sondas half-open. O objetivo é proteger os fluxos principais primeiro e recuperar rapidamente.
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.
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.
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).
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:
Retry-After quando for fornecido.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.
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.
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.
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.
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?"
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.
Monitore latência, taxa de erros e eventos de breaker aberto/fechado. Alarme em mudanças sustentadas, não em piscadas únicas.
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:
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.
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.
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."
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.
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:
Resultado: menos carrinhos abandonados e menos pedidos travados quando o provedor de impostos está lento.
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.
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:
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.