Skip to content

Requests and responses

  • Base URL: https://api.learningaide.ai. Every path in this document is relative to it, so POST /v1/papers means POST https://api.learningaide.ai/v1/papers.
  • HTTPS only, with TLS 1.2 or later.
  • JSON request and response bodies in UTF-8. Send Content-Type: application/json on requests with a body, uncompressed. A request body may be up to 1 MB; documents go through Files.
  • Field names are snake_case.
  • Timestamps are ISO 8601 strings in UTC, for example 2026-10-06T09:15:30Z.
  • Numbers: marks are numbers with up to 2 decimal places, such as 1.5. Credits and page counts are whole numbers. A percentage has 1 decimal place.
  • IDs are opaque strings of up to 64 characters with a type prefix, such as sub_7Hk2Pz9QaN. Store them as given and don’t parse them.
  • Every object has an object field naming its type. Objects that belong to a mode also have a livemode field.
  • Every documented field is always present in a response; it is null when it doesn’t apply.
  • Unknown request fields are rejected with 400 parameter_unknown, so typos surface at once.
  • Responses can gain new fields at any time. Ignore fields you don’t recognise.
  • A machine-readable OpenAPI 3.1 description of every request, response and webhook is at https://api.learningaide.ai/v1/openapi.json. It needs no key.
  • external_id on a paper is your own ID for it. It must be unique per mode, and you can look papers up by it.
  • student.external_id on a submission is your ID for the student. Use a stable ID that is not personal data (see Data protection).
  • metadata is an object of up to 20 string key–value pairs, available on papers and submissions. Keys can be up to 40 characters and values up to 500 characters. We store it, return it and include it in webhooks, but never act on it. Don’t put personal data in it. A PATCH that includes metadata replaces the whole object.

Question text, answer keys, students’ answers and feedback are Markdown, including tables and bold, with two additions:

  • Maths is LaTeX between $…$ inside a line, or $$…$$ on a line of its own.
  • Chemistry is mhchem \ce{…}, with no dollar signs around it, for example \ce{CaCO3(s) -> CaO(s) + CO2(g)}.

We never use $ for money in these fields, so every $ starts or ends maths. KaTeX with its mhchem extension renders all of it. Feedback is written in English.

List endpoints return objects in pages:

{
"object": "list",
"data": [ { "id": "sub_7Hk2Pz9QaN", "object": "submission" } ],
"has_more": true,
"next_cursor": "c_8f2kQ1x9LmZt"
}
Parameter Meaning
limit Items per page, from 1 to 100. Default 20.
cursor The next_cursor from the previous page. Omit it for the first page.
created_after, created_before Only objects created after or before this time (ISO 8601, exclusive).
updated_after Only objects changed after this time (exclusive).
  • Results are newest first.
  • With updated_after they are oldest change first instead, ordered by updated_at, so you can sync by saving the updated_at of the last item you processed and passing it next time.
  • Keep requesting with cursor until has_more is false. A cursor stays valid while you page through, even if objects change.

Every response carries an X-Request-Id header, such as 3f2b8c1e-9d4a-4e7b-a6c5-1f0e2d3c4b5a. Log it, and include it when you contact support about a request.

Code Meaning
200 OK The request succeeded.
201 Created A new object was created. Its processing may continue in the background.
202 Accepted Not ready yet. Retry after the number of seconds in Retry-After.
204 No Content The object was deleted.
4xx The request has a problem you need to fix. See Errors.
5xx Something went wrong on our side. Retry with backoff.