# Como Lidar com Erros Comuns ao Consumir APIs de CPF em Ruby

> Aprenda a lidar com erros comuns ao consumir APIs de CPF em Ruby, incluindo timeouts, rate limiting, erros de rede e respostas inesperadas.

**Publicado:** 14/08/2024
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/lidar-erros-comuns-consumir-apis-cpf-ruby

---


Consumir APIs externas é uma operação inerentemente frágil: timeouts, erros de rede, rate limiting e respostas inesperadas são situações que toda aplicação deve estar preparada para enfrentar. Em Ruby, a forma como esses erros são tratados pode significar a diferença entre uma aplicação resiliente e uma que falha silenciosamente ou trava completamente.

## Introdução

Ao integrar uma API de CPF em produção, erros acontecem — e a maioria deles é previsível. Classificar cada tipo de falha e definir a resposta correta (retentar, registrar ou rejeitar) é o que separa um cliente HTTP ingênuo de uma integração confiável. Este guia apresenta o catálogo completo de erros e a implementação Ruby para tratá-los com segurança.

## Catálogo de erros comuns

Os erros ao consumir APIs podem ser agrupados em categorias com tratamentos distintos.

| Código HTTP | Tipo de Erro | Causa Provável | Ação Recomendada |
|---|---|---|---|
| 400 | Bad Request | CPF em formato inválido | Corrigir input, não retentar |
| 401 | Unauthorized | API key inválida ou ausente | Verificar configuração, não retentar |
| 403 | Forbidden | Plano sem acesso a este recurso | Verificar plano, não retentar |
| 404 | Not Found | CPF não encontrado na base | Registrar, não retentar |
| 429 | Too Many Requests | Rate limit excedido | Aguardar e retentar com backoff |
| 500 | Internal Server Error | Erro interno da API | Retentar com backoff |
| 502/503 | Bad Gateway / Unavailable | API temporariamente indisponível | Retentar com backoff |
| Timeout | Timeout | Rede lenta ou API sobrecarregada | Retentar com timeout maior |

**Erros de cliente (4xx)** -- geralmente não devem ser retentados pois o problema está na requisição.

**Erros de servidor (5xx)** -- são transitórios e devem ser retentados com backoff exponencial.

**Erros de rede** -- timeout, connection refused e DNS failure exigem retry com estratégia de backoff. A [OWASP](https://owasp.org/) recomenda tratar cada classe de erro de forma distinta para evitar que retentativas cegamente agravem uma situação de sobrecarga no servidor.

---

## Classe de exceções customizadas

Definir exceções customizadas permite tratamento granular em diferentes partes da aplicação.

```ruby
module CpfHub
 class Error < StandardError; end

 class ApiError < Error
 attr_reader :status_code, :response_body

 def initialize(message, status_code: nil, response_body: nil)
 @status_code = status_code
 @response_body = response_body
 super(message)
 end
 end

 class CpfInvalidoError < ApiError; end
 class AuthenticationError < ApiError; end
 class RateLimitError < ApiError
 attr_reader :retry_after

 def initialize(message, retry_after: 60, **kwargs)
 @retry_after = retry_after
 super(message, **kwargs)
 end
 end

 class CpfNaoEncontradoError < ApiError; end
 class ServerError < ApiError; end
 class TimeoutError < Error; end
 class NetworkError < Error; end
end
```

---

## Cliente HTTP com tratamento robusto de erros

Implemente um cliente que mapeia cada tipo de resposta para a exceção apropriada.

```ruby
require "faraday"
require "json"

module CpfHub
 class Client
 BASE_URL = "https://api.cpfhub.io"

 def initialize(api_key: ENV["CPFHUB_API_KEY"])
 @api_key = api_key
 @connection = build_connection
 end

 def consultar(cpf)
 cpf_limpo = cpf.to_s.gsub(/\D/, "")
 validar_formato_cpf!(cpf_limpo)

 resposta = @connection.get("/cpf/#{cpf_limpo}")
 tratar_resposta(resposta, cpf_limpo)
 rescue Faraday::TimeoutError => e
 raise CpfHub::TimeoutError,
 "Timeout ao consultar CPF: #{e.message}"
 rescue Faraday::ConnectionFailed => e
 raise CpfHub::NetworkError,
 "Falha de conexao: #{e.message}"
 end

 private

 def build_connection
 Faraday.new(url: BASE_URL) do |conn|
 conn.headers["x-api-key"] = @api_key
 conn.headers["Content-Type"] = "application/json"
 conn.options.timeout = 10
 conn.options.open_timeout = 5
 conn.adapter Faraday.default_adapter
 end
 end

 def validar_formato_cpf!(cpf)
 unless cpf.match?(/\A\d{11}\z/)
 raise CpfHub::CpfInvalidoError.new(
 "CPF deve conter exatamente 11 digitos",
 status_code: 400
 )
 end
 end

 def tratar_resposta(resposta, cpf)
 case resposta.status
 when 200
 body = JSON.parse(resposta.body)
 return body["data"] if body["success"]

 raise CpfHub::CpfNaoEncontradoError.new(
 "CPF #{cpf} nao encontrado",
 status_code: 200,
 response_body: resposta.body
 )
 when 401
 raise CpfHub::AuthenticationError.new(
 "API key invalida ou ausente",
 status_code: 401
 )
 when 429
 retry_after = resposta.headers["Retry-After"]&.to_i || 60
 raise CpfHub::RateLimitError.new(
 "Rate limit excedido",
 status_code: 429,
 retry_after: retry_after
 )
 when 500..599
 raise CpfHub::ServerError.new(
 "Erro interno da API (#{resposta.status})",
 status_code: resposta.status,
 response_body: resposta.body
 )
 else
 raise CpfHub::ApiError.new(
 "Resposta inesperada (#{resposta.status})",
 status_code: resposta.status,
 response_body: resposta.body
 )
 end
 end
 end
end
```

---

## Retry com backoff exponencial

Implemente retry inteligente que trata cada tipo de erro de forma diferente.

```ruby
module CpfHub
 class ResilientClient
 MAX_RETRIES = 3

 def initialize
 @client = Client.new
 end

 def consultar(cpf)
 tentativa = 0

 begin
 tentativa += 1
 @client.consultar(cpf)
 rescue CpfHub::RateLimitError => e
 if tentativa <= MAX_RETRIES
 delay = e.retry_after || (2**tentativa)
 log_retry(cpf, tentativa, delay, e)
 sleep(delay)
 retry
 end
 raise
 rescue CpfHub::ServerError, CpfHub::TimeoutError,
 CpfHub::NetworkError => e
 if tentativa <= MAX_RETRIES
 delay = (2**tentativa) + rand(0.0..1.0)
 log_retry(cpf, tentativa, delay, e)
 sleep(delay)
 retry
 end
 raise
 rescue CpfHub::CpfInvalidoError,
 CpfHub::AuthenticationError,
 CpfHub::CpfNaoEncontradoError
 raise # Não retentar erros de cliente
 end
 end

 private

 def log_retry(cpf, tentativa, delay, erro)
 puts "[CpfHub] Tentativa #{tentativa}/#{MAX_RETRIES} " \
 "para CPF #{cpf}. Retentando em #{delay.round(1)}s. " \
 "Erro: #{erro.class} - #{erro.message}"
 end
 end
end
```

| Tipo de Erro | Retentar | Delay | Máximo de Tentativas |
|---|---|---|---|
| CpfInvalidoError | Não | N/A | N/A |
| AuthenticationError | Não | N/A | N/A |
| CpfNaoEncontradoError | Não | N/A | N/A |
| RateLimitError | Sim | Retry-After header | 3 |
| ServerError | Sim | Exponencial + jitter | 3 |
| TimeoutError | Sim | Exponencial + jitter | 3 |
| NetworkError | Sim | Exponencial + jitter | 3 |

---

## Uso prático com tratamento de erros

Veja como consumir o cliente resiliente na aplicação, tratando cada cenário de erro.

```ruby
cliente = CpfHub::ResilientClient.new

begin
 dados = cliente.consultar("12345678901")
 puts "Nome: #{dados['name']}"
 puts "Data de nascimento: #{dados['birthDate']}"
rescue CpfHub::CpfInvalidoError => e
 puts "CPF em formato invalido: #{e.message}"
rescue CpfHub::CpfNaoEncontradoError => e
 puts "CPF nao encontrado na base de dados"
rescue CpfHub::AuthenticationError => e
 puts "Erro de autenticacao. Verifique a API key."
 # Notificar equipe de operações
rescue CpfHub::RateLimitError => e
 puts "Limite de requisicoes excedido. Tente novamente em #{e.retry_after}s"
rescue CpfHub::ServerError => e
 puts "API temporariamente indisponivel (#{e.status_code})"
 # Registrar para monitoramento
rescue CpfHub::TimeoutError => e
 puts "Timeout na consulta. Verifique a conectividade."
rescue CpfHub::NetworkError => e
 puts "Erro de rede: #{e.message}"
end
```

---

## Perguntas frequentes

### Quando devo retentar uma requisição e quando devo falhar imediatamente?
Erros de servidor (5xx) e erros de rede (timeout, connection refused) são transitórios e devem ser retentados com backoff exponencial — até 3 tentativas é o padrão seguro. Erros de cliente (4xx) indicam problema na requisição em si e nunca devem ser retentados: corrigir o input é o caminho certo.

### Como tratar o caso em que o CPF não é encontrado na API?
Um CPF não encontrado retorna HTTP 200 com `success: false`. Isso não é um erro de rede — é uma resposta válida indicando que o CPF não existe na base. O comportamento correto é registrar o resultado, não retentar, e bloquear o fluxo conforme a regra de negócio (ex: não emitir boleto, não aprovar cadastro).

### O que fazer quando a API CPFHub.io retorna erro 429?
A CPFHub.io não bloqueia o acesso quando o limite mensal do plano é atingido — cobra R$0,15 por consulta adicional. O erro 429 é uma resposta temporária de rate limiting por volume de requisições simultâneas. A resposta deve incluir o header `Retry-After` com o tempo de espera; use esse valor no backoff antes de retentar.

### Como proteger a API key em uma aplicação Ruby?
Armazene a chave em variável de ambiente (`ENV["CPFHUB_API_KEY"]`) e nunca a inclua no código-fonte ou em logs. Em Rails, use o `credentials.yml.enc` ou uma gem como `dotenv` para desenvolvimento local. Garanta também que os logs de requisição mascarem o número do CPF antes de gravar, conforme orientações de segurança em APIs REST.

### Leia também

- [Diferença entre validação de CPF e consulta de CPF: quando usar cada uma](https://cpfhub.io/blog/diferenca-entre-validacao-de-cpf-e-consulta-de-cpf-quando-usar-cada-uma)
- [API de CPF grátis para desenvolvedores: como começar em 5 minutos](https://cpfhub.io/blog/api-cpf-gratis-desenvolvedores-comecar-5-minutos)
- [Onboarding digital em fintechs: como validar CPF em menos de 30 segundos](https://cpfhub.io/blog/onboarding-digital-em-fintechs-como-validar-cpf-em-menos-de-30-segundos)
- [KYC no Brasil: quais setores são obrigados a validar CPF por lei](https://cpfhub.io/blog/kyc-no-brasil-quais-setores-sao-obrigados-a-validar-cpf-por-lei)

---

## Conclusão

O tratamento robusto de erros ao consumir APIs de CPF em Ruby é fundamental para construir aplicações resilientes. Exceções customizadas, retry inteligente com backoff exponencial e classificação correta dos erros garantem que a aplicação se recupere de falhas transitórias e falhe de forma informativa quando o problema está na requisição. Implementar essas práticas desde o início do projeto evita surpresas em produção e melhora significativamente a confiabilidade do sistema.

Cadastre-se em [cpfhub.io](https://www.cpfhub.io/) — 50 consultas mensais gratuitas, sem cartão de crédito — e comece a construir sua integração Ruby com tratamento de erros robusto desde o primeiro deploy.

