Public API
Grading API
Send a marking key and photos of a student's handwritten answer sheet. Get back marks per question with the reasoning, what the grader read on the page, a confidence score, and a flag on anything a teacher should check. It is the same grader teachers use inside Proctor AI.
$0.10 a sheet
₹10 in India. Up to 20 pages, any number of questions.
Prepaid
Charged before the sheet is graded, returned if grading fails. Only a 200 costs anything.
Short-lived
Uploads are deleted once graded (72 hours at most). With an Idempotency-Key the result is kept encrypted for 72 hours, only to answer retries.
Base URL https://agent.proctorai.io/api/v1. Machine-readable description: openapi.json (OpenAPI 3.1, generated from the same schemas the server validates with).
Getting started
- Sign in and create a key in Settings, API keys. It is shown once; store it as a secret. Tick test key to build against the API for free.
- Buy gradings on the top-up page (from 10). A sheet costs 0.4 of a grading. Bought gradings never expire.
- Call
POST /gradefrom your server. Keys must never be put in a browser or an app.
The API spends only gradings you have bought. Your plan's monthly gradings and pilot gradings stay for grading inside the app.
Authentication
Send the key as Authorization: Bearer pai_live_…, or as X-API-Key if your client cannot set Authorization. Up to 5 active keys per account.
Test keys
A test key (pai_test_…) goes through every check, upload and idempotency step like a live key, but is never charged and never reaches the model. Instead of a real grade it returns fixed marks: odd rows of the marking key get full marks, even rows get half marks and needsReview: true, so your integration sees both cases. The response carries "test": true. Test keys are limited to 20 requests a minute and GET /account reports a zero price for them.
POST /grade
One request grades one student's sheet. Send the marking key as questions and the student's work as pages, uploaded files, typed answers or one answerSheetText. Set a client timeout of at least 300 seconds.
Fields
questionsRequired, 1 to 100. Each has aprompt(up to 4,000 characters) and ideallymaxMarks(up to 1,000) andexpectedAnswer(up to 8,000). Without a max the grader reads it off the paper or picks one and flags it; without an expected answer it solves the question itself. Addoptions(2 to 10, up to 500 characters each) for multiple choice, marked all-or-nothing. UsesubQuestions(1 to 20) for parts (a), (b), each with its ownmaxMarksand an optionallabelof up to 10 characters.idis 1 to 40 characters of letters, digits,.,_and-; it defaults to the position ("1", "2"). Ids must be unique.pagesThe answer sheet in order, up to 20. PublichttpsURLs (JPEG, PNG, WebP or HEIC, 10 MB each) ordata:image/…;base64,URLs. The whole request must stay under 4.5 MB, so use URLs for photos. The grader finds each answer by the student's own numbering, on any page.filesFiles you uploaded withPOST /uploads, as[{ "uploadId": "upl_…" }], each id once, in page order after anypages. A PDF counts as one page per PDF page. The 20-page limit covers both together.answersTyped answers per question, instead of or as well as pages: up to 500 of{ "questionId": "1", "subQuestionId": "a", "text": "…" }, 20,000 characters each. EveryquestionIdmust exist.answerSheetTextThe student's typed work as one document (up to 50,000 characters) when it is not split by question, for example an essay box in an LMS. Use this oranswers, not both.title, level, studentNameOptional context (200, 80 and 120 characters). The name is read from the cover page when omitted.instructionsOptional marking guidance in plain words, up to 2,000 characters, for example "ignore spelling" or "award method marks generously".
At least one of pages, files, answers or answerSheetText is required. Unknown fields are rejected.
Response
status is answered, not_attempted, illegible or partially_legible. Totals already apply the paper's rules: a question never scores above its max and objective items are all-or-nothing. Questions the grader could not find score 0 with needsReview: true. pages are 1-based page numbers where the answer was found. billing.balance is what is left after this call.
Reading the response
POST /uploads
For files you cannot put at a public URL (a school server, an LMS behind a firewall), and for PDFs. Ask for one upload per file, PUT each file's bytes to its url with exactly the headers given, then name the uploads in files. Free. Images up to 10 MB, PDFs up to 30 MB, up to 20 files per call. Each PUT URL works for 15 minutes. Uploads can be graded for 72 hours, are deleted as soon as they are graded, and only your account can use them. An account may hold 500 ungraded uploads at once; past that you get 429 too_many_uploads.
Idempotency
Send Idempotency-Key: <your id> (8 to 255 characters of letters, digits, _ - : .) on POST /grade, one key per sheet, for example your submission id. Grading is slow and paid up front, so a client that times out and retries must not be charged twice. If the same key comes again within 72 hours:
- after a
200: the same result is returned with the headerIdempotent-Replayed: true. It is not graded or charged again. - while the first request is still grading:
409 idempotency_in_progresswithRetry-After. - with a different body:
422 idempotency_key_mismatch. - after an error: it runs fresh, because only successful results are kept.
- if the first run was cut off mid-grade: the retry takes over, and if the first run had already been charged, the retry is not charged again.
Keys belong to your account, not to one API key, so rotating a key keeps retries working. The stored result is encrypted and deleted after 72 hours. Retrying without a key may grade the sheet twice, and both are charged. Retrying after a 502 is always free.
GET /account
Free. Returns balance, pricePerSheet, sheetsRemaining, maxPagesPerSheet, acceptedTypes, maxImageBytes and maxPdfBytes, so you can check before a batch instead of discovering a 402 halfway through. With a test key, test is true, pricePerSheet is 0 and sheetsRemaining is a nominal 1000.
Errors
Errors are { "error": { "code", "message", … } }. Some add fields such as issues, balance, price, pages, max or id. Only a 200 is ever charged.
| Status | Code | Route | Meaning |
|---|---|---|---|
| 400 | invalid_json | grade, uploads | The request body is not JSON. |
| 400 | invalid_request | grade, uploads | The body does not match the schema (`issues` lists each field), question ids repeat, an uploadId repeats, both `answers` and `answerSheetText` were sent, an answer names an unknown question, the Idempotency-Key is malformed, or an upload is over its size limit. |
| 401 | invalid_api_key | all | Missing, wrong or revoked key. |
| 402 | insufficient_balance | grade | Not enough bought gradings for this sheet. `balance` and `price` say how short. |
| 409 | idempotency_in_progress | grade | A request with the same Idempotency-Key is still being graded. Retry after `Retry-After` seconds to get its result. |
| 422 | unreadable_page | grade | A page or upload could not be downloaded, is not a supported image or PDF, is over the size limit, was declared as one type but is another, or is a damaged or password-protected PDF. |
| 422 | too_many_pages | grade | More than 20 pages in total, counting every page of every PDF. `pages` and `max` give the numbers. |
| 422 | upload_not_found | grade | An uploadId is unknown, has expired, or belongs to another account. |
| 422 | upload_incomplete | grade | The upload exists but its file was never PUT. |
| 422 | idempotency_key_mismatch | grade | The Idempotency-Key was already used with a different request body. Use a new key. |
| 429 | rate_limited | grade, uploads | Over the per-key limit: 60 grade requests a minute (20 for a test key), 120 upload requests a minute. Honour `Retry-After`. |
| 429 | too_many_uploads | uploads | The account already holds 500 uploads that have not been graded. Grade them or wait for them to expire. `Retry-After` is 300. |
| 502 | grading_failed | grade | The grader could not finish the sheet. The charge is returned automatically; `id` identifies the attempt. Safe to retry. |
| 503 | unavailable | grade, uploads | Grading, uploads or idempotency keys are temporarily unavailable, or uploaded files could not be read just now. Nothing was charged; retry shortly. |
Limits
| Price | 0.4 of a grading per sheet (about $0.10, ₹10 in India), charged only on a 200. |
| Pages per sheet | 20, counting every page of every PDF, across `pages` and `files` together. |
| Questions | 1 to 100 per request, up to 20 sub-questions each. Prompts up to 4,000 characters, expected answers up to 8,000, options 2 to 10. |
| Typed answers | Up to 500 `answers` of 20,000 characters each, or one `answerSheetText` of 50,000 characters. |
| Other fields | `title` 200, `level` 80, `studentName` 120, `instructions` 2,000 characters. Ids: 1 to 40 characters of letters, digits, `.`, `_`, `-`. |
| Request size | Under 4.5 MB in total. Send photos as URLs or uploads, not inline data URLs. |
| Files | Images (JPEG, PNG, WebP, HEIC) up to 10 MB, PDFs up to 30 MB. Up to 20 files per upload call. |
| Uploads | PUT URL valid 15 minutes. Kept 72 hours, deleted as soon as graded. 500 ungraded uploads per account. |
| Rate limits | 60 grade requests a minute per live key, 20 per test key. 120 upload requests a minute per key. |
| Idempotency-Key | 8 to 255 characters of letters, digits, `_ - : .`. Results kept encrypted for 72 hours. |
| Keys | Up to 5 active keys per account, live or test. Shown once at creation. |
| Time | A sheet takes 20 to 90 seconds; the request may run for up to 300 seconds. |
Claude connector
Proctor AI as an MCP server for Claude
Teachers can connect Claude (Desktop, claude.ai or Cline) to their own Proctor AI account and ask it to list assignments, read submissions, grade papers against the rubric, save grades and export reports. Everything is scoped to the signed-in teacher. This is a separate surface from the API key above: it uses OAuth, not keys, and spends the teacher's plan gradings.
- Endpoint
https://agent.proctorai.io/api/mcp, Streamable HTTP. - Auth: OAuth 2.0. The client follows the
WWW-Authenticatechallenge, the teacher signs in and approves access, and a token is issued. No manual key. - Alternative for clients without OAuth: a private connect URL from Settings, Connect to Claude, with the token embedded.
Moodle plugin
Grade Moodle assignments with this API
The Proctor AI Moodle plugin uses POST /uploads and POST /grade exactly as documented here: one upload per submitted file, one grade call per student, an Idempotency-Key per submission, and GET /account before a batch. A test key lets a Moodle administrator try the whole flow without buying gradings.
Questions: support@proctorai.io
