Como testar um endpoint de API: DNS, TLS, cabeçalhos e CORS
Ponto-chave
"A API caiu" quase nunca é um problema só. Teste em seis camadas (DNS, porta 443, TLS, status HTTP, CORS, payload) e pare na primeira que falhar. Os culpados típicos: um registro DNS que nunca propagou, um certificado que expirou durante a noite, ou um cabeçalho Access-Control-Allow-Origin ausente que quebra as chamadas do navegador enquanto o curl funciona normalmente.
Verifique agora

Comece a ler
- Por que "a API caiu" nunca é um problema só
- Escolha um endpoint real que você possa testar à vontade
- Camada 1: o nome do host resolve?
- Camada 2: a porta 443 está aberta?
- Camada 3: o certificado TLS é válido?
- Camada 4: leia os cabeçalhos da resposta
- Como é uma resposta JSON saudável
- Camada 5: o cabeçalho que só quebra chamadas do navegador
- A armadilha do preflight: funciona até você adicionar um cabeçalho
- Camada 6: o payload tem o formato esperado?
- Sintoma → camada → ferramenta
- Perguntas frequentes
Por que "a API caiu" nunca é um problema só
Quando uma chamada de API falha, o erro que seu cliente mostra costuma ser gerado várias camadas acima do que realmente quebrou. Um navegador que informa Failed to fetch não consegue dizer se o nome do host não resolveu, se a conexão TCP foi recusada, se o handshake TLS foi rejeitado, ou se o servidor devolveu um 200 perfeitamente saudável que o navegador descartou por falta de um cabeçalho CORS. Os quatro casos geram a mesma mensagem.
A solução é parar de adivinhar e percorrer a pilha em ordem. Cada camada só faz sentido se a de baixo funcionou, então a primeira camada que falha é o seu bug de verdade; tudo que vem depois é ruído.
Há seis camadas que valem a verificação, e juntas levam cerca de dois minutos:
- DNS: o nome do host resolve para algum IP?
- Porta 443: tem algo escutando, ou a conexão é recusada ou filtrada?
- TLS: o certificado é válido, não expirou e é servido com a cadeia completa?
- Status HTTP: o que o servidor realmente retorna: 200, 401, 429, 503?
- CORS: a resposta traz os cabeçalhos de que um navegador precisa para entregar o corpo a você?
- Payload: o JSON tem o formato que seu código espera?
Verifique agora o status e os cabeçalhos de um endpoint
Escolha um endpoint real que você possa testar à vontade
Para praticar você precisa de um endpoint no ar que retorne dados reais, não exija chave de API e não bloqueie você por repetir a mesma requisição vinte vezes. Isso descarta a maioria dos candidatos óbvios: quase toda API pública conhecida agora exige um formulário de cadastro e um token, o que significa que uma requisição com falha pode sempre ser culpa das suas credenciais, e não da camada que você está testando.
Os exemplos abaixo usam a API gratuita de horários de oração do Waqt Azan (مواقيت الصلاة), um serviço árabe de horários de oração que cobre mais de 60.000 lugares em mais de 100 países. Ela funciona bem como endpoint de aprendizado por três motivos: não há chave nem cabeçalho de autenticação, retorna JSON estruturado em vez de HTML e define uma política CORS permissiva. Assim, os comandos curl rodam exatamente como estão, e o exemplo com fetch() funciona no console de uma página sem uma Content-Security-Policy rígida.
O endpoint recebe o slug do país e da cidade direto no caminho:
# Prayer times for a city — no API key, no headers required
curl -s https://waqtazan.com/api/v1/timings/saudi-arabia/riyadh
# The same data as a subscribable calendar feed
curl -s https://waqtazan.com/api/v1/ical/saudi-arabia/riyadh.icsTroque pelo host que você está realmente depurando: as seis camadas são idênticas, seja uma API pública, seu próprio servidor de staging ou um receptor de webhook de terceiros.
Camada 1: o nome do host resolve?
O DNS é um culpado frequente. Um registro foi alterado e não propagou, um wildcard foi removido, ou a API fica em um subdomínio que nunca foi criado.
Resolva o nome do host antes de qualquer outra coisa. Se este passo não retornar nada, reiniciar a aplicação não vai ajudar: o seu cliente nem chega a abrir um socket.
dig +short waqtazan.com A
nslookup waqtazan.com
# Compare what different resolvers see — catches mid-propagation records
dig +short waqtazan.com A @1.1.1.1
dig +short waqtazan.com A @8.8.8.8Se dois resolvedores públicos discordam logo depois de você alterar um registro, ele ainda está propagando, e a API vai parecer quebrada de forma intermitente conforme o resolvedor que cada cliente usa (uma CDN com roteamento geográfico pode entregar sempre um endereço diferente a cada resolvedor, então compare com o registro que você definiu). Faça uma consulta DNS para ver o conjunto completo de registros, ou confira a propagação em resolvedores do mundo todo no verificador de propagação de DNS logo após alterar um registro.
Uma resposta vazia aqui corresponde diretamente a ERR_NAME_NOT_RESOLVED no Chrome e a getaddrinfo ENOTFOUND no Node: ERR_NAME_NOT_RESOLVED traz a lista completa de correções.
Uma coisa com que não se preocupar: muitas APIs ficam atrás de uma CDN, então o registro A aponta para uma borda da CDN e não para o servidor de origem. Isso é esperado. O que importa é obter uma resposta, não qual é o IP.
Camada 2: a porta 443 está aberta?
O nome do host resolve, então existe um endereço. Agora descubra se algo está escutando nele. É aqui que você separa três falhas bem diferentes que os clientes adoram reportar do mesmo jeito:
- Aberta: o handshake TCP é concluído. Passe para a próxima camada.
- Recusada: o host respondeu com um RST imediato. Há algo ali, mas nada está associado àquela porta: geralmente o serviço travou, ou escuta apenas em localhost.
- Filtrada: a conexão fica pendurada até estourar o tempo limite. Um firewall ou grupo de segurança descarta pacotes em silêncio, e por isso sua requisição espera um minuto ou mais antes de falhar (75 segundos no macOS).
# Time-boxed TCP check: -w limits the connect on Linux; on macOS use -G 5 instead
nc -vz -w 5 waqtazan.com 443
# Or with curl: --connect-timeout caps the connect; 000 means no response came back
curl -s -o /dev/null -w '%{http_code}\n' --connect-timeout 5 https://waqtazan.com/Uma conexão recusada e uma filtrada pedem correções completamente diferentes, então vale saber qual é o seu caso. O verificador de portas distingue as duas e testa de fora da sua rede, o que importa: uma porta aberta pela VPN do escritório pode estar bloqueada por firewall na internet pública.
Se o curl imprimir 000 como código de status, nenhuma resposta HTTP voltou. Geralmente a requisição falhou nas camadas 1 a 3 (sem resposta de DNS, porta recusada ou filtrada, handshake TLS com falha), mas um servidor que aceita a conexão e depois a fecha, ou nunca responde, também gera 000; adicione -v para ver o último passo que funcionou. ERR_CONNECTION_REFUSED cobre o caso da recusa em detalhes.
Camada 3: o certificado TLS é válido?
A porta 443 está aberta, mas porta aberta não significa endpoint HTTPS funcionando. Certificados são uma quebra clássica da noite para o dia, porque expiram em data marcada e a automação de renovação falha em silêncio.
O certificado precisa atender a três condições ao mesmo tempo: não estar expirado, ter um nome que corresponda ao host solicitado e o servidor enviar a cadeia intermediária completa. A terceira é a traiçoeira: uma cadeia incompleta muitas vezes funciona nos navegadores, porque o Chrome baixa sozinho o intermediário que falta e o Firefox os pré-carrega, enquanto Node, Python, Java e builds do curl com OpenSSL falham (o curl que vem com o macOS usa o verificador da Apple e pode passar). Essa é a origem do clássico relato "funciona no meu navegador, mas não em produção".
# Expiry dates and subject
echo | openssl s_client -connect waqtazan.com:443 -servername waqtazan.com 2>/dev/null \
| openssl x509 -noout -subject -dates
# Full chain as presented by the server
openssl s_client -connect waqtazan.com:443 -servername waqtazan.com -showcerts < /dev/nullConte os certificados na saída de -showcerts. Um único certificado geralmente significa um intermediário ausente. O verificador de certificado SSL informa validade, completude da cadeia e correspondência do nome do host de uma só vez, e o que é uma cadeia de certificados SSL explica por que o intermediário importa tanto.
Mantenha -servername: o OpenSSL 1.1.1 e posteriores preenchem o SNI a partir do host de -connect, mas o openssl LibreSSL que vem com o macOS não preenche, e nenhuma versão preenche quando você se conecta a um endereço IP. Sem SNI, uma hospedagem compartilhada entrega o certificado padrão dela ou, como a Cloudflare, aborta o handshake, e você acaba perseguindo uma divergência de nome que não existe.
Camada 4: leia os cabeçalhos da resposta
O TLS está bom, então você finalmente tem uma conversa HTTP de verdade. Agora leia o que o servidor realmente disse, em vez do que seu cliente resumiu para você. Imprima só os cabeçalhos: o corpo ainda é baixado e descartado, então é o mesmo GET que seu cliente envia.
curl -s -D - -o /dev/null https://waqtazan.com/api/v1/timings/egypt/cairoLeia primeiro a linha de status, depois o content-type, depois o resto. Esses dois campos dizem se a requisição teve sucesso e se o corpo é JSON antes de você ler qualquer outra coisa.
Como é uma resposta JSON saudável
Rodar o comando acima contra o endpoint de teste em 27 de outubro de 2026 retornou estes cabeçalhos, entre outros:
| Cabeçalho | Valor | O que ele diz |
|---|---|---|
HTTP/2 | 200 | A requisição teve sucesso e o servidor fala HTTP/2 |
content-type | application/json | Você vai receber JSON, não uma página de erro HTML |
content-length | 567 | Um payload de verdade, não um corpo vazio |
access-control-allow-origin | * | Os navegadores podem ler esta resposta |
server | cloudflare | Uma CDN fica na frente da origem |
alt-svc | h3=":443" | HTTP/3 é anunciado para as requisições seguintes |
Camada 5: o cabeçalho que só quebra chamadas do navegador
É nesta camada que as horas somem, porque é onde o curl e o navegador realmente discordam. O CORS é aplicado pelo navegador, não pelo servidor. Se Access-Control-Allow-Origin estiver ausente, o servidor ainda devolve um 200 completo e correto: o navegador simplesmente se recusa a entregar o corpo ao seu JavaScript.
Então o sintoma é este: o curl funciona perfeitamente, o Postman funciona perfeitamente, e o console do navegador diz que a requisição foi bloqueada pela política CORS. Não há nada de errado com o servidor. A resposta só está sem um cabeçalho.
O endpoint de teste retorna access-control-allow-origin: *, e é por isso que um fetch() simples a partir de qualquer origem funciona:
// Run it in the console of a tab on a site without a strict CSP, such as example.com
// (this page's own CSP blocks it). It works there because ACAO is set to *
const r = await fetch('https://waqtazan.com/api/v1/timings/egypt/cairo')
console.log(await r.json())A armadilha do preflight: funciona até você adicionar um cabeçalho
Existe uma segunda pegadinha, e este endpoint a demonstra muito bem. Uma requisição simples (um GET sem cabeçalhos personalizados) funciona. Mas no momento em que você adiciona um cabeçalho personalizado, ou faz POST com Content-Type: application/json, o navegador envia antes um preflight OPTIONS, e a resposta ao preflight precisa trazer seus próprios cabeçalhos CORS (MDN).
Teste o preflight separadamente, porque é uma resposta diferente, vinda de um caminho de código diferente:
curl -s -D - -o /dev/null -X OPTIONS \
-H 'Origin: https://example.com' \
-H 'Access-Control-Request-Method: GET' \
https://waqtazan.com/api/v1/timings/egypt/cairoEm 27 de outubro de 2026, esse preflight retorna um 200 com content-type: text/html e nenhum cabeçalho access-control-allow-origin, então uma requisição com preflight vinda de um navegador seria bloqueada, mesmo que o GET simples funcione bem.
Essa é exatamente a armadilha: sua integração funciona em desenvolvimento, você adiciona um cabeçalho Authorization ou X-Request-Id, e ela quebra sem nenhuma mudança no servidor. Se você só precisa de dados públicos, mantenha a requisição simples e ela continuará funcionando.
Camada 6: o payload tem o formato esperado?
A última camada é a que seu código realmente importa. Um 200 com JSON válido ainda pode quebrar você se um campo foi renomeado, um tipo mudou de número para string, ou um timestamp chega em um formato que você não interpreta.
Olhe a resposta real antes de escrever o parser. Este endpoint retorna tanto horários formatados para exibição quanto timestamps ISO, um padrão que vale copiar: as strings localizadas são para pessoas, os valores ISO são para o código:
{
"city": { "slug": "riyadh", "name_ar": "الرياض", "name_en": "Riyadh" },
"country": "saudi-arabia",
"date": "2026-10-26",
"times": { "fajr": "4:38 ص", "dhuhr": "11:37 ص", "maghrib": "5:17 م" },
"timesISO": { "fajr": "2026-10-26T01:38:00.000Z", "dhuhr": "2026-10-26T08:37:00.000Z" },
"method": "umm_al_qura"
}Sempre faça o parse do campo legível por máquina. Aqui isso significa timesISO, nunca times: as strings de exibição são localizadas e mudarão de formato assim que o idioma mudar.
Passe a resposta pelo jq para inspecionar a estrutura rapidamente: curl -s <url> | jq keys lista os campos de primeiro nível, e jq '.times' entra em um campo sem rolar pelo corpo bruto.
Sintoma → camada → ferramenta
Use esta tabela para ir direto à camada que importa, em vez de percorrer as seis toda vez:
| O que você vê | Camada com falha | Verifique com |
|---|---|---|
ERR_NAME_NOT_RESOLVED, ENOTFOUND | 1 — DNS | Consulta DNS |
ECONNREFUSED, falha instantânea | 2 — Porta | Verificador de portas |
| A requisição trava e depois expira | 2 — Porta filtrada | Verificador de portas |
CERT_HAS_EXPIRED, UNABLE_TO_VERIFY_LEAF_SIGNATURE | 3 — TLS | Verificador SSL |
| Funciona no navegador, falha no curl ou no Node | 3 — Cadeia incompleta | Verificador SSL |
401, 429, 503 | 4 — Status HTTP | Verificador de Cabeçalhos HTTP |
| Funciona no curl, bloqueado no navegador | 5 — CORS | Verificador de Cabeçalhos HTTP |
Unexpected token '<', "<!DOCTYPE "... is not valid JSON | 4 — Página de erro HTML | Verificador de Cabeçalhos HTTP |
| Campos ausentes ou com tipo errado | 6 — Payload | curl … | jq |
A latência fica um pouco fora desta escada. Se todas as camadas passam mas as respostas são lentas, meça o tempo de ida e volta até o host com o ping e veja se a própria resolução de nomes é o gargalo com um teste de velocidade de DNS.
Veja os cabeçalhos de resposta reais de qualquer endpoint
Veja o código de status, o content-type e os cabeçalhos CORS exatos que uma API retorna, solicitados pelo servidor, então nenhum cache de navegador ou extensão distorce o resultado. Grátis, sem cadastro.
Fontes
Perguntas frequentes
Como testar um endpoint de API sem o Postman?
Por que minha API funciona no curl mas falha no navegador?
O que significa o curl retornar o código de status 000?
Como saber se uma API está fora do ar para todo mundo ou só para mim?
Por que uma API retorna 200 mas meu parser JSON lança um erro?
O que é um preflight CORS e quando ele acontece?
Devo testar o DNS da API com meu próprio resolvedor ou com um público?
Uma porta 443 aberta significa que o endpoint HTTPS funciona?
Artigos Relacionados
Ferramentas Relacionadas
Verificador de headers HTTP
Analise cabeçalhos de resposta HTTP e classificação de cabeçalhos de segurança
Consulta DNS
Todos os registros DNS direto dos servidores do domínio, ao lado do cache do Google e da Cloudflare
Verificador de Portas
Teste a conectividade de portas e disponibilidade de serviços
Verificador de Certificado SSL
Verifique a validade e detalhes de segurança do certificado SSL
Teste de Ping
Meça seu ping, jitter e perda de pacotes, seu ping para 34 regiões de nuvem e faça ping em qualquer site ou IP a partir de 11 cidades



