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

  1. 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.
  2. Buy gradings on the top-up page (from 10). A sheet costs 0.4 of a grading. Bought gradings never expire.
  3. Call POST /grade from 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.

Request

Fields

  • questionsRequired, 1 to 100. Each has a prompt (up to 4,000 characters) and ideally maxMarks (up to 1,000) and expectedAnswer (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. Add options (2 to 10, up to 500 characters each) for multiple choice, marked all-or-nothing. Use subQuestions (1 to 20) for parts (a), (b), each with its own maxMarks and an optional label of up to 10 characters. id is 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. Public https URLs (JPEG, PNG, WebP or HEIC, 10 MB each) or data: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 with POST /uploads, as [{ "uploadId": "upl_…" }], each id once, in page order after any pages. 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. Every questionId must 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 or answers, 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

200

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.

Upload flow

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 header Idempotent-Replayed: true. It is not graded or charged again.
  • while the first request is still grading: 409 idempotency_in_progress with Retry-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.

StatusCodeRouteMeaning
400invalid_jsongrade, uploadsThe request body is not JSON.
400invalid_requestgrade, uploadsThe 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.
401invalid_api_keyallMissing, wrong or revoked key.
402insufficient_balancegradeNot enough bought gradings for this sheet. `balance` and `price` say how short.
409idempotency_in_progressgradeA request with the same Idempotency-Key is still being graded. Retry after `Retry-After` seconds to get its result.
422unreadable_pagegradeA 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.
422too_many_pagesgradeMore than 20 pages in total, counting every page of every PDF. `pages` and `max` give the numbers.
422upload_not_foundgradeAn uploadId is unknown, has expired, or belongs to another account.
422upload_incompletegradeThe upload exists but its file was never PUT.
422idempotency_key_mismatchgradeThe Idempotency-Key was already used with a different request body. Use a new key.
429rate_limitedgrade, uploadsOver the per-key limit: 60 grade requests a minute (20 for a test key), 120 upload requests a minute. Honour `Retry-After`.
429too_many_uploadsuploadsThe account already holds 500 uploads that have not been graded. Grade them or wait for them to expire. `Retry-After` is 300.
502grading_failedgradeThe grader could not finish the sheet. The charge is returned automatically; `id` identifies the attempt. Safe to retry.
503unavailablegrade, uploadsGrading, uploads or idempotency keys are temporarily unavailable, or uploaded files could not be read just now. Nothing was charged; retry shortly.

Limits

Price0.4 of a grading per sheet (about $0.10, ₹10 in India), charged only on a 200.
Pages per sheet20, counting every page of every PDF, across `pages` and `files` together.
Questions1 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 answersUp 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 sizeUnder 4.5 MB in total. Send photos as URLs or uploads, not inline data URLs.
FilesImages (JPEG, PNG, WebP, HEIC) up to 10 MB, PDFs up to 30 MB. Up to 20 files per upload call.
UploadsPUT URL valid 15 minutes. Kept 72 hours, deleted as soon as graded. 500 ungraded uploads per account.
Rate limits60 grade requests a minute per live key, 20 per test key. 120 upload requests a minute per key.
Idempotency-Key8 to 255 characters of letters, digits, `_ - : .`. Results kept encrypted for 72 hours.
KeysUp to 5 active keys per account, live or test. Shown once at creation.
TimeA 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-Authenticate challenge, 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.

Moodle plugin page

Questions: support@proctorai.io