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
curlinstalado (curl --versionpara confirmar; instale viasudo apt install curlousudo 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:
jqinstalado 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:
| Recurso | curl | wget |
|---|---|---|
| Caso de uso principal | Chamadas de API, depuração HTTP, scripting | Downloads recursivos, espelhamento de sites |
| Suporte a APIs REST | Completo (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 redirecionamentos | Flag -L necessário | Segue por padrão |
| Retomar downloads | -C - | Flag -c |
| Download recursivo | Não suportado | Flag --recursive |
| Saída para stdout | Padrão | Requer -O - |
| Scripting / piping | Excelente (stdout por padrão) | Menos natural |
| Protocolos suportados | 30+ (FTP, SFTP, SMTP, IMAP…) | HTTP, HTTPS, FTP |
| Exibição de progresso | -# ou --progress-bar | Exibe 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
-Xpara definir o método HTTP (POST, PUT, PATCH, DELETE),-Hpara cabeçalhos,-dpara o corpo da requisição - Autenticação:
-H "Authorization: Bearer TOKEN"para autenticação por token,-u usuario:senhapara Basic Auth - Baixe arquivos com
-O(nome original) ou-o arquivo; use-Lpara seguir redirecionamentos -vmodo 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-timeoute--max-timepara 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-rawquando os dados do corpo contêm caracteres@literais para evitar confusão com leitura de arquivos - Redirecione para
jqpara saída JSON formatada:curl -s https://api.example.com/data | jq .