# Como validar CPF em tempo real usando Elixir e APIs REST

> Aprenda a validar CPF em tempo real em Elixir combinando validação algorítmica com consulta à API REST do CPFHub.

**Publicado:** 18/11/2024
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/como-validar-cpf-tempo-real-elixir-apis-rest

---


Validar CPF em tempo real com Elixir combina dois passos: verificação algorítmica dos dígitos verificadores (< 0,1ms, sem rede) e consulta à API REST do CPFHub.io para confirmar os dados cadastrais (~900ms). O modelo de processos leves da BEAM VM permite executar centenas dessas validações em paralelo sem degradar a performance da aplicação.

## Introdução

A validação de CPF em tempo real é crucial para garantir a qualidade dos dados em formulários de cadastro. Elixir, com seu modelo de concorrência baseado em processos leves, é ideal para esse tipo de operação que combina processamento local rápido com chamadas assíncronas a APIs externas.

---

## Validação algorítmica do CPF

Implemente a verificação dos dígitos verificadores de forma funcional e idiomática em Elixir:

```elixir
defmodule CpfValidator do
 @moduledoc """
 Módulo para validação algorítmica de CPF.
 """

 @doc """
 Valida os dígitos verificadores de um CPF.
 """
 def validar(cpf) when is_binary(cpf) do
 cpf
 |> String.replace(~r/\D/, "")
 |> validar_digitos()
 end

 defp validar_digitos(cpf) when byte_size(cpf) != 11, do: {:error, :tamanho_invalido}

 defp validar_digitos(cpf) do
 digitos = cpf |> String.graphemes() |> Enum.map(&String.to_integer/1)

 if Enum.uniq(digitos) |> length() == 1 do
 {:error, :digitos_repetidos}
 else
 d1 = calcular_digito(digitos, 9, 10)
 d2 = calcular_digito(digitos, 10, 11)

 if Enum.at(digitos, 9) == d1 and Enum.at(digitos, 10) == d2 do
 :ok
 else
 {:error, :digitos_verificadores_invalidos}
 end
 end
 end

 defp calcular_digito(digitos, count, peso_inicial) do
 soma =
 digitos
 |> Enum.take(count)
 |> Enum.with_index()
 |> Enum.reduce(0, fn {d, i}, acc -> acc + d * (peso_inicial - i) end)

 resto = rem(soma, 11)
 if resto < 2, do: 0, else: 11 - resto
 end
end
```

---

## Serviço de validação em tempo real

Combine a validação local com a consulta à API para fornecer um resultado completo:

```elixir
defmodule CpfValidationService do
 @moduledoc """
 Serviço de validação de CPF em tempo real com consulta à API.
 """

 alias CpfValidator

 @base_url "https://api.cpfhub.io"

 def validar_completo(cpf, api_key) do
 cpf_limpo = String.replace(cpf, ~r/\D/, "")

 with :ok <- CpfValidator.validar(cpf_limpo),
 {:ok, dados} <- consultar_api(cpf_limpo, api_key) do
 {:ok, %{
 formato_valido: true,
 encontrado: true,
 nome: dados["name"],
 data_nascimento: dados["birthDate"],
 genero: dados["gender"],
 mensagem: "CPF válido e encontrado na base"
 }}
 else
 {:error, :tamanho_invalido} ->
 {:error, %{formato_valido: false, mensagem: "CPF deve conter 11 dígitos"}}

 {:error, :digitos_repetidos} ->
 {:error, %{formato_valido: false, mensagem: "CPF com dígitos repetidos"}}

 {:error, :digitos_verificadores_invalidos} ->
 {:error, %{formato_valido: false, mensagem: "Dígitos verificadores inválidos"}}

 {:error, :cpf_nao_encontrado} ->
 {:ok, %{
 formato_valido: true,
 encontrado: false,
 nome: nil,
 data_nascimento: nil,
 genero: nil,
 mensagem: "CPF válido, porém não encontrado na base"
 }}

 {:error, reason} ->
 {:error, %{formato_valido: true, mensagem: "Erro: #{inspect(reason)}"}}
 end
 end

 defp consultar_api(cpf, api_key) do
 headers = [{"x-api-key", api_key}]
 url = "#{@base_url}/cpf/#{cpf}"

 case HTTPoison.get(url, headers, recv_timeout: 5000) do
 {:ok, %{status_code: 200, body: body}} ->
 case Jason.decode!(body) do
 %{"success" => true, "data" => data} -> {:ok, data}
 _ -> {:error, :cpf_nao_encontrado}
 end

 {:ok, %{status_code: 404}} ->
 {:error, :cpf_nao_encontrado}

 {:error, reason} ->
 {:error, {:falha_api, reason}}
 end
 end
end
```

| Etapa | Tempo | Recurso utilizado |
|---|---|---|
| Limpeza do CPF | < 0.1ms | Regex local |
| Validação de dígitos | < 0.1ms | Cálculo aritmético |
| Consulta à API | ~900ms | HTTP via rede |
| Resposta total | ~900ms | Resultado combinado |

---

## Integrando com LiveView para feedback instantâneo

O Phoenix LiveView permite validação em tempo real com feedback visual imediato:

```elixir
defmodule MinhaAppWeb.CpfValidationLive do
 use MinhaAppWeb, :live_view

 def mount(_params, _session, socket) do
 {:ok, assign(socket,
 cpf_input: "",
 validacao_local: nil,
 resultado_api: nil,
 loading: false
 )}
 end

 def handle_event("validar_local", %{"cpf" => cpf}, socket) do
 resultado = CpfValidator.validar(cpf)
 {:noreply, assign(socket, cpf_input: cpf, validacao_local: resultado)}
 end

 def handle_event("consultar_api", %{"cpf" => cpf}, socket) do
 socket = assign(socket, loading: true)
 api_key = Application.get_env(:minha_app, :cpfhub)[:api_key]

 case CpfValidationService.validar_completo(cpf, api_key) do
 {:ok, resultado} ->
 {:noreply, assign(socket, resultado_api: resultado, loading: false)}

 {:error, resultado} ->
 {:noreply, assign(socket, resultado_api: resultado, loading: false)}
 end
 end

 def render(assigns) do
 ~H"""
 <div class="max-w-md mx-auto">
 <h2>Validação de CPF</h2>

 <form phx-change="validar_local" phx-submit="consultar_api">
 <input type="text" name="cpf" value={@cpf_input}
 placeholder="000.000.000-00" maxlength="14"
 class="form-input w-full" />

 <button type="submit" disabled={@loading} class="btn mt-2">
 <%= if @loading, do: "Consultando...", else: "Consultar" %>
 </button>
 </form>

 <%= if @resultado_api do %>
 <div class="mt-4 p-4 border rounded">
 <p><strong>Status:</strong> <%= @resultado_api.mensagem %></p>
 <%= if @resultado_api[:nome] do %>
 <p><strong>Nome:</strong> <%= @resultado_api.nome %></p>
 <p><strong>Nascimento:</strong> <%= @resultado_api.data_nascimento %></p>
 <% end %>
 </div>
 <% end %>
 </div>
 """
 end
end
```

---

## Adicionando cache com ETS

Para evitar consultas repetidas, utilize ETS como cache em memória:

```elixir
defmodule CpfCache do
 use GenServer

 @table_name :cpf_cache
 @ttl_seconds 600

 def start_link(_opts) do
 GenServer.start_link(__MODULE__, [], name: __MODULE__)
 end

 def init(_) do
 :ets.new(@table_name, [:named_table, :public, read_concurrency: true])
 {:ok, %{}}
 end

 def get(cpf) do
 case :ets.lookup(@table_name, cpf) do
 [{^cpf, resultado, timestamp}] ->
 if System.system_time(:second) - timestamp < @ttl_seconds do
 {:ok, resultado}
 else
 :ets.delete(@table_name, cpf)
 :miss
 end
 [] -> :miss
 end
 end

 def put(cpf, resultado) do
 :ets.insert(@table_name, {cpf, resultado, System.system_time(:second)})
 :ok
 end
end
```

---

## Perguntas frequentes

### Por que separar a validação algorítmica da consulta à API em Elixir?

A validação algorítmica (dígitos verificadores) é local e executa em menos de 0,1ms — ela elimina CPFs estruturalmente inválidos antes de qualquer chamada de rede. Isso evita consumo desnecessário de quota da API e reduz a latência para entradas inválidas. Apenas CPFs que passam na validação local disparam a consulta HTTP ao CPFHub.io.

### Como processar múltiplas validações de CPF em paralelo com Elixir?

Use `Task.async_stream/3` para paralelizar as consultas dentro do limite de concorrência desejado. A BEAM VM agenda os processos leves de forma eficiente, e cada consulta à API (~900ms) é executada de forma independente. Para cenários de alta escala, considere [Oban](https://hexdocs.pm/oban) para gerenciar filas de validação em background.

### A API CPFHub.io retorna HTTP 429 quando o limite é atingido?

Não. A CPFHub.io não bloqueia e não retorna erro 429: consultas excedentes são cobradas a R$ 0,15 cada. O plano gratuito oferece 50 consultas mensais sem cartão de crédito; o plano Pro inclui 1.000 consultas por R$ 149/mês. Isso simplifica o tratamento de erros no código Elixir — não é necessário lidar com backoff por limite de taxa.

### Como garantir conformidade com a LGPD ao usar a API de CPF em aplicações Elixir?

Use o CPF apenas para a finalidade declarada ao titular, armazene apenas o necessário (não persista o CPF cru se um token bastar), implemente controle de acesso aos logs de consulta e documente a base legal para o tratamento. A [ANPD](https://www.gov.br/anpd) orienta que dados de identificação devem ser tratados com o princípio da necessidade.

### Leia também

- [Como integrar a API de CPF em uma aplicação Phoenix](https://cpfhub.io/blog/como-integrar-api-cpf-aplicacao-phoenix)
- [Como integrar validação de CPF em Elixir com Phoenix LiveView](https://cpfhub.io/blog/como-integrar-validacao-de-cpf-em-elixir-com-phoenix-liveview)
- [Como consumir a API de CPF em Elixir usando HTTPoison e Tesla](https://cpfhub.io/blog/como-consumir-api-cpf-elixir-httpoison-tesla)
- [Como usar Oban para processar consultas de CPF em background no Elixir](https://cpfhub.io/blog/como-usar-oban-processar-consultas-cpf-background-elixir)

---

## Conclusão

A validação de CPF em tempo real com Elixir combina a eficiência da BEAM VM com a riqueza de dados da API. O modelo de concorrência do Elixir permite processar múltiplas validações simultaneamente sem degradação de performance, e o cache com ETS elimina consultas redundantes.

Cadastre-se em [cpfhub.io](https://www.cpfhub.io/) — 50 consultas mensais gratuitas, sem cartão de crédito — e integre a validação de CPF na sua aplicação Elixir com dados cadastrais reais em produção.

