O comando curl que usuários Linux utilizam diariamente é muito mais do que uma ferramenta de download — é um cliente HTTP completo capaz de testar APIs REST, automatizar fluxos de trabalho, depurar problemas de rede e transferir dados através de dezenas de protocolos. Seja para solucionar um webhook quebrado, criar scripts de integração com APIs ou inspecionar certificados TLS, dominar o curl é uma das habilidades de maior impacto que um sysadmin ou desenvolvedor pode ter. Este guia percorre todos os casos de uso práticos, desde GETs simples até testes completos de APIs REST com CRUD, com exemplos reais ao longo do texto.

Pré-requisitos

  • Um sistema Linux com curl instalado (curl --version para confirmar; instale via sudo apt install curl ou sudo dnf install curl)
  • Familiaridade básica com o terminal e conceitos HTTP (métodos de requisição, cabeçalhos, códigos de status)
  • Um endpoint de API para testar — os exemplos usam https://jsonplaceholder.typicode.com, uma API mock pública e gratuita
  • Opcional: jq instalado para formatar saídas JSON (sudo apt install jq)

Fazendo Requisições HTTP: GET, POST, PUT, DELETE

O curl usa GET por padrão, então o comando mais simples possível é apenas uma URL:

curl https://jsonplaceholder.typicode.com/posts/1

Adicione -s (silencioso) para suprimir o medidor de progresso em scripts, e redirecione para jq para uma saída legível:

curl -s https://jsonplaceholder.typicode.com/posts/1 | jq .

POST — criando um recurso:

curl -s -X POST \
  -H "Content-Type: application/json" \
  -d '{"title":"My Post","body":"Hello world","userId":1}' \
  https://jsonplaceholder.typicode.com/posts | jq .

O flag -X POST define o método. -H adiciona um cabeçalho. -d envia o corpo da requisição. Para envios de formulários em vez de JSON, omita o cabeçalho Content-Type ou use -d "campo=valor&outro=dado".

PUT — atualizando um recurso:

curl -s -X PUT \
  -H "Content-Type: application/json" \
  -d '{"title":"Updated Title","body":"New body","userId":1}' \
  https://jsonplaceholder.typicode.com/posts/1 | jq .

PATCH — atualização parcial:

curl -s -X PATCH \
  -H "Content-Type: application/json" \
  -d '{"title":"Just the title changed"}' \
  https://jsonplaceholder.typicode.com/posts/1 | jq .

DELETE — removendo um recurso:

curl -s -X DELETE https://jsonplaceholder.typicode.com/posts/1
echo "HTTP status: $?"

Para capturar o código de status HTTP explicitamente:

curl -s -o /dev/null -w "%{http_code}" -X DELETE \
  https://jsonplaceholder.typicode.com/posts/1

O flag -w (write-out) com %{http_code} exibe apenas o código de status numérico — ideal para verificar sucesso ou falha em scripts shell.

Autenticação: Cabeçalhos, Bearer Tokens e Basic Auth

A maioria das APIs em produção exige autenticação. O curl lida com todos os esquemas comuns.

Bearer token (OAuth 2.0 / JWT):

curl -s -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  https://api.example.com/protected-resource | jq .

HTTP Basic Auth — duas formas equivalentes:

# Forma 1: flag -u (o curl adiciona o cabeçalho Authorization automaticamente)
curl -s -u myuser:mysecretpass https://api.example.com/data

# Forma 2: cabeçalho explícito (útil ao criar scripts com variáveis)
curl -s -H "Authorization: Basic $(echo -n myuser:mysecretpass | base64)" \
  https://api.example.com/data

API key como parâmetro de query:

curl -s "https://api.example.com/data?api_key=SUA_CHAVE_AQUI" | jq .

API key como cabeçalho personalizado (comum em serviços como OpenAI ou Stripe):

curl -s -H "X-API-Key: SUA_CHAVE_AQUI" https://api.example.com/endpoint | jq .

Armazenar credenciais em um arquivo netrc mantém segredos fora do histórico do shell:

# ~/.netrc
machine api.example.com
login myuser
password mysecretpass
curl -s --netrc https://api.example.com/data

Download e Upload de Arquivos

Download básico — salvar com o nome original:

curl -O https://releases.ubuntu.com/24.04/ubuntu-24.04-desktop-amd64.iso

Download para um caminho específico:

curl -o /tmp/ubuntu.iso https://releases.ubuntu.com/24.04/ubuntu-24.04-desktop-amd64.iso

Seguir redirecionamentos (muitas URLs de download redirecionam):

curl -L -O https://github.com/cli/cli/releases/latest/download/gh_linux_amd64.tar.gz

Retomar um download interrompido:

curl -C - -O https://example.com/large-file.tar.gz

Download com barra de progresso (útil em terminais interativos):

curl --progress-bar -O https://example.com/large-file.tar.gz

Upload de arquivo com multipart/form-data (simulando um input de arquivo do navegador):

curl -s -X POST \
  -F "file=@/path/to/document.pdf" \
  -F "description=My document" \
  https://api.example.com/upload | jq .

Upload binário bruto com PUT:

curl -s -X PUT \
  -H "Content-Type: application/octet-stream" \
  --data-binary @/path/to/image.png \
  https://api.example.com/files/image.png

Depuração Verbose com -v e —trace

O flag -v é sua melhor ferramenta de depuração. Ele exibe o handshake TLS, os cabeçalhos da requisição, os cabeçalhos da resposta e a linha de status:

curl -v https://jsonplaceholder.typicode.com/posts/1

Saída de exemplo (truncada):

* Connected to jsonplaceholder.typicode.com (104.21.x.x) port 443
* TLSv1.3, TLS handshake
> GET /posts/1 HTTP/2
> Host: jsonplaceholder.typicode.com
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/2 200
< content-type: application/json; charset=utf-8
< cache-control: max-age=43200
<
{ ... JSON body ... }

Linhas que começam com > são enviadas pelo curl; < são recebidas do servidor. Isso é essencial para verificar se seus cabeçalhos estão sendo enviados corretamente.

Para ainda mais detalhes, use --trace para despejar bytes brutos:

curl --trace /tmp/curl-trace.txt https://api.example.com/endpoint

Verificar apenas cabeçalhos sem baixar o corpo:

curl -I https://example.com

-I envia uma requisição HEAD. Para enviar HEAD especificando um método personalizado:

curl -s -X HEAD -I https://example.com

Inspecionar informações do certificado TLS:

curl -v --connect-to :: https://example.com 2>&1 | grep -A5 "Server certificate"

Ou com --cert-status em builds compatíveis.

curl vs wget: Escolhendo a Ferramenta Certa

Tanto curl quanto wget baixam arquivos, mas servem a propósitos principais diferentes:

Recursocurlwget
Caso de uso principalChamadas de API, depuração HTTP, scriptingDownloads recursivos, espelhamento de sites
Suporte a APIs RESTCompleto (GET/POST/PUT/DELETE/PATCH)Apenas GET por padrão
Corpo de requisição JSON-d '{"key":"val"}'Não suportado nativamente
Cabeçalhos personalizados-H "Header: Value"--header="Header: Value"
Seguir redirecionamentosFlag -L necessárioSegue por padrão
Retomar downloads-C -Flag -c
Download recursivoNão suportadoFlag --recursive
Saída para stdoutPadrãoRequer -O -
Scripting / pipingExcelente (stdout por padrão)Menos natural
Protocolos suportados30+ (FTP, SFTP, SMTP, IMAP…)HTTP, HTTPS, FTP
Exibição de progresso-# ou --progress-barExibe por padrão

Regra geral: Use curl para trabalho com APIs, depuração e pipelines de scripting. Use wget quando precisar espelhar ou baixar recursivamente um site, ou ao retomar downloads de forma mais simples.

Cenário Real: Testando uma API REST de Ponta a Ponta

Você tem um servidor em produção executando um novo microsserviço de gerenciamento de usuários. Antes de implantar, você quer validar o ciclo de vida CRUD completo a partir da linha de comando, capturando códigos de status para integração com CI/CD.

#!/bin/bash
set -euo pipefail
BASE="https://api.example.com/v1"
TOKEN="eyJhbGciOiJIUzI1NiIs..."

echo "=== Testando API de Gerenciamento de Usuários ==="

# 1. Verificação de saúde
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$BASE/health")
echo "Health check: $STATUS"
[[ "$STATUS" == "200" ]] || { echo "FALHA: API não está saudável"; exit 1; }

# 2. Criar um novo usuário
RESPONSE=$(curl -s -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"username":"testuser","email":"test@example.com","role":"viewer"}' \
  "$BASE/users")
USER_ID=$(echo "$RESPONSE" | jq -r '.id')
echo "ID do usuário criado: $USER_ID"

# 3. Recuperar o usuário
curl -s -H "Authorization: Bearer $TOKEN" \
  "$BASE/users/$USER_ID" | jq '.username'

# 4. Atualizar o papel do usuário
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
  -X PATCH \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"role":"editor"}' \
  "$BASE/users/$USER_ID")
echo "Status PATCH: $STATUS"
[[ "$STATUS" == "200" ]] || echo "AVISO: esperado 200, recebido $STATUS"

# 5. Listar todos os usuários e contar
COUNT=$(curl -s -H "Authorization: Bearer $TOKEN" \
  "$BASE/users" | jq 'length')
echo "Total de usuários: $COUNT"

# 6. Excluir usuário de teste
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
  -X DELETE \
  -H "Authorization: Bearer $TOKEN" \
  "$BASE/users/$USER_ID")
echo "Status DELETE: $STATUS"
[[ "$STATUS" == "204" ]] || echo "AVISO: esperado 204, recebido $STATUS"

echo "=== Todos os testes passaram ==="

Este script valida o contrato completo da API e sai com um código diferente de zero se algo falhar — pronto para ser inserido em um pipeline CI/CD.

Armadilhas e Casos Especiais

Erros de certificado SSL em servidores internos/de desenvolvimento: Use -k ou --insecure para ignorar a verificação, mas apenas em desenvolvimento:

curl -k https://internal-dev-server.local/api

Aspas simples vs duplas em shells: Dentro de aspas duplas, $ e crases são expandidos. Use aspas simples em torno de corpos JSON para evitar surpresas:

# Seguro: aspas simples evitam expansão de variáveis dentro do JSON
curl -d '{"userId":1}' https://api.example.com/items

# Arriscado: $HOME é expandido dentro de aspas duplas
curl -d "{\"userId\":$USER_ID}" https://api.example.com/items  # OK se $USER_ID for intencional

Enviar um @ literal em dados POST: o curl trata -d @arquivo como “ler o corpo do arquivo”. Para enviar um @ literal, use --data-raw:

curl --data-raw '{"email":"user@example.com"}' https://api.example.com/subscribe

Limitação de taxa e retentativas: Adicione --retry e --retry-delay para scripts resilientes:

curl --retry 5 --retry-delay 2 --retry-all-errors \
  -s https://api.example.com/endpoint | jq .

Controle de timeout: Evite que scripts fiquem bloqueados indefinidamente:

curl --connect-timeout 10 --max-time 30 https://api.example.com/slow-endpoint

--connect-timeout limita a fase de conexão; --max-time limita o tempo total de transferência.

Cookies: Envie e persista cookies para APIs baseadas em sessão:

# Salvar cookies em um arquivo após o login
curl -s -c /tmp/cookies.txt -X POST \
  -d "username=admin&password=secret" \
  https://example.com/login

# Reutilizar cookies salvos para requisições autenticadas
curl -s -b /tmp/cookies.txt https://example.com/dashboard

Solução de Problemas

“Could not resolve host”: Falha de DNS. Verifique /etc/resolv.conf ou teste com curl --dns-servers 8.8.8.8 https://example.com.

“SSL: no alternative certificate subject name matches”: O certificado do servidor não corresponde ao hostname. Use -v para ver o que o certificado diz; o hostname na sua URL pode estar incorreto.

“Empty reply from server”: O servidor fechou a conexão sem enviar cabeçalhos. Tente -v para ver se o handshake TLS foi bem-sucedido; pode indicar uma incompatibilidade de protocolo (o flag --http1.1 pode ajudar).

O corpo da resposta está vazio, mas o status é 200: Alguns endpoints retornam 204 No Content em caso de sucesso (especialmente DELETE). Use -v para confirmar a linha de status.

Resposta grande truncada no terminal: Redirecione para less ou para um arquivo:

curl -s https://api.example.com/large-dataset | jq . | less
curl -s https://api.example.com/large-dataset > /tmp/response.json

Verificar qual versão do curl você tem e quais protocolos ele suporta:

curl --version

Procure por Protocols: na saída — isso mostra a disponibilidade de FTP, SFTP, HTTP/2 e HTTP/3 dependendo do seu build.

Resumo

  • curl é o cliente HTTP padrão do Linux para testes de APIs REST, transferência de arquivos e depuração de rede
  • Use -X para definir o método HTTP (POST, PUT, PATCH, DELETE), -H para cabeçalhos, -d para o corpo da requisição
  • Autenticação: -H "Authorization: Bearer TOKEN" para autenticação por token, -u usuario:senha para Basic Auth
  • Baixe arquivos com -O (nome original) ou -o arquivo; use -L para seguir redirecionamentos
  • -v modo verbose exibe cabeçalhos completos de requisição/resposta e detalhes do TLS — essencial para depuração
  • -w "%{http_code}" extrai o código de status para scripting e validação em CI/CD
  • Use --retry, --connect-timeout e --max-time para tornar scripts resilientes para produção
  • Prefira curl em vez de wget para trabalho com APIs e scripting; use wget para downloads recursivos de sites
  • Sempre use --data-raw quando os dados do corpo contêm caracteres @ literais para evitar confusão com leitura de arquivos
  • Redirecione para jq para saída JSON formatada: curl -s https://api.example.com/data | jq .

Artigos Relacionados