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.

Resposta crua
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.

ClasseSignificadoDe quem é a culpa
1xxInformativa. A requisição ainda está em andamento.De ninguém. Você raramente as vê diretamente.
2xxSucesso. O servidor fez o que você pediu.De ninguém.
3xxRedirecionamento. Procure em outro lugar.De ninguém. O cliente precisa dar o próximo passo.
4xxErro do cliente. A requisição estava errada.Do cliente: dados ruins, sem permissão, URL errada.
5xxErro 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ódigoNomeUso típico
200OKA requisição funcionou e o corpo traz o resultado.
201CreatedUm POST criou um registro novo.
204No ContentFuncionou e, de propósito, não há corpo. Comum depois de um DELETE.
301 / 302Moved Permanently / FoundO recurso está na URL do cabeçalho Location.
304Not ModifiedA sua cópia em cache continua atual. Visto junto com cache.
400Bad RequestA requisição está malformada ou algum valor é inválido.
401UnauthorizedSem credenciais, ou com credenciais erradas. Apesar do nome, significa não autenticado.
403ForbiddenAs credenciais estão certas, mas este usuário não pode fazer isto.
404Not FoundNão há nada nesta URL, ou nenhum registro com este ID.
405Method Not AllowedA URL existe, mas não com este método.
409ConflictA requisição conflita com o estado atual, como um e-mail duplicado.
422Unprocessable ContentA sintaxe está certa, mas os dados quebram uma regra.
429Too Many RequestsVocê atingiu um limite de taxa. Vá mais devagar.
500Internal Server ErrorUm erro não tratado no servidor.
502Bad GatewayUm proxy recebeu uma resposta ruim do servidor por trás dele.
503Service UnavailableO 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:

GET/api/status/201
GET/api/status/503

No curl, o 503 fica assim:

Resposta
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:

Resposta para GET /api/status/302
HTTP/1.1 302 Found
Content-Type: application/json; charset=utf-8
location: /api/echo

Navegadores 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:

GET/api/Products?limit=2&page=3
Cabeçalhos de resposta
HTTP/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: 10

Content-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:

GET/api/formats/no-content
Esta requisição usa um endpoint para membros conectados.EntrarCriar uma conta gratuita
Resposta
HTTP/1.1 204 No Content
x-powered-by: TestingAPIs

Có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:

GET/api/formats/wrong-content-type
Esta requisição usa um endpoint para membros conectados.EntrarCriar uma conta gratuita
Resposta
HTTP/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-Type bate 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": false e tente pedi-lo.

Pratique e vá além