Browse the documentation

Reference

Errors and rate limits

Every failure has the same shape, and every code is stable. The code is what your retry logic should branch on; the message is written for whoever is reading a log.

The error shape

json
{
  "error": "insufficient_balance",
  "message": "Not enough balance for this call. Top up to continue."
}

One code, one meaning, on every endpoint. A code is never reworded into a different code, so error is safe to switch on. The message may change.

The two money refusals carry extra fields, because the fix is worth putting in the same response rather than making you ask for it. insufficient_balance adds balancePence and topUpUrl; topup_required adds topUpUrl alone, since the balance is not what failed. balancePence is an integer number of pence, like every money field on this API.

json
{
  "error": "insufficient_balance",
  "message": "Not enough balance for this call. Top up to continue.",
  "balancePence": 2,
  "topUpUrl": "https://www.revisiongenie.com/developers"
}

Every code

Grouped by what went wrong rather than by status, since that is how you debug.

Your request

CodeStatusWhen
invalid_request400The body could not be read, is not valid JSON, is not a JSON object, or a field failed validation. The message names the field and says what was expected.
invalid_id400A path id is not a 24-character hex id. Checked before any lookup, so a typo reads as a 400 and never as a server error.
invalid_query400A required query parameter is missing or too short. Search needs a q of at least two characters.
request_too_large413The body exceeded the endpoint's cap: 32 KB for chat and align, 8 KB for question generation. Measured in bytes on the raw text, before parsing.

What you asked for

CodeStatusWhen
not_found404No subject or subtopic with that id. Inactive, archived and user-created content reads the same as missing, deliberately.
subject_not_found404Align could not resolve the subject. A name matching several subjects with no qualification or board given resolves to nothing rather than to a guess.
subtopic_not_found404Question generation found no subtopic with that id in either hierarchy.
genie_not_found404No system subject genie with that shortName. Unknown, unlinked and user-created genies all return this.

Your credentials and your account

CodeStatusWhen
invalid_key401Missing, malformed, unknown, revoked or expired key, or a key that is not a developer key. One refusal for all of them.
forbidden403Valid key, wrong scope. Mint a key with the scope the endpoint needs.
suspended403The developer account is suspended. Data reads stop too, not only billable calls.
billing_not_enabled403The paid endpoints are not enabled on this account. Contact support.
insufficient_balance402The balance does not cover the call. Carries balancePence and topUpUrl.
topup_required402This account has never topped up, so the curriculum data endpoints are not unlocked yet. One top-up of any amount we offer unlocks them permanently, whatever your balance does afterwards. Carries topUpUrl. It is a different thing from insufficient_balance, which is a balance that has run out.

Limits and our side

CodeStatusWhen
rate_limited429A ceiling was reached. Also returned after repeated failed authentication from one address. Wait a minute and retry.
upstream_failed502The model was unavailable, or returned nothing at all. The call is not charged.
generation_failed502Question generation failed or produced nothing usable. The call is not charged.
server_error500Something failed on our side. Safe to retry.

What is never charged

A charge is taken after a request has been fully validated and never before, so a call we were never going to answer cannot cost you anything. Specifically, none of these moves your balance:

  • A malformed request. Every field is validated before any charge, so a missing field, a bad type or an oversized body is free.
  • An unknown resource. The subject, subtopic or genie is resolved before the charge, even though that is a database read we may then throw away. An unknown id costs you nothing.
  • A safeguarding refusal. Detection runs before the charge and before the model. A refusal is answered with an HTTP 200 and a refusal event, and nothing is taken.
  • A call where the model delivered nothing. If generation fails, or a stream produces no content at all, the charge is returned in full and the response says so.
  • A rate-limited call. The ceiling is checked before the charge.

The one case that is charged and not refunded is a stream that fails part way through after content has already reached you. You received an answer, even a partial one. That is rare, and a disputed message is a support matter rather than something we resolve automatically.

Developer terms, clause 10.4

A call that fails because of a fault on our side is not charged, and where a charge has already been taken for such a call we return it to your balance.

Rate limits

Limits are per key, not per account, so one runaway job cannot throttle your other integrations. They are counted per minute, and calls beyond a limit are refused rather than queued.

  • 120 calls a minute across everything a key does.
  • 30 calls a minute on the paid endpoints, on top of the ceiling above. It is lower because those calls cost real money to serve.
  • Failed authentication is limited by address. Repeated bad keys from one address get a 429 rather than another 401, so guessing costs the guesser.

Keying on the key rather than the address is deliberate: your product probably calls us from a handful of servers, and your users may sit behind one shared address, so an address bucket would throttle you for reasons that have nothing to do with you.

Expecting a launch or a start-of-term peak? Tell us in advance at support@revisiongenie.com and we will do what we can to accommodate it. Working around a limit by spreading traffic across several keys or accounts is a breach of the terms, not a workaround.

Back off, do not hammer

On a 429, wait and retry with a growing delay. Retrying immediately in a loop keeps you limited, and on the paid endpoints the limiter fails closed during an infrastructure problem: if we cannot count your calls we refuse them, because the limit is part of what you paid for.

Retrying safely

  • Retry on 429, 500 and 502, with a growing delay. The 502 codes tell you the call was not charged, so a retry costs you the same as the first attempt.
  • Do not retry on 400, 401, 403, 404 or 413. Nothing about the same request will succeed.
  • On 402, top up. Both codes carry a link. insufficient_balance also carries your live balance and means you have run out; topup_required means you have never topped up and the data endpoints are still locked. One top-up fixes either.
  • Never retry past a safeguarding refusal. Retrying to get an answer out of a refused message is a breach of the developer terms, clause 8.7.

Revision Genie is an independent product and is not affiliated with or endorsed by AQA, Pearson Edexcel, OCR, WJEC or Eduqas.

Use of this API is governed by the developer terms. Questions: support@revisiongenie.com.