Lendo uma resposta
Códigos de status, cabeçalhos de resposta e o corpo.
Toda requisição recebe exatamente uma resposta (a não ser que a conexão caia, o que é outro tipo de resposta e aparece em uma lição mais adiante). Uma resposta tem três partes: uma linha de status, cabeçalhos e um corpo. Quem está começando a testar costuma olhar só o corpo, porque é lá que estão os dados. A maioria dos bugs que passam despercebidos está nas outras duas partes.
Como é uma resposta
Como a requisição, uma resposta HTTP/1.1 é texto puro. Esta é a resposta para GET /api/health, do jeito que o curl mostra com a opção -i (que imprime os cabeçalhos junto com o corpo). Alguns cabeçalhos de infraestrutura foram removidos.
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
x-powered-by: TestingAPIs
{"status":"healthy","uptimeSeconds":3542,"timestamp":"2026-09-30T18:28:12Z"}A primeira linha é a linha de status: a versão do protocolo, um código de status de três dígitos e uma frase curta de motivo. Depois vêm os cabeçalhos, um por linha, depois uma linha vazia, e depois o corpo. A frase de motivo é só para pessoas. Clientes e testes devem olhar o número. O HTTP/2 elimina a frase de motivo por completo, então um teste que confere o texto "OK" pode quebrar quando o servidor for atualizado.
Classes de códigos de status
O primeiro dígito do código de status diz a classe da resposta. Muitas vezes dá para julgar um resultado só por esse dígito.
| Classe | Significado | De quem é a culpa |
|---|---|---|
| 1xx | Informativa. A requisição ainda está em andamento. | De ninguém. Você raramente as vê diretamente. |
| 2xx | Sucesso. O servidor fez o que você pediu. | De ninguém. |
| 3xx | Redirecionamento. Procure em outro lugar. | De ninguém. O cliente precisa dar o próximo passo. |
| 4xx | Erro do cliente. A requisição estava errada. | Do cliente: dados ruins, sem permissão, URL errada. |
| 5xx | Erro do servidor. A requisição pode ter estado certa. | Do servidor ou de algo por trás dele. |
Essa última coluna é o motivo de os códigos de status importarem tanto nos testes. Um 4xx diz "corrija a sua requisição". Um 5xx diz "algo quebrou do nosso lado". Se você manda lixo e recebe um 500, isso é um bug, mesmo a requisição sendo ruim, porque o servidor quebrou onde deveria ter recusado a entrada com educação, com um 400. Encontrar esses 500 é uma das coisas mais produtivas que alguém começando a testar pode fazer.
Os códigos que você mais vai ver
| Código | Nome | Uso típico |
|---|---|---|
| 200 | OK | A requisição funcionou e o corpo traz o resultado. |
| 201 | Created | Um POST criou um registro novo. |
| 204 | No Content | Funcionou e, de propósito, não há corpo. Comum depois de um DELETE. |
| 301 / 302 | Moved Permanently / Found | O recurso está na URL do cabeçalho Location. |
| 304 | Not Modified | A sua cópia em cache continua atual. Visto junto com cache. |
| 400 | Bad Request | A requisição está malformada ou algum valor é inválido. |
| 401 | Unauthorized | Sem credenciais, ou com credenciais erradas. Apesar do nome, significa não autenticado. |
| 403 | Forbidden | As credenciais estão certas, mas este usuário não pode fazer isto. |
| 404 | Not Found | Não há nada nesta URL, ou nenhum registro com este ID. |
| 405 | Method Not Allowed | A URL existe, mas não com este método. |
| 409 | Conflict | A requisição conflita com o estado atual, como um e-mail duplicado. |
| 422 | Unprocessable Content | A sintaxe está certa, mas os dados quebram uma regra. |
| 429 | Too Many Requests | Você atingiu um limite de taxa. Vá mais devagar. |
| 500 | Internal Server Error | Um erro não tratado no servidor. |
| 502 | Bad Gateway | Um proxy recebeu uma resposta ruim do servidor por trás dele. |
| 503 | Service Unavailable | O servidor está sobrecarregado ou em manutenção. |
As APIs discordam sobre alguns desses. Uma equipe devolve 400 para todo erro de validação e outra usa 422. Algumas devolvem 200 para uma exclusão, com uma mensagem no corpo; outras, 204 sem nada. Nenhuma está errada. O que você confere é se a API bate com a própria documentação e é consistente de um endpoint para o outro.
Experimentando códigos de status
/api/status/{code} responde com o código que você colocar no caminho. É útil para ver como uma ferramenta ou uma biblioteca cliente se comporta ao receber um status específico. Experimente alguns:
/api/status/201/api/status/503No curl, o 503 fica assim:
HTTP/1.1 503 Service Unavailable
Content-Type: application/json; charset=utf-8
retry-after: 5
{
"status": 503,
"statusText": "Service Unavailable",
"category": "server-error",
"message": "This is a 503 response. Use /api/status/{code} to request any status."
}Repare no cabeçalho retry-after: 5. Um 503 ou 429 muitas vezes diz ao cliente quantos segundos esperar antes de tentar de novo, e um cliente bem-comportado respeita isso. Você confere se o cabeçalho está lá, não só o status.
Códigos fora da faixa válida são recusados. /api/status/999 volta como 400 com a mensagem Status code must be between 200 and 599 (1xx cannot be sent as a final response). E /api/status/abc devolve um 404 com corpo vazio, porque abc nem combina com a rota. Duas entradas ruins, dois tipos diferentes de falha. Você vai ver esse padrão com frequência: um valor do tipo errado tende a falhar na camada de roteamento, antes mesmo de o código da aplicação rodar.
Redirecionamentos
Uma resposta 3xx geralmente não tem um corpo útil. A parte importante é o cabeçalho Location, que diz para onde ir em seguida:
HTTP/1.1 302 Found
Content-Type: application/json; charset=utf-8
location: /api/echoNavegadores e a maioria das bibliotecas HTTP seguem redirecionamentos automaticamente e mostram só a resposta final. Isso é prático até você precisar testar o próprio redirecionamento. O curl não os segue a menos que você adicione -L, o que faz dele a melhor ferramenta para isso.
Cabeçalhos de resposta
Os cabeçalhos de resposta descrevem o corpo e dão instruções ao cliente. Peça a página 3 da lista de produtos, dois produtos por página:
/api/Products?limit=2&page=3HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
x-page: 3
x-per-page: 2
x-total-count: 20
x-total-pages: 10Content-Type diz que o corpo é JSON em UTF-8. Os quatro cabeçalhos x-page e x-total trazem os detalhes da paginação: há 20 produtos, 10 páginas, e esta é a página 3. O corpo é só um array de dois produtos, então um cliente que ignora esses cabeçalhos não tem como saber que há mais. Cabeçalhos que começam com X- são personalizados, inventados pela API. Quem os documenta é a API, não o padrão HTTP.
Alguns cabeçalhos guardam horários como timestamps Unix: o número de segundos desde 1º de janeiro de 1970, em UTC. APIs com limite de taxa muitas vezes mandam um em x-ratelimit-reset para dizer quando a sua cota é renovada. Converta antes de confiar: um horário de renovação no passado, ou daqui a anos, é um bug que você só pega lendo os cabeçalhos em vez de pulá-los. (O conversor de timestamp faz a conversão.)
Talvez você espere um cabeçalho Content-Length. Esta API quase sempre manda Transfer-Encoding: chunked no lugar, o que significa que o corpo é enviado em pedaços e o tamanho total não é anunciado de antemão. As duas formas são válidas. Uma resposta tem uma ou outra, não as duas.
O corpo
O corpo são os dados: um produto, uma lista, uma mensagem de erro, uma imagem. Content-Type diz como lê-lo. A maioria das suas verificações será no corpo, e Lendo JSON trata disso em detalhes. Antes, dois casos especiais.
Nenhum corpo
Um 204 significa sucesso sem corpo, de propósito. A resposta também não tem Content-Type, porque não há nada para descrever:
/api/formats/no-contentHTTP/1.1 204 No Content
x-powered-by: TestingAPIsCódigo cliente que sempre chama response.json() vai quebrar aqui, porque uma string vazia não é JSON válido. Se você escrever helpers de teste que interpretam toda resposta, eles precisam tratar um corpo vazio.
Quando os cabeçalhos mentem
Status, cabeçalhos e corpo são produzidos por partes diferentes do servidor, e nada obriga que concordem. Aqui está uma resposta que diz ser HTML:
/api/formats/wrong-content-typeHTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
{"id":1,"name":"Alice Johnson","email":"alice@example.com","roles":["admin","editor"],"active":true,"score":98.5,"manager":null}O corpo é um JSON perfeitamente válido. Uma pessoa lendo nunca notaria um problema. Um navegador tentaria exibi-lo como página web, e algumas bibliotecas HTTP vão se recusar a interpretá-lo como JSON ou devolvê-lo como string. Este endpoint está quebrado de propósito. Em sistemas reais, o mesmo bug geralmente vem de uma página de erro gerada por um proxy ou por um padrão do framework. O seu teste deve conferir o Content-Type explicitamente.
O contrário também acontece: um status 200 com um corpo que diz {"success": false}. Algumas APIs reportam todo erro assim. Ferramentas de monitoramento que contam respostas 5xx nunca veem essas falhas, e nem um teste que confere só o código de status. Se uma API faz isso, os seus testes precisam ler o corpo de toda resposta.
O que conferir em toda resposta
Antes de olhar um único campo de dados, confirme o básico. Leva alguns segundos e pega uma quantidade surpreendente de bugs.
- O código de status é o que a documentação promete para essa situação.
Content-Typebate com o corpo que você realmente recebeu.- Os cabeçalhos que a documentação menciona (paginação,
Location,Retry-After) estão lá e fazem sentido. - O corpo pode ser interpretado e tem o formato que você espera.
- As respostas de erro seguem o mesmo formato dos outros erros desta API.
Exercício
Para cada requisição, anote o código de status, o Content-Type e uma frase sobre o corpo.
/api/status/418. Pesquise por que esse código existe./api/formats/emptyentre para ver. Qual a diferença para/api/formats/no-contententre para ver? Um devolve 200 e o outro 204. Qual você acha que um cliente trata melhor?/api/Products?limit=2&page=99. Não existe página 99. Que status e corpo você recebeu, e o que os cabeçalhos de paginação dizem? Você teria projetado do mesmo jeito?/api/status/list. Encontre na lista um código marcado com"supported": falsee tente pedi-lo.