Raw examination questions by subject, exam type, and year — the foundation layer every other endpoint builds on.
The Questions endpoint returns examination questions sourced from WAEC, JAMB, NECO, POST-UTME, and state boards. Every question carries its options, correct answer, and exam context — subject, year, and exam type are normalised so you never have to clean the data yourself.
L1 is intentionally narrow. It does not return topic, difficulty, or explanation — that's 2.2 Metadata and 2.3 Explanations. You pay 1 credit per request regardless of how many questions a filtered call returns.
At least one filter — subject, examType, or year — is required on every call. This isn't a quirk, it's by design: unfiltered access is how datasets like this get scraped wholesale.
Pass your API key in the X-API-Key header on every request. Full setup is covered in 1.1 Authentication — the short version:
subjectstringone of threeSubject name, e.g. mathematicsexamTypeenumone of threewaec · neco · jamb · post_utme · stateyearintegerone of threeExamination year, 1990–2030topicL2stringnoFilter by topic — requires Developer+ tierdifficultyL2int 1–5noFilter by difficulty score — requires Developer+ tiercountrystringno2-letter ISO code, e.g. NGrandombooleannoRandom order, capped at 10 resultscursorstringnoPagination cursor from a previous responselimitint ≤50noResults per page — default 20curl "https://dev.aloc.com.ng/api/v1/questions?subject=mathematics&examType=jamb&year=2019" \
-H "X-API-Key: alc_live_7f3a91c2d8e4f1ab92dd"{
"data": [
{
"id": "f47ac10b-58cc...",
"text": "What is the value of x...",
"options": { "a": "3", "b": "5", "c": "7", "d": "10" },
"correctAnswer": "b",
"examType": "jamb",
"subject": "mathematics",
"year": 2019,
"country": "NG"
}
],
"pagination": { "nextCursor": "eyJpZCI6Mj...", "hasMore": true },
"meta": { "creditsUsed": 1, "tier": "growth" }
}Results page with an opaque cursor, not an offset. Pass pagination.nextCursor from one response as the cursor param on the next call. When hasMore is false, you're at the end of the result set.
There's no sequential offset mode and no plan tier unlocks one. Access is session and search-based by design — see 2.1.7 Exam sessions for the structured alternative.
Filtered list calls are fine for browsing. For an actual exam paper or practice test — where a candidate needs a fixed, non-repeating set of questions — use a session instead. It's the pattern the API expects for structured exams, and it's why sequential pagination doesn't exist on this endpoint: a session allocates the questions once, server-side, rather than letting a client page through the whole bank.
Allocate a psychometrically balanced examination paper. count defaults to 40. The session expires in 2 hours for secure, anti-scraping delivery.
Fetch the questions allocated to that session. Billed once per assessment assembly, not per individual question.
Calling GET /sessions/{id}/questions after the 2-hour window returns 403, not a stale result. Re-issue a new session rather than retrying the old ID.
For English Language and Literature subjects, questions based on reading passages include dedicated fields to allow clean UI rendering on frontend applications:
hasPassagebooleanReturned as true for English/Literature comprehension questions. Omitted for non-passage and non-English subjects.sectionstring | nullContains the full reading comprehension passage text. Render this block at the top or side-by-side with the question.categorystring | nullPassage group identifier (e.g. passage-a, passage-b) linking consecutive questions belonging to the same passage.When building exam sessions via POST /sessions, passage groups are automatically preserved in order so students see all questions for a passage grouped together. Pass excludePassages: true if you want grammar/vocabulary questions only.
30 req/min on Free, scaling with tier. Watch X-RateLimit-Remaining rather than counting locally — it's the source of truth.