OMR Answer Sheet Reader

Upload one or more scanned/photographed answer sheets. Processing runs locally on this server — nothing is sent anywhere else.

API Reference

Use this to integrate the reader into other systems (grading tools, student portals, scripts, etc.) without the browser UI.

Endpoint

POST /api/process

Authentication

Send your API key either as a header (preferred for scripts/servers) or a form field (used by this page's browser upload):

X-API-Key: <your-secret>

— or —

Content-Type: multipart/form-data
api_key=<your-secret>

Request

multipart/form-data with one or more files under the field name files. Multiple files can be sent in a single request.

curl example

curl -X POST https://ocr.srv1586461.hstgr.cloud/api/process \
     -H "X-API-Key: your-secret-key" \
     -F "files=@sheet1.jpg" \
     -F "files=@sheet2.jpg"

JavaScript (fetch) example

const form = new FormData();
form.append('files', fileInput.files[0]);

const res = await fetch('https://ocr.srv1586461.hstgr.cloud/api/process', {
  method: 'POST',
  headers: { 'X-API-Key': 'your-secret-key' },
  body: form
});
const data = await res.json();

Python example

import requests

resp = requests.post(
    "https://ocr.srv1586461.hstgr.cloud/api/process",
    headers={"X-API-Key": "your-secret-key"},
    files=[("files", open("sheet1.jpg", "rb")),
           ("files", open("sheet2.jpg", "rb"))],
)
data = resp.json()

Response

200 OK — one result object per uploaded file, in the same order:

{
  "results": [
    {
      "source_file": "sheet1.jpg",
      "header_ocr_raw": {
        "name": "...", "student_id": "...", "program": "...",
        "cnic": "...", "date": "..."
      },
      "header_note": "Handwritten fields are OCR-best-effort only; verify manually.",
      "total_questions": 100,
      "bubbles_detected": 399,
      "answers": { "1": "A", "2": "B", "3": null, "...": "..." },
      "unanswered_or_undetected": [28, 61, 93],
      "flags": { "86": " ambiguous/multiple marks" }
    }
  ]
}

answers maps question number (string) to the detected option ("A""D") or null if unanswered/undetected. Always check unanswered_or_undetected and flags before treating a sheet as fully graded.

Error responses

401 Unauthorized   { "detail": "Invalid or missing API key" }
400 Bad Request    { "detail": "No files uploaded" }

Per-file processing errors (e.g. an unreadable image) don't fail the whole batch — that file's result object will contain an "error" field instead of answers.

Health check

GET /health  →  { "status": "ok" }