Como consumir API de CPF em Go com net/http e tratamento de erros

Aprenda a consumir a API de consulta de CPF em Go usando net/http com tratamento de erros robusto, timeout e boas práticas idiomáticas.

Redação CPFHub.io
Redação CPFHub.io
··7 min de leitura
Como consumir API de CPF em Go com net/http e tratamento de erros

Para consumir a API de CPF da CPFHub.io em Go, use a biblioteca padrão net/http com context.WithTimeout, deserialize o JSON de resposta em structs tipadas e trate erros com variáveis sentinela. Nenhuma dependência externa é necessária — o código completo abaixo cobre autenticação via header x-api-key, timeout configurável e processamento em lote com controle de rate limit.

Introdução

A linguagem Go (Golang) é reconhecida pela sua simplicidade, performance e excelente suporte nativo para operações de rede. A biblioteca padrão net/http é suficiente para consumir APIs REST sem necessidade de dependências externas, o que a torna uma escolha popular para microserviços, CLIs e ferramentas de automação.

Para aplicações que operam no mercado brasileiro, a validação de CPF via API é um requisito frequente. A CPFHub.io fornece um endpoint simples e bem documentado que se integra naturalmente ao estilo idiomático de Go.


Pré-requisitos

  • Go 1.21+ -- Instalado e configurado.
  • Conta na CPFHub.io -- Para obter a chave de API. O plano gratuito oferece 50 consultas/mês.

Inicializando o módulo

mkdir cpfhub-go && cd cpfhub-go
go mod init cpfhub-go

Modelando a resposta da API

Defina as structs que representam o JSON retornado:

package main

type CpfResponse struct {
    Success bool `json:"success"`
    Data CpfData `json:"data"`
}

type CpfData struct {
    Cpf string `json:"cpf"`
    Name string `json:"name"`
    NameUpper string `json:"nameUpper"`
    Gender string `json:"gender"`
    BirthDate string `json:"birthDate"`
    Day int `json:"day"`
    Month int `json:"month"`
    Year int `json:"year"`
}

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
    }
}

Implementação com net/http

Abaixo está a implementação completa com tratamento de erros, timeout e validação:

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "os"
    "regexp"
    "time"
)

var (
    ErrCpfInvalido      = fmt.Errorf("CPF inválido: informe 11 dígitos numéricos")
    ErrCpfNaoEncontrado = fmt.Errorf("CPF não encontrado na base de dados")
    ErrTimeout          = fmt.Errorf("a requisição excedeu o tempo limite")
)

type CpfClient struct {
    httpClient *http.Client
    apiKey     string
    baseURL    string
}

func NewCpfClient(apiKey string) *CpfClient {
    return &CpfClient{
        httpClient: &http.Client{
            Timeout: 10 * time.Second,
        },
        apiKey:  apiKey,
        baseURL: "https://api.cpfhub.io",
    }
}

func (c *CpfClient) ConsultarCpf(ctx context.Context, cpf string) (*CpfData, error) {
    re := regexp.MustCompile(`\D`)
    cpfLimpo := re.ReplaceAllString(cpf, "")

    if len(cpfLimpo) != 11 {
        return nil, ErrCpfInvalido
    }

    url := fmt.Sprintf("%s/cpf/%s", c.baseURL, cpfLimpo)

    req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
    if err != nil {
        return nil, fmt.Errorf("erro ao criar requisição: %w", err)
    }

    req.Header.Set("x-api-key", c.apiKey)
    req.Header.Set("Accept", "application/json")

    resp, err := c.httpClient.Do(req)
    if err != nil {
        if ctx.Err() == context.DeadlineExceeded {
            return nil, ErrTimeout
        }
        return nil, fmt.Errorf("erro na requisição HTTP: %w", err)
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return nil, fmt.Errorf("erro ao ler resposta: %w", err)
    }

    switch resp.StatusCode {
    case http.StatusOK:
        // Continua processamento abaixo
    case http.StatusBadRequest:
        return nil, ErrCpfInvalido
    case http.StatusUnauthorized:
        return nil, fmt.Errorf("chave de API inválida ou ausente")
    default:
        return nil, fmt.Errorf("erro HTTP %d: %s", resp.StatusCode, string(body))
    }

    var cpfResp CpfResponse
    if err := json.Unmarshal(body, &cpfResp); err != nil {
        return nil, fmt.Errorf("erro ao decodificar JSON: %w", err)
    }

    if !cpfResp.Success {
        return nil, ErrCpfNaoEncontrado
    }

    return &cpfResp.Data, nil
}

func main() {
    apiKey := os.Getenv("CPFHUB_API_KEY")
    if apiKey == "" {
        apiKey = "SUA_CHAVE_DE_API"
    }

    client := NewCpfClient(apiKey)

    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    dados, err := client.ConsultarCpf(ctx, "12345678900")
    if err != nil {
        fmt.Fprintf(os.Stderr, "Erro: %v\n", err)
        os.Exit(1)
    }

    fmt.Printf("Nome: %s\n", dados.Name)
    fmt.Printf("CPF: %s\n", dados.Cpf)
    fmt.Printf("Nascimento: %s\n", dados.BirthDate)
    fmt.Printf("Gênero: %s\n", dados.Gender)
}

Tratamento de erros idiomático em Go

Em Go, o tratamento de erros é explícito e faz parte do fluxo normal do programa. A abordagem utilizada neste exemplo segue as melhores práticas descritas no Effective Go:

  • Erros sentinela -- ErrCpfInvalido e ErrTimeout são variáveis de erro pré-definidas que facilitam a comparação com errors.Is().

  • Wrapping de erros -- fmt.Errorf("... %w", err) preserva o erro original, permitindo unwrapping em camadas superiores.

  • Context com timeout -- O context.WithTimeout garante que a requisição não ultrapasse o tempo limite, independentemente do timeout do HTTP client.

Verificando tipos de erro no chamador

import "errors"

dados, err := client.ConsultarCpf(ctx, cpf)
if err != nil {
    if errors.Is(err, ErrCpfInvalido) {
        // Tratar CPF inválido — retornar erro de validação ao usuário
    } else if errors.Is(err, ErrTimeout) {
        // Notificar timeout — considerar retry com backoff
    } else {
        // Erro genérico — logar e retornar erro interno
    }
}

Processamento em lote com goroutines

Para validar múltiplos CPFs com controle de concorrência e respeito ao rate limit:

func validarLote(client *CpfClient, cpfs []string) []CpfData {
    var resultados []CpfData

    for _, cpf := range cpfs {
        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)

        dados, err := client.ConsultarCpf(ctx, cpf)
        cancel()

        if err != nil {
            fmt.Fprintf(os.Stderr, "CPF %s: %v\n", cpf, err)
            continue
        }

        resultados = append(resultados, *dados)

        // Respeitar rate limit (plano gratuito: 1 req/2s)
        time.Sleep(2 * time.Second)
    }

    return 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

Boas práticas

  • Context -- Sempre passe context.Context para funções que fazem I/O, permitindo cancelamento e timeout.

  • Timeout duplo -- Configure timeout tanto no http.Client quanto no context.WithTimeout para redundância.

  • Variáveis de ambiente -- Armazene a chave de API em variáveis de ambiente (CPFHUB_API_KEY), não no código.

  • Rate limit -- No plano gratuito, 1 requisição a cada 2 segundos e 50 consultas/mês. O plano Pro oferece 1 req/s e 1.000 consultas por R$149/mês; ao ultrapassar o limite, o serviço continua ativo e cobra R$0,15 por consulta extra.

  • Sem dependências externas -- A biblioteca padrão net/http é suficiente para a maioria dos casos de uso.


Perguntas frequentes

Por que usar net/http em vez de um cliente HTTP de terceiros em Go?

A biblioteca padrão net/http do Go é madura, bem documentada e não adiciona dependências externas ao módulo. Para consumir um endpoint simples como o da CPFHub.io — uma única chamada GET com header de autenticação — ela oferece controle total sobre timeout, context e tratamento de erros sem overhead desnecessário.

Como lidar com o limite de consultas do plano gratuito em Go?

No plano gratuito, a CPFHub.io permite 1 req a cada 2 segundos e 50 consultas por mês. No processamento em lote, adicione time.Sleep(2 * time.Second) entre chamadas. Ao ultrapassar as 50 consultas mensais, a API não bloqueia — no plano Pro, o excedente é cobrado automaticamente a R$0,15 por consulta.

Como o context.WithTimeout interage com o Timeout do http.Client?

Os dois timeouts são independentes e complementares. O http.Client.Timeout cobre toda a vida da requisição (DNS, conexão, leitura). O context.WithTimeout permite cancelar a operação a partir de qualquer camada acima — útil quando a função de consulta faz parte de um pipeline maior com prazo global definido.

Como adaptar este código para uso em microserviços Go com Gin?

Encapsule o CpfClient como dependência injetada no handler do Gin, reutilizando a mesma instância entre requisições (o http.Client é thread-safe). Passe o c.Request.Context() do Gin diretamente para ConsultarCpf() para que o cancelamento da requisição HTTP propague corretamente pelo pipeline.


Conclusão

Consumir a API de consulta de CPF da CPFHub.io em Go é direto e idiomático: structs tipadas para deserializar o JSON, erros sentinela para tratamento granular e context.WithTimeout para controle de prazo. A biblioteca padrão net/http é tudo que você precisa para uma integração robusta em 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 serviço Go em menos de 30 minutos com o código deste guia.

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