# Como repetir requisições com backoff exponencial (retry)

> Chamadas de rede falham de vez em quando: o servidor fica sobrecarregado, a conexão cai, a API pede para esperar. Repetir na hora piora o problema. Este guia mostra como repetir com backoff exponencial e jitter, quais respostas vale repetir (429 e 5xx, mas não 404) e como evitar que uma nova tentativa faça a mesma cobrança duas vezes.

- Endereço oficial: https://pradev.com.br/guias/como-repetir-requisicoes-com-backoff
- Tipo: Guia
- Publicado em: 2026-10-01
- Atualizado em: 2026-10-01
- Autor: Leonardo Gaertner (https://pradev.com.br/sobre)

## Por que esperar cada vez mais

Se mil clientes repetem a chamada na mesma hora em que o servidor caiu, ele volta e cai de novo. O backoff exponencial dobra a espera a cada tentativa (200 ms, 400 ms, 800 ms...), com um teto. O jitter sorteia a espera entre zero e esse limite, para os clientes não voltarem todos juntos.

## O que vale repetir

- Falhas de rede e tempo esgotado: podem passar na próxima tentativa.
- 429 (muitas requisições) e 503 (indisponível): a API pede para esperar. Se a resposta trouxer o cabeçalho `Retry-After`, espere o tempo indicado.
- Outros 5xx: costumam ser temporários.
- Não repita 400, 401, 403, 404 e 422: a resposta não muda enquanto o pedido for o mesmo, e repetir só gasta tempo e cota.

## Em JavaScript

A função `comRetry` serve para qualquer tarefa assíncrona, e `buscarComRetry` aplica as regras acima ao `fetch`. No `fetch`, uma falha de rede aparece como `TypeError`, enquanto respostas de erro, como 503, chegam normalmente e precisam ser conferidas pelo `status`.

```javascript
const esperar = (ms) => new Promise((ok) => setTimeout(ok, ms));

// Repete a tarefa quando ela falha, esperando cada vez mais entre as tentativas
async function comRetry(tarefa, opcoes = {}) {
  const { tentativas = 5, baseMs = 200, maxMs = 5000, repetirSe = () => true } = opcoes;
  for (let i = 0; ; i++) {
    try {
      return await tarefa();
    } catch (erro) {
      if (i + 1 >= tentativas || !repetirSe(erro)) throw erro;
      // Backoff exponencial com jitter: espera um tempo aleatório entre 0 e o limite da vez
      const limite = Math.min(maxMs, baseMs * 2 ** i);
      await esperar(Math.random() * limite);
    }
  }
}

// Só vale repetir quando o problema pode passar: 429 (muitas requisições) e erros 5xx
async function buscarComRetry(url, opcoes) {
  return comRetry(async () => {
    const resposta = await fetch(url, opcoes);
    if (resposta.status === 429 || resposta.status >= 500) {
      throw Object.assign(new Error(`HTTP ${resposta.status}`), { temporario: true });
    }
    return resposta; // outros 4xx não mudam com nova tentativa
  }, { repetirSe: (e) => e.temporario || e.name === 'TypeError' }); // TypeError: falha de rede
}
```

_JavaScript_

## Em Python e PHP

O `repetir_se` recebe o erro e decide se vale tentar de novo. Em Python, bibliotecas como `tenacity` e o `Retry` do urllib3 já fazem isso com mais opções.

```python
import random
import time


def com_retry(tarefa, tentativas=5, base=0.2, maximo=5.0, repetir_se=lambda erro: True):
    """Repete a tarefa quando ela falha, esperando cada vez mais entre as tentativas."""
    for i in range(tentativas):
        try:
            return tarefa()
        except Exception as erro:
            if i + 1 == tentativas or not repetir_se(erro):
                raise
            # Backoff exponencial com jitter: entre 0 e o limite da vez
            time.sleep(random.uniform(0, min(maximo, base * 2**i)))
```

_Python_

```php
<?php
// Repete a tarefa quando ela lança exceção, esperando cada vez mais entre as tentativas
function comRetry(
    callable $tarefa,
    int $tentativas = 5,
    int $baseMs = 200,
    int $maxMs = 5000,
    ?callable $repetirSe = null
) {
    for ($i = 0; ; $i++) {
        try {
            return $tarefa();
        } catch (Throwable $erro) {
            if ($i + 1 >= $tentativas || ($repetirSe && !$repetirSe($erro))) {
                throw $erro;
            }
            $limite = min($maxMs, $baseMs * 2 ** $i);
            usleep(random_int(0, $limite) * 1000); // jitter: entre 0 e o limite da vez
        }
    }
}
```

_PHP_

## Não cobre duas vezes

Uma requisição pode chegar ao servidor e a resposta se perder no caminho. Se você repetir uma criação de pedido ou um pagamento, pode fazer tudo de novo. Repita sem medo só operações idempotentes, que dão o mesmo resultado se feitas duas vezes, como um GET ou um PUT que grava o mesmo valor.

- Para criações e pagamentos, envie uma chave de idempotência: um identificador único por operação, no cabeçalho `Idempotency-Key`, que muitas APIs de pagamento aceitam. O servidor reconhece a repetição e devolve o resultado da primeira vez.
- Gere essa chave uma vez, antes da primeira tentativa, por exemplo com um [UUID](https://pradev.com.br/gerador-de-uuid), e mande a mesma em todas as repetições.
- Limite o tempo total, não só o número de tentativas: o usuário não deve esperar um minuto por uma resposta.

## Perguntas frequentes

### O que é backoff exponencial?

É dobrar o tempo de espera a cada nova tentativa, como 200 ms, 400 ms, 800 ms, até um limite. Assim o cliente insiste sem sobrecarregar um servidor que já está com problema.

### Para que serve o jitter?

Para os clientes não tentarem todos ao mesmo tempo. Em vez de esperar exatamente o limite, cada um sorteia um tempo entre zero e o limite, e as novas tentativas se espalham.

### Devo repetir uma requisição que deu 404?

Não. Erros 4xx, exceto o 429, indicam que o pedido em si está errado ou não é permitido, e a resposta será a mesma. Repita falhas de rede, 429 e 5xx.

### Como evitar cobrança duplicada ao repetir um pagamento?

Envie uma chave de idempotência, gerada uma vez antes da primeira tentativa e repetida em todas. A API de pagamento reconhece a repetição e não cobra de novo.

## Fontes

- [AWS: Exponential Backoff and Jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/)
- [MDN: cabeçalho Retry-After](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Retry-After)

## Guias relacionados

- [Como buscar o endereço pelo CEP com o ViaCEP](https://pradev.com.br/guias/como-buscar-endereco-pelo-cep-viacep): Como consultar o endereço de um CEP no ViaCEP em JavaScript, Python e PHP, tratar CEP inexistente e erros de rede e preencher o formulário sozinho.
- [Como gerar PIX copia e cola (BR Code) em código](https://pradev.com.br/guias/como-gerar-pix-copia-e-cola): Como montar o código PIX copia e cola (BR Code estático) em JavaScript, Python e PHP: os campos, o CRC16 e os erros que fazem o banco recusar o código.
