Skip to main content
Todos os exemplos assumem a chave em ENEMHUB_API_KEY e a base https://api.enemhub.com.br.

Filtrar questões

GET /v1/enem/questions aceita quatro filtros, todos opcionais e combináveis:
Questões difíceis de 2023
Não existe endpoint para listar matérias no produto ENEM. Para descobrir um subjectId, puxe uma página de questões e leia o subject.id — os IDs são estáveis.

Paginar até o fim

meta.total é a contagem do conjunto filtrado inteiro, então dá para calcular o número de páginas antes de começar:
limit=100 é o teto. Puxar o acervo inteiro a 20 por vez custa cinco vezes mais requisições da sua cota do que a 100 por vez.

Buscar uma questão específica

Diferente da listagem, esta rota devolve a questão sem envelope:
Um ID que não existe — ou que pertence a um exame fora do produto ENEM — devolve 404:

Estrutura da resposta

Cada questão retornada contém os seguintes campos:
Exemplo completo de uma questão
Na listagem (GET /v1/enem/questions), a resposta vem dentro de um envelope { "data": [...], "meta": { "page", "limit", "total" } }. Na busca por ID (GET /v1/enem/questions/:id), a questão vem diretamente, sem envelope.

statement é HTML, não texto puro

Cerca de 1 em cada 3 questões tem imagem — mas não existe um campo imageUrl separado para ela. A imagem vem embutida no HTML do próprio statement, como <figure><img src="..."></figure> no meio dos parágrafos:
Renderize statement como HTML (sanitizado), não como texto — se você jogar o valor cru num nó de texto, o cliente mostra as tags em vez da questão. text das alternativas, por outro lado, é sempre texto puro: nenhuma alternativa tem imagem embutida.

Montar uma prova

Correta e alternativas vêm juntas na resposta, então embaralhar e esconder o gabarito é trabalho do seu lado:

Lidar com limites

Toda resposta de /v1/* traz dois headers: Ao estourar, a resposta é 429 com Retry-After: 60. São três limites independentes:
  • Por minuto — definido pelo plano.
  • Por dia — também definido pelo plano, em torno de 3× a média diária da cota mensal.
  • Por mês — a cota do plano. Ao estourar, a mensagem pede upgrade.
Uma requisição recusada com 429 não consome cota mensal. Estourar o limite por minuto não queima o seu volume contratado.
Retry com backoff

Tratar erros

O corpo do erro não é uniforme em toda a API — documentamos o que existe hoje, não o que gostaríamos que existisse.As rotas de gestão devolvem { "error", "message", "statusCode" }. Os erros de autenticação e de rate limit devolvem { "error", "message" }, sem statusCode. E o 404 das rotas de questão devolve só { "error": "Questão não encontrada" }.Ou seja: trate pelo status HTTP, que é consistente, e use error / message apenas para exibir ou registrar em log.
Um wrapper que cobre o caso comum: