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

**Publicado:** 05/02/2025
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/como-consumir-api-de-cpf-em-go-com-net-http-e-tratamento-de-erros

---


Para consumir a API de CPF da [**CPFHub.io**](https://www.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**](https://www.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

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

---

## Modelando a resposta da API

Defina as structs que representam o JSON retornado:

```go
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:

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

---

## Implementação com net/http

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

```go
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](https://go.dev/doc/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

```go
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:

```go
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

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

---

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

### Leia também

- [Como integrar validação de CPF em microserviços Go com Gin framework](https://cpfhub.io/blog/como-integrar-validacao-de-cpf-em-microservicos-go-com-gin-framework)
- [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)

---

## Conclusão

Consumir a API de consulta de CPF da [**CPFHub.io**](https://www.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](https://www.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.

