Validação de CPF em Go: exemplo prático com net/http

Aprenda a validar e consultar CPF em Go usando a API SmartCPF v2 com net/http padrão, autenticação X-API-KEY e tratamento de erros robusto.

Redação Smart CPFRedação Smart CPF
·· 7 min de leitura
Validação de CPF em Go: exemplo prático com net/http

Validar CPF em aplicações Go vai além de verificar o dígito verificador: em produção, você precisa confirmar se o documento existe na base da Receita Federal, obter dados cadastrais atualizados e tratar casos de CPF suspenso, cancelado ou nulo. A API SmartCPF resolve isso com uma chamada HTTP única que retorna situação cadastral, dados pessoais, e-mails, telefones e endereços — tudo com autenticação por header X-API-KEY e modelo de créditos previsível.

Este artigo mostra como consumir o endpoint GET /v2/individuals/{cpf} usando apenas a biblioteca padrão net/http, sem dependências externas, cobrindo montagem da requisição, leitura do corpo, parsing JSON e tratamento de códigos de status HTTP comuns (401, 404, 422, 429).

Como a consulta de CPF funciona na SmartCPF

A API expõe o recurso individuals onde o CPF (apenas números ou formatado) é passado no path e o pacote de dados desejado vem via query string package. Os pacotes disponíveis — basic, person, contact, address, score e complete — controlam quais campos voltam no result, permitindo otimizar custo e latência. A resposta sempre traz o envelope meta com consultId (rastreabilidade), balance (créditos restantes), executionTimeMs e success, além do array errors vazio em sucesso.

Vantagens de usar a API SmartCPF em Go

1. Zero dependências externas
O exemplo usa só net/http, encoding/json e io — compila em binário único, ideal para containers leves e serverless.

2. Controle total do ciclo de vida da requisição
Timeouts, retries, circuit breaker e logging ficam no seu código, não escondidos em SDKs.

3. Autenticação simples e segura
Header X-API-KEY evita vazamento de segredos em logs de proxy ou URL; rotação de chave é imediata no painel.

4. Modelo de créditos previsível
Cada resposta traz meta.balance; você monitora consumo em tempo real sem faturas surpresa.

5. Dados enriquecidos em uma chamada
Pacote complete retorna nome, data de nascimento, idade, nome da mãe, sexo, situação cadastral, e-mails, telefones com operadora/tipo/WhatsApp e endereços — dispensando múltiplas integrações.

6. Resposta padronizada e versionada
meta.apiVersion garante compatibilidade; campos novos entram sem breaking changes.

Desafios e o que considerar antes de integrar

1. Rate limiting por IP e chave
Exceder o limite retorna 429 com Retry-After; implemente backoff exponencial e jitter.

2. CPF inválido ou não encontrado
400 para formato inválido; 404 para CPF inexistente na base — trate ambos como "não elegível" no seu fluxo.

3. Saldo insuficiente
402 quando balance zera; sua aplicação deve alertar o time financeiro ou bloquear consultas não essenciais.

4. Latência de rede variável
meta.executionTimeMs médio ~70 ms, mas picos ocorrem; defina timeouts realistas (ex.: 5 s total, 2 s dial).

5. Dados sensíveis na resposta
Nome, e-mail, telefone e endereço são PII; garanta TLS em trânsito, logs sem corpo da resposta e retenção mínima.

Como implementar com a SmartCPF

Abaixo, uma função ConsultarCPF completa: monta a URL, injeta o header X-API-KEY, faz a requisição com timeout, checa status code, decodifica JSON e retorna struct tipada ou erro descritivo. O main demonstra uso real com variável de ambiente para a chave.

cpf_consulta.go
package main

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

// Meta contém metadados da resposta da API
type Meta struct {
    SearchType      string  `json:"searchType"`
    ConsultID       string  `json:"consultId"`
    User            string  `json:"user"`
    Message         string  `json:"message"`
    IP              string  `json:"ip"`
    Success         bool    `json:"success"`
    APIVersion      string  `json:"apiVersion"`
    Date            string  `json:"date"`
    ExecutionTimeMs float64 `json:"executionTimeMs"`
    Balance         float64 `json:"balance"`
}

// Phone representa um telefone retornado pela API
type Phone struct {
    Number               string `json:"number"`
    BlockedTelemarketing bool   `json:"blockedTelemarketing"`
    Operator             string `json:"operator"`
    Type                 string `json:"type"`
    WhatsApp             bool   `json:"whatsapp"`
}

// Result contém os dados do CPF consultado
type Result struct {
    CPF        string   `json:"cpf"`
    Name       string   `json:"name"`
    BirthDate  string   `json:"birthDate"`
    Age        int      `json:"age"`
    MotherName string   `json:"motherName"`
    Gender     string   `json:"gender"`
    Status     string   `json:"status"`
    Emails     []string `json:"emails"`
    Phones     []Phone  `json:"phones"`
    Addresses  []string `json:"addresses"`
}

// APIResponse é o envelope completo da resposta
type APIResponse struct {
    Meta    Meta    `json:"meta"`
    Result  Result  `json:"result"`
    Success bool    `json:"success"`
    Errors  []string `json:"errors"`
}

// ConsultarCPF realiza a consulta de CPF na SmartCPF v2.
// cpf: apenas números (ex.: "11111111111").
// apiKey: sua chave obtida no painel SmartCPF.
// pkg: pacote de dados (basic, person, contact, address, score, complete).
// Retorna *APIResponse ou erro com detalhes do status HTTP.
func ConsultarCPF(ctx context.Context, cpf, apiKey, pkg string) (*APIResponse, error) {
    if apiKey == "" {
        return nil, errors.New("X-API-KEY não informada")
    }

    // Limpa formatação do CPF (remove pontos e traço)
    cleanCPF := ""
    for _, r := range cpf {
        if r >= '0' && r <= '9' {
            cleanCPF += string(r)
        }
    }
    if len(cleanCPF) != 11 {
        return nil, fmt.Errorf("CPF deve ter 11 dígitos, recebeu %d", len(cleanCPF))
    }

    baseURL := "https://api.smartcpf.com.br/v2/individuals/" + cleanCPF
    u, _ := url.Parse(baseURL)
    q := u.Query()
    q.Set("package", pkg)
    u.RawQuery = q.Encode()

    req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
    if err != nil {
        return nil, fmt.Errorf("criar request: %w", err)
    }
    // Autenticação obrigatória via header X-API-KEY
    req.Header.Set("X-API-KEY", apiKey)
    req.Header.Set("Accept", "application/json")
    req.Header.Set("User-Agent", "smartcpf-go-example/1.0")

    client := &http.Client{
        Timeout: 5 * time.Second, // timeout total (dial + TLS + response)
    }

    resp, err := client.Do(req)
    if err != nil {
        // Erro de rede, DNS, timeout, TLS
        return nil, fmt.Errorf("executar request: %w", err)
    }
    defer resp.Body.Close()

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

    // Tratamento por status code
    switch resp.StatusCode {
    case http.StatusOK: // 200
        var data APIResponse
        if err := json.Unmarshal(body, &data); err != nil {
            return nil, fmt.Errorf("decode JSON: %w", err)
        }
        if !data.Success || len(data.Errors) > 0 {
            return nil, fmt.Errorf("API retornou erro: %v", data.Errors)
        }
        return &data, nil

    case http.StatusBadRequest: // 400 - CPF mal formatado
        return nil, errors.New("CPF inválido (formato)")

    case http.StatusUnauthorized: // 401 - X-API-KEY ausente ou inválida
        return nil, errors.New("autenticação falhou: X-API-KEY inválida")

    case http.StatusPaymentRequired: // 402 - saldo zerado
        return nil, errors.New("saldo de créditos insuficiente")

    case http.StatusNotFound: // 404 - CPF não encontrado na base
        return nil, errors.New("CPF não encontrado na base da Receita Federal")

    case http.StatusUnprocessableEntity: // 422 - regra de negócio
        return nil, errors.New("entidade não processável (regras de negócio)")

    case http.StatusTooManyRequests: // 429 - rate limit
        retryAfter := resp.Header.Get("Retry-After")
        return nil, fmt.Errorf("rate limit excedido, tente novamente em %s segundos", retryAfter)

    default:
        return nil, fmt.Errorf("status inesperado %d: %s", resp.StatusCode, string(body))
    }
}

func main() {
    // Em produção, use variável de ambiente ou secret manager
    apiKey := os.Getenv("SMARTCP_API_KEY")
    if apiKey == "" {
        fmt.Println("ERRO: defina a variável de ambiente SMARTCP_API_KEY")
        os.Exit(1)
    }

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

    cpf := "11111111111" // apenas números
    pkg := "complete"    // pacote mais completo

    resp, err := ConsultarCPF(ctx, cpf, apiKey, pkg)
    if err != nil {
        fmt.Printf("Erro na consulta: %v\n", err)
        os.Exit(1)
    }

    // Exemplo de uso dos campos retornados
    fmt.Printf("Consulta ID: %s\n", resp.Meta.ConsultID)
    fmt.Printf("Saldo restante: %.2f créditos\n", resp.Meta.Balance)
    fmt.Printf("Tempo de execução: %.1f ms\n", resp.Meta.ExecutionTimeMs)
    fmt.Printf("CPF: %s\n", resp.Result.CPF)
    fmt.Printf("Nome: %s\n", resp.Result.Name)
    fmt.Printf("Situação: %s\n", resp.Result.Status)
    fmt.Printf("Idade: %d anos\n", resp.Result.Age)
    fmt.Printf("E-mails: %v\n", resp.Result.Emails)
    for _, ph := range resp.Result.Phones {
        fmt.Printf("Telefone: %s (%s, %s, WhatsApp: %v)\n",
            ph.Number, ph.Operator, ph.Type, ph.WhatsApp)
    }
}

Resposta da API

response.json
{
  "meta": {
    "searchType": "CPF G",
    "consultId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "user": "Fulano de Tal",
    "message": "CPF encontrado com sucesso",
    "ip": "127.0.0.1",
    "success": true,
    "apiVersion": "v2",
    "date": "2026-08-21T14:30:45.123Z",
    "executionTimeMs": 74.1,
    "balance": 9.77
  },
  "result": {
    "cpf": "111.111.111-11",
    "name": "João da Silva",
    "birthDate": "10/09/1992",
    "age": 33,
    "motherName": "Maria da Silva",
    "gender": "M",
    "status": "Regular",
    "emails": ["[email protected]"],
    "phones": [
      {
        "number": "(55) 99999-9999",
        "blockedTelemarketing": false,
        "operator": "VIVO",
        "type": "CELULAR",
        "whatsapp": false
      }
    ],
    "addresses": []
  },
  "success": true,
  "errors": []
}

O fluxo acima resolve na prática:

  • Rastreabilidade total: meta.consultId permite auditar cada consulta no painel ou suporte.
  • Controle de custo em tempo real: meta.balance (ex.: 9.77) mostra créditos restantes após a chamada.
  • Observabilidade de latência: meta.executionTimeMs (74.1 ms) alimenta dashboards de SLA.
  • Decisão de negócio imediata: result.status ("Regular", "Suspenso", "Cancelado", "Nulo") direciona onboarding, crédito ou compliance.
  • Dados de contato prontos para uso: result.emails, result.phones (com operadora, tipo e flag WhatsApp) e result.addresses eliminam necessidade de APIs separadas.

Por que usar a SmartCPF

  • Integração em minutos: apenas net/http + header X-API-KEY; sem SDK, sem OAuth, sem fluxo de token.
  • Latência consistente: média sub-100 ms com SLA de 99,9% de disponibilidade.
  • Modelo pay-as-you-go: créditos não expiram; você recarrega quando precisar, sem assinatura mensal.
  • Dados oficiais e atualizados: fonte direta da Receita Federal e bureaus parceiros, com cobertura nacional.
  • Escalabilidade nativa: a mesma chave atende de dezenas a milhões de consultas/dia sem mudança de código.

Conclusão

Validar CPF em Go com a SmartCPF reduz a complexidade a uma única chamada HTTP autenticada por X-API-KEY, retornando dados cadastrais completos, metadados de auditoria e controle de saldo embutidos. O exemplo com net/http demonstra que não há necessidade de dependências pesadas — o padrão da linguagem já cobre timeouts, contextos, streaming de body e parsing JSON de forma idiomática. Crie sua conta na SmartCPF e faça sua primeira consulta em minutos.

Redação Smart CPF

Sobre a redação

Redação Smart CPF

Autor do blog

Pronto para validar CPFs em segundos?

Consultas gratuitas para testar agora. Acesso imediato à documentação.