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.

Redação CPFHub.io
Redação CPFHub.io
··7 min de leitura
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

cargo new cpfhub-rust
cd cpfhub-rust

Dependências no Cargo.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:

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:

{
    "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:

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

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

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:

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

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.

  • 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.


Conclusão

Consumir a API de consulta de CPF da 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 — 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.

CPFHub.io

Pronto para integrar a API?

50 consultas gratuitas para testar agora. Sem cartão de crédito. Acesso imediato à documentação.

Redação CPFHub.io

Sobre a redação

Redação CPFHub.io

Time editorial especializado em APIs de CPF, identidade digital e compliance no mercado brasileiro. Produzimos guias técnicos, análises regulatórias e tutoriais sobre LGPD e KYC para desenvolvedores e líderes de produto.

WhatsAppFale conosco via WhatsApp