MegaExams API v1 - REST, JSON, Bearer auth

Build on the
MegaExams question bank.

A simple REST API to read and create questions, list and inspect exams, pull published results, and browse the library - all scoped to your organization, all over plain JSON.

Base URL
https://megaexams.com/api/v1
Envelope
{ "data": ..., "meta": ... }
Rate limit
120 requests / minute per key

Authentication

One Bearer key per organization

Create a key in the admin panel under Settings > API access. This card is visible to the organization owner only.

Name the key, pick its scopes, and create it. The full key is shown once - copy it immediately. We store only a hash, so it can never be shown again. You can revoke a key at any time.

Send it as a Bearer token on every request:

questions:read questions:write exams:read results:read

A request without the required scope returns 403 insufficient_scope. Keys only ever see their own organization's data.

# Set your key and base URL
KEY="mx_live_your_key_here"
BASE="https://megaexams.com/api/v1"

# Every request carries the Bearer token
curl -s "$BASE/questions?per_page=3" \
  -H "Authorization: Bearer $KEY"

Every authenticated response includes X-RateLimit-Limit and X-RateLimit-Remaining headers. Over the limit returns 429 rate_limited.

Endpoint reference

Eight endpoints, one bank

GET /questions scope: questions:read

List the organization's question bank. Filter by type, difficulty, grade, subject_id, chapter_id, topic_id, tag, q (text search), and language. Paginate with page and per_page.

Request
curl -s "$BASE/questions?type=mcq&per_page=3" \
  -H "Authorization: Bearer $KEY"
Response
{
  "data": [
    {
      "id": "f17c4bc1-3e16-476a-aa48-1b91b0739aae",
      "type": "mcq",
      "difficulty": "easy",
      "language": "en",
      "question_text": "What is 2+2?",
      "points": 1,
      "negative_marks": 0,
      "curriculum": { "subject": "Mathematics", "chapter": "Arithmetic", "topic": "Addition" },
      "tags": ["api-test", "math"],
      "created_at": "2026-06-11T15:11:17Z"
    }
  ],
  "meta": { "page": 1, "per_page": 3, "total": 8, "total_pages": 3 }
}
GET /questions/{id} scope: questions:read

Fetch a single question with its type-specific fields: options for MCQ, accepted_answers for fill_blank, and rubric / model_answer / word_limit for essay.

Request
curl -s "$BASE/questions/f17c4bc1-3e16-476a-aa48-1b91b0739aae" \
  -H "Authorization: Bearer $KEY"
Response
{
  "data": {
    "id": "f17c4bc1-3e16-476a-aa48-1b91b0739aae",
    "type": "mcq",
    "question_text": "What is 2+2?",
    "points": 1,
    "options": [
      { "id": "a90fe079-...", "option_text": "4", "is_correct": true, "sort_order": 0 },
      { "id": "451e807d-...", "option_text": "5", "is_correct": false, "sort_order": 1 }
    ],
    "images": []
  }
}
POST /questions scope: questions:write

Create a question in your bank. category_path resolves a "Subject > Chapter > Topic" path. MCQ needs at least 2 options with at least 1 correct. Returns 201 with the created question.

Request
curl -s -X POST "$BASE/questions" \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "mcq",
    "question_text": "Which planet is known as the Red Planet?",
    "difficulty": "easy",
    "category_path": "Physics > Laws of Motion > Newton's First Law",
    "tags": ["astronomy"],
    "options": [
      { "option_text": "Mars", "is_correct": true },
      { "option_text": "Venus", "is_correct": false }
    ]
  }'
Response
{
  "data": {
    "id": "86b9e62e-80ca-421c-b4ff-5a6703d13768",
    "type": "mcq",
    "question_text": "Which planet is known as the Red Planet?",
    "curriculum": { "subject": "Physics", "chapter": "Laws of Motion", "topic": "Newton's First Law" },
    "tags": ["api-docs", "astronomy"],
    "options": [
      { "id": "5534564f-...", "option_text": "Mars", "is_correct": true, "sort_order": 0 }
    ],
    "images": []
  }
}
GET /taxonomy scope: questions:read

Your full Subject > Chapter > Topic tree.

Request
curl -s "$BASE/taxonomy" -H "Authorization: Bearer $KEY"
Response
{
  "data": [
    {
      "id": "e081ee4e-...",
      "name": "Mathematics",
      "chapters": [
        {
          "id": "24b941c7-...",
          "name": "Arithmetic",
          "topics": [ { "id": "a1fac685-...", "name": "Addition" } ]
        }
      ]
    }
  ]
}
GET /exams scope: exams:read

List the organization's exams. Filter by status (draft | active | inactive). Paginate with page and per_page.

Request
curl -s "$BASE/exams?status=active&per_page=2" \
  -H "Authorization: Bearer $KEY"
Response
{
  "data": [
    {
      "id": "315c41d9-b4f5-4590-80d6-d4c9b0159115",
      "title": "PlayerTest IBPS Fast",
      "slug": "ptst01",
      "status": "active",
      "visibility": "link",
      "question_count": 4,
      "negative_marking": true,
      "shuffle_scope": "section",
      "share_url": "https://megaexams.com/e/ptst01",
      "created_at": "2026-06-11T14:39:09Z"
    }
  ],
  "meta": { "page": 1, "per_page": 2, "total": 10, "total_pages": 5 }
}
GET /exams/{id} scope: exams:read

Fetch an exam with its sections and questions. Option is_correct is included only if the key also holds questions:read.

Request
curl -s "$BASE/exams/315c41d9-b4f5-4590-80d6-d4c9b0159115" \
  -H "Authorization: Bearer $KEY"
Response
{
  "data": {
    "id": "315c41d9-...",
    "title": "PlayerTest IBPS Fast",
    "status": "active",
    "sections": [
      { "id": "05064e13-...", "name": "Reasoning", "sort_order": 0, "time_limit_mins": 1, "instructions": "Answer all questions in order." }
    ],
    "questions": [
      {
        "id": "b0845d82-...",
        "type": "mcq",
        "question_text": "Which number completes the series 2, 4, 8, 16, ?",
        "points": 2,
        "section_id": "05064e13-...",
        "sort_order": 0,
        "options": [
          { "id": "20761d38-...", "option_text": "32", "sort_order": 1, "is_correct": true }
        ]
      }
    ]
  }
}
GET /exams/{id}/results scope: results:read

Published, ranked results for an exam, including per-student scores and section breakdowns. Paginate with page and per_page.

Request
curl -s "$BASE/exams/315c41d9-b4f5-4590-80d6-d4c9b0159115/results?per_page=5" \
  -H "Authorization: Bearer $KEY"
Response
{
  "data": [
    {
      "id": "17475d85-...",
      "student": { "name": "Ravi Tester", "city": "Chennai", "class_grade": "9", "institution": "Demo Public School" },
      "marks_obtained": 0,
      "total_marks": 10,
      "percentage": 0,
      "correct": 0,
      "incorrect": 0,
      "unanswered": 4,
      "rank_overall": 1,
      "total_time_secs": 121,
      "submitted_at": "2026-06-11T14:44:41Z",
      "section_scores": [
        { "section_name": "Quant", "marks_obtained": 0, "total_marks": 7 }
      ]
    }
  ],
  "meta": { "page": 1, "per_page": 5, "total": 1, "total_pages": 1 }
}
GET /library scope: exams:read

Pre-made papers visible to your organization (platform papers and parent-organization shares).

Request
curl -s "$BASE/library" -H "Authorization: Bearer $KEY"
Response
{
  "data": [
    {
      "id": "add7bcee-...",
      "title": "Class 9 Physics - Laws of Motion (Unit Test)",
      "source": "platform",
      "question_count": 3,
      "total_points": 7,
      "time_limit_mins": 20,
      "language": "en"
    }
  ],
  "meta": { "page": 1, "per_page": 1, "total": 1, "total_pages": 1 }
}

Error codes

Predictable, machine-readable errors

Errors always use the envelope { "error": { "code", "message" } } with the matching HTTP status.

HTTP code When
400 invalid_request The POST body is not a JSON object
401 invalid_api_key Missing, malformed, unknown, or revoked key
403 insufficient_scope The key lacks the required scope
404 not_found Resource not in the key's org, or unknown endpoint
405 method_not_allowed Wrong HTTP method on a known path
422 validation_failed The question payload failed validation
422 plan_limit_reached The org is at its question-bank limit
429 rate_limited More than 120 requests per minute for the key
500 server_error Unexpected failure

Ready to build?

Create an API key in Settings > API access, grab the OpenAPI spec, and start in minutes.