Paid
Generate exam-style questions
POST
/api/v1/questions/generate8p- Scope
ai:questions- Price
- 8p per call
A set of questions with their answers, as plain text: written questions with mark schemes, multiple-choice questions with options and the correct answer, or both. Anchor a set on one of our subtopics or on a topic in your own words.
What to know
- THE PRICE IS PER SET, NOT PER QUESTION AND NOT PER TYPE. 8p whether you ask for 1 written question or 5 split across both shapes.
- ANCHOR ON A SUBTOPIC OR ON A TOPIC, NEVER BOTH. A subtopic gets you questions written against our curriculum and pitched by that subject’s own genie. A topic gets you questions about your own wording, which is what to use when what you teach does not line up with a subtopic of ours.
- `count` IS THE TOTAL FOR THE SET, shared out across the types you asked for, with the odd question going to written. Asking for both shapes with a count of 1 therefore returns one written question and no multiple choice: ask for at least 2 to get both.
- READ THE `type` FIELD, NOT THE POSITION. Every question carries `type`, and written questions come first. A written question has `marks`, `markScheme` and `markSchemePoints`; a multiple-choice question has `options`, `correctAnswer` (always one of `options` verbatim) and usually an `explanation`.
- `markScheme` and `markSchemePoints` ARE THE SAME CONTENT. The array is the string split on its line breaks. The string is the original field and is not going anywhere.
- ONLY SUBTOPIC-ANCHORED WRITTEN QUESTIONS ENTER THE SHARED REVISION GENIE POOL, and they carry an `id`. You are paying for the generation, not for exclusivity: they are our content, written against our subtopics, and students on our platform may see them too. Topic-anchored questions and multiple-choice questions are not stored anywhere, so keep what you are sent.
- THE SUBJECT FIELDS ARE A HINT, NOT A FILTER. With a `topic`, naming a subject pitches the questions at that qualification and board. A subject we cannot resolve is not an error: you get generally-pitched questions and `subject: null` in the response, so you can see the hint did not land.
- `difficulty` is a HINT. The topic-anchored and multiple-choice generators act on it; the subtopic-anchored written generator records it against the generation without changing its prompt, so treat it as a request rather than a contract.
- YOU MAY GET FEWER QUESTIONS THAN YOU ASKED FOR. A question the generator returns without a usable answer is dropped rather than sent. A call that produces one shape but not the other still delivers, and is charged; a call that produces nothing at all is refunded in full.
- Nothing here is checked by a person before it reaches you. Review anything you show a user, particularly a child (developer terms clause 9.2).
- Generation is one or two large model calls and can take a while. Allow a generous timeout.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| subtopicId | string | No | A subtopic id from either tree. We work out which hierarchy it belongs to, so you do not have to. Send this OR "topic", never both. |
| topic | string | No | A topic in your own words, 3 to 200 characters, for example "photosynthesis and limiting factors". Send this OR "subtopicId", never both. |
| types | string[] | No | Which shapes to return: any of written, multipleChoice. Defaults to both for a "topic", and to written alone for a "subtopicId" so an existing integration is unchanged. |
| count | integer | No | How many questions in the set, 1 to 5. Defaults to 3. It is the TOTAL for the set, shared out across the types you asked for. |
| difficulty | string | No | One of foundation, standard, higher. A hint, not a guaranteed banding. |
| subjectId | string | No | Optional with "topic": pitch the questions at one of our subjects. Ignored with "subtopicId", which already knows its subject. |
| subject | string | No | Optional with "topic": a subject name, for example "Biology", instead of an id. Narrow it with "qualification" and "examBoard". |
| qualification | string | No | Narrows a subject name, for example "GCSE". |
| examBoard | string | No | Narrows a subject name, for example "AQA". |
Example request
curl
curl -X POST "https://www.revisiongenie.com/api/v1/questions/generate" \
-H "Authorization: Bearer rgk_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{"topic":"Photosynthesis","types":["written","multipleChoice"],"count":2,"subject":"Biology","qualification":"GCSE","examBoard":"AQA"}'Example response
json
// anchored on a topic, both shapes
{
"source": "topic",
"subtopic": null,
"topic": "Photosynthesis",
"subject": {
"id": "6612f0a1c3b4d5e6f7a80912",
"name": "Biology",
"qualification": "GCSE",
"examBoard": "AQA"
},
"questions": [
{
"type": "written",
"question": "Explain why the rate of photosynthesis stops rising once light intensity is very high.",
"marks": 3,
"markScheme": "Light is no longer the limiting factor (1 mark)\nAnother factor limits the rate (1 mark)\nAccept: carbon dioxide concentration or temperature (1 mark)",
"markSchemePoints": [
"Light is no longer the limiting factor (1 mark)",
"Another factor limits the rate (1 mark)",
"Accept: carbon dioxide concentration or temperature (1 mark)"
]
},
{
"type": "multipleChoice",
"question": "Which gas is taken in by a leaf during photosynthesis?",
"options": ["Oxygen", "Carbon dioxide", "Nitrogen", "Hydrogen"],
"correctAnswer": "Carbon dioxide",
"explanation": "Carbon dioxide diffuses in through the stomata and is combined with water to make glucose."
}
],
"usage": { "costPence": 8, "balancePence": 4808 }
}
// anchored on a subtopic, written only (what you get with no "types")
{
"source": "subtopic",
"subtopic": {
"id": "6612f0e5c3b4d5e6f7a80d01",
"name": "Eukaryotes and prokaryotes",
"topic": "Paper 1: Cell biology and organisation",
"subject": "Biology",
"hierarchy": "exam"
},
"topic": null,
"subject": {
"id": "6612f0a1c3b4d5e6f7a80912",
"name": "Biology",
"qualification": "GCSE",
"examBoard": "AQA"
},
"questions": [
{
"type": "written",
"id": "6613a1b2c3d4e5f6a7b80101",
"question": "(a) Name two structures found in a plant cell but not in an animal cell. [2]\n(b) Explain why a bacterial cell is described as prokaryotic. [3]",
"marks": 5,
"markScheme": "(a) Cell wall (1); chloroplast / permanent vacuole (1) ...",
"markSchemePoints": ["(a) Cell wall (1); chloroplast / permanent vacuole (1) ..."],
"parts": [
{
"partNumber": "a",
"question": "Name two structures found in a plant cell but not in an animal cell.",
"marks": 2,
"answerFormat": "lined"
}
]
}
],
"usage": { "costPence": 8, "balancePence": 4800 }
}Errors
Every error carries the same shape: a stable error code to branch on and a message for a human. Codes specific to this endpoint come first.
| Code | Status | When |
|---|---|---|
| insufficient_balance | 402 | The balance does not cover the call. The body carries `balancePence` and a top-up link. |
| billing_not_enabled | 403 | The paid endpoints are not enabled on this developer account. |
| invalid_request | 400 | The body is not JSON, is not a JSON object, or a field failed validation. The message names the field. |
| request_too_large | 413 | The request body is larger than 8 KB. |
| subtopic_not_found | 404 | No subtopic with that id in either hierarchy, or it could not be resolved to a subject we generate for. Never returned for a "topic": a topic we cannot place is still generated for. |
| generation_failed | 502 | The generator produced nothing usable at all. The call is not charged. |
| invalid_key | 401 | The key is missing, malformed, revoked, expired, or is not a developer key. |
| forbidden | 403 | The key is valid but does not hold the scope this endpoint requires. |
| suspended | 403 | The developer account is suspended. Data reads stop as well as billable calls. |
| rate_limited | 429 | The key went past its ceiling: 120 calls a minute overall, 30 a minute on the paid endpoints. Also returned after repeated failed authentication from one address. |
| server_error | 500 | Something failed on our side. Retry; a paid call is refunded. |