# Como consumir API de CPF em Rust com reqwest e serde

> Aprenda a consumir a API de consulta de CPF em Rust usando reqwest e serde. Guia completo com exemplos de código, tratamento de erros e boas práticas.

**Publicado:** 07/03/2025
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/como-consumir-api-de-cpf-em-rust-com-reqwest-e-serde

---


Para consumir a API de CPF da CPFHub.io em Rust, use `reqwest` para as requisições HTTP assíncronas e `serde` para deserializar o JSON de resposta. O padrão recomendado combina erros tipados com `thiserror`, timeout configurável no `Client::builder()` e a chave de autenticação lida de variável de ambiente — uma integração segura e idiomática em Rust.

## Introdução

Rust vem ganhando popularidade entre desenvolvedores que buscam performance, segurança de memória e controle fino sobre recursos do sistema. Em aplicações que lidam com dados sensíveis, como sistemas de validação de identidade, as garantias de segurança do Rust são um diferencial significativo.

A validação de CPF via API é uma necessidade comum em sistemas brasileiros que realizam cadastro de usuários, onboarding digital ou processamento de transações.

---

## Pré-requisitos

* **Rust 1.75+** -- Instalado via rustup.
* **Conta na CPFHub.io** -- Para obter a chave de API (50 consultas/mês no plano gratuito).

### Criando o projeto

```bash
cargo new cpfhub-rust
cd cpfhub-rust
```

### Dependências no Cargo.toml

```toml
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1", features = ["full"] }
thiserror = "1.0"
```

---

## Modelando a resposta da API

Defina as structs com derive de Deserialize:

```rust
use serde::Deserialize;

#[derive(Debug, Deserialize)]
pub struct CpfResponse {
 pub success: bool,
 pub data: Option<CpfData>,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CpfData {
 pub cpf: String,
 pub name: String,
 pub name_upper: String,
 pub gender: String,
 pub birth_date: String,
 pub day: u32,
 pub month: u32,
 pub year: u32,
}
```

Resposta esperada da API:

```json
{
 "success": true,
 "data": {
 "cpf": "12345678900",
 "name": "João da Silva",
 "nameUpper": "JOÃO DA SILVA",
 "gender": "M",
 "birthDate": "15/06/1990",
 "day": 15,
 "month": 6,
 "year": 1990
 }
}
```

---

## Definindo erros customizados

Use a crate `thiserror` para definir erros tipados:

```rust
use thiserror::Error;

#[derive(Error, Debug)]
pub enum CpfError {
 #[error("CPF inválido: informe 11 dígitos numéricos")]
 CpfInvalido,

 #[error("CPF não encontrado na base de dados")]
 CpfNaoEncontrado,

 #[error("Consulta cobrada como excedente (R$0,15): verifique seu saldo")]
 ExcedenteDeConsulta,

 #[error("A requisição excedeu o tempo limite")]
 Timeout,

 #[error("Erro HTTP: {status}")]
 HttpError { status: u16 },

 #[error("Erro na requisição: {0}")]
 RequestError(#[from] reqwest::Error),
}
```

---

## Implementando o cliente

```rust
use reqwest::header::{HeaderMap, HeaderValue, ACCEPT};
use std::time::Duration;

pub struct CpfClient {
 client: reqwest::Client,
 base_url: String,
}

impl CpfClient {
 pub fn new(api_key: &str) -> Result<Self, CpfError> {
 let mut headers = HeaderMap::new();
 headers.insert(
 "x-api-key",
 HeaderValue::from_str(api_key)
 .map_err(|_| CpfError::CpfInvalido)?,
 );
 headers.insert(
 ACCEPT,
 HeaderValue::from_static("application/json"),
 );

 let client = reqwest::Client::builder()
 .default_headers(headers)
 .timeout(Duration::from_secs(10))
 .build()?;

 Ok(Self {
 client,
 base_url: "https://api.cpfhub.io".to_string(),
 })
 }

 pub async fn consultar_cpf(&self, cpf: &str) -> Result<CpfData, CpfError> {
 let cpf_limpo: String = cpf.chars().filter(|c| c.is_ascii_digit()).collect();

 if cpf_limpo.len() != 11 {
 return Err(CpfError::CpfInvalido);
 }

 let url = format!("{}/cpf/{}", self.base_url, cpf_limpo);

 let response = self.client.get(&url).send().await.map_err(|e| {
 if e.is_timeout() {
 CpfError::Timeout
 } else {
 CpfError::RequestError(e)
 }
 })?;

 let status = response.status();

 if !status.is_success() {
 return Err(CpfError::HttpError {
 status: status.as_u16(),
 });
 }

 let cpf_response: CpfResponse = response.json().await?;

 match cpf_response.data {
 Some(data) if cpf_response.success => Ok(data),
 _ => Err(CpfError::CpfNaoEncontrado),
 }
 }
}
```

---

## Função main

```rust
use std::env;

#[tokio::main]
async fn main() {
 let api_key = env::var("CPFHUB_API_KEY")
 .unwrap_or_else(|_| "SUA_CHAVE_DE_API".to_string());

 let client = match CpfClient::new(&api_key) {
 Ok(c) => c,
 Err(e) => {
 eprintln!("Erro ao criar cliente: {}", e);
 return;
 }
 };

 match client.consultar_cpf("12345678900").await {
 Ok(dados) => {
 println!("Nome: {}", dados.name);
 println!("CPF: {}", dados.cpf);
 println!("Nascimento: {}", dados.birth_date);
 println!("Gênero: {}", dados.gender);
 println!("Ano: {}", dados.year);
 }
 Err(e) => {
 eprintln!("Erro: {}", e);
 }
 }
}
```

---

## Processamento em lote com controle de concorrência

Para validar múltiplos CPFs de forma eficiente:

```rust
use tokio::time::sleep;
use std::time::Duration;

async fn validar_lote(client: &CpfClient, cpfs: &[&str]) -> Vec<Result<CpfData, CpfError>> {
 let mut resultados = Vec::new();

 for cpf in cpfs {
 let resultado = client.consultar_cpf(cpf).await;
 resultados.push(resultado);

 // Intervalo entre consultas para evitar excedente desnecessário
 sleep(Duration::from_millis(500)).await;
 }

 resultados
}
```

---

## Testando com cURL

```bash
curl -X GET https://api.cpfhub.io/cpf/12345678900 \
 -H "x-api-key: SUA_CHAVE_DE_API" \
 -H "Accept: application/json" \
 --max-time 10
```

---

## Vantagens de usar Rust para validação de CPF

* **Segurança de memória** -- Sem null pointer exceptions ou data races. O compilador garante a corretude do código em tempo de compilação.

* **Performance** -- Rust compila para código nativo com performance comparável a C/C++, ideal para microserviços de alta demanda.

* **Erros tipados** -- O sistema de tipos de Rust com `Result<T, E>` torna o tratamento de erros explícito e exaustivo.

* **Async nativo** -- Com tokio e reqwest, as requisições HTTP são totalmente assíncronas, maximizando o throughput.

---

## Boas práticas

* **Timeout** -- Configure timeout no `reqwest::Client::builder()` para evitar bloqueios indefinidos.

* **Erros tipados** -- Use `thiserror` para criar enums de erro que facilitam o tratamento em cada camada da aplicação.

* **Variáveis de ambiente** -- Armazene a chave de API em variáveis de ambiente, nunca no código-fonte. Consulte as [recomendações da OWASP para proteção de segredos em APIs](https://owasp.org/www-project-api-security/).

* **Plano e excedente** -- No plano gratuito da CPFHub.io, você tem 50 consultas/mês. Ao ultrapassar, a API cobra R$0,15 por consulta adicional sem bloquear. O plano Pro oferece 1.000 consultas por R$149/mês.

* **serde rename_all** -- Use `#[serde(rename_all = "camelCase")]` para mapear automaticamente campos JSON em camelCase para snake_case do Rust.

---

## Perguntas frequentes

### Por que usar Rust para integrar uma API de CPF em vez de linguagens mais populares?
Rust oferece garantias de segurança de memória em tempo de compilação, o que é valioso em sistemas que processam dados pessoais. Além disso, a performance nativa de Rust é ideal para microserviços de alta concorrência. Se a equipe já usa Rust ou o sistema é crítico em termos de performance, a escolha é natural.

### O reqwest suporta requisições síncronas ou apenas assíncronas?
O `reqwest` tem suporte a ambos os modos. Para o modo síncrono, ative a feature `blocking` no Cargo.toml: `reqwest = { version = "0.12", features = ["json", "blocking"] }`. O modo assíncrono com tokio é recomendado para aplicações de produção, pois maximiza o throughput.

### Como tratar o caso em que a franquia mensal de consultas foi ultrapassada?
A CPFHub.io não retorna erro 429 nem bloqueia ao atingir o limite — as consultas continuam funcionando e são cobradas a R$0,15 cada. Para controlar o gasto, monitore o consumo no painel em app.cpfhub.io e implemente um contador local de requisições na sua aplicação Rust.

### Como serializar a resposta da API de CPF para salvar no banco de dados?
Adicione `Serialize` ao derive das structs além de `Deserialize`. Para persistência, use uma crate como `sqlx` (PostgreSQL, MySQL, SQLite) ou `diesel`. Armazene apenas os campos necessários para a finalidade de negócio — conforme o princípio da minimização de dados da LGPD.

### Leia também

- [Como validar CPF no frontend com React e API REST](https://cpfhub.io/blog/como-validar-cpf-no-frontend-com-react-e-api-rest)
- [SLA de API de CPF: níveis de disponibilidade e o que exigir do seu provedor](https://cpfhub.io/blog/sla-api-cpf-niveis-disponibilidade)
- [API de CPF grátis para desenvolvedores: como começar em 5 minutos](https://cpfhub.io/blog/api-cpf-gratis-desenvolvedores-comecar-5-minutos)
- [Como consumir API de CPF em Go com net/http e tratamento de erros](https://cpfhub.io/blog/como-consumir-api-de-cpf-em-go-com-net-http-e-tratamento-de-erros)

---

## Conclusão

Consumir a API de consulta de CPF da [**CPFHub.io**](https://www.cpfhub.io/) em Rust é direto: `reqwest` cuida das requisições assíncronas, `serde` deserializa o JSON e `thiserror` organiza os erros de forma idiomática. O resultado é um cliente tipado, seguro e pronto para produção.

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 ao seu projeto Rust em menos de uma hora.

