Testbook APItestbook.com ↗
Search Testbook exams, courses, and test series. List exam categories like SSC, Banking, Railways. Fetch upcoming live classes by category. 3 endpoints.
What is the Testbook API?
The Testbook API gives developers structured access to India's government exam preparation platform across 3 endpoints. The search endpoint queries exams, courses, test series, articles, and more in a single call, returning per-type result counts and item arrays. The list_exam_categories endpoint enumerates all major exam super groups, and get_upcoming_classes returns scheduled free lessons with instructor and series details.
curl -X GET 'https://api.parse.bot/scraper/fd84e6b2-7849-4708-adad-1bdac1e30c63/search' \ -H 'X-API-Key: $PARSE_API_KEY'
Typed, relational, agent-ready
A generated client with real types, enums, and the links between objects — the structure a flat JSON response can't carry. Autocompletes in your editor and reads cleanly to coding agents.
- Fully typed · autocompletes
- Objects link to objects
- Typed errors & pagination
Typed Python client. Set up the SDK in your uv project, then pull this API’s typed client:
uv add parse-sdk uv run parse init uv run parse add --marketplace testbook-com-api
uv run parse add --marketplace pulls a pinned snapshot of this canonical API — it won’t change underneath you. To customize it, subscribe and swap to your own copy.
"""
Testbook Exam Preparation API Client
Get your API key from: https://parse.bot/settings
"""
import os
import requests
from typing import Optional, Dict, Any, List
from datetime import datetime
class ParseClient:
"""Client for Testbook Exam Preparation API via Parse.bot"""
def __init__(self, api_key: Optional[str] = None):
"""Initialize the Parse API client.
Args:
api_key: API key for authentication. If not provided, reads from PARSE_API_KEY env var.
"""
self.base_url = "https://api.parse.bot"
self.scraper_id = "fd84e6b2-7849-4708-adad-1bdac1e30c63"
self.api_key = api_key or os.getenv("PARSE_API_KEY")
if not self.api_key:
raise ValueError("API key not provided and PARSE_API_KEY environment variable not set")
def _call(self, endpoint: str, method: str = "POST", **params) -> Dict[str, Any]:
"""Make an API call to the Parse.bot scraper.
Args:
endpoint: The API endpoint name (e.g., 'search', 'list_exam_categories')
method: HTTP method - 'GET' or 'POST'
**params: Query/request parameters
Returns:
Response JSON as dictionary
Raises:
requests.exceptions.RequestException: If the API call fails
"""
url = f"{self.base_url}/scraper/{self.scraper_id}/{endpoint}"
headers = {
"X-API-Key": self.api_key,
"Content-Type": "application/json"
}
try:
if method.upper() == "GET":
response = requests.get(url, headers=headers, params=params, timeout=30)
elif method.upper() == "POST":
response = requests.post(url, headers=headers, json=params, timeout=30)
else:
raise ValueError(f"Unsupported HTTP method: {method}")
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"API Error: {e}")
raise
def search(
self,
term: str,
search_on: Optional[str] = None,
language: str = "English"
) -> Dict[str, Any]:
"""Search across Testbook's content.
Args:
term: Search query term (e.g., 'SSC', 'Banking', 'Railways')
search_on: Comma-separated content types to search.
Default: 'targets,courses,testSeries,tests,articles,quizzes'
language: Language for results ('English' or 'Hindi'). Default: 'English'
Returns:
Dictionary with search results, counts, and ordering
"""
params = {"term": term, "language": language}
if search_on:
params["search_on"] = search_on
return self._call("search", method="GET", **params)
def list_exam_categories(self, language: str = "English") -> Dict[str, Any]:
"""List all exam category super groups available on Testbook.
Args:
language: Language for results ('English' or 'Hindi'). Default: 'English'
Returns:
Dictionary containing list of exam categories
"""
return self._call("list_exam_categories", method="GET", language=language)
def get_upcoming_classes(
self,
super_group_id: Optional[str] = None,
skip: int = 0,
limit: int = 9,
language: str = "English"
) -> Dict[str, Any]:
"""Get upcoming and live free classes for a given exam super group.
Args:
super_group_id: Super group ID from list_exam_categories.
Default: Teaching Exams ID
skip: Number of results to skip for pagination. Default: 0
limit: Maximum number of lessons to return. Default: 9
language: Language for results ('English' or 'Hindi'). Default: 'English'
Returns:
Dictionary containing lessons and count
"""
params = {
"skip": skip,
"limit": limit,
"language": language
}
if super_group_id:
params["super_group_id"] = super_group_id
return self._call("get_upcoming_classes", method="GET", **params)
def main():
"""Practical workflow demonstrating Testbook API usage."""
# Initialize the client
client = ParseClient()
print("=" * 60)
print("TESTBOOK EXAM PREPARATION API - PRACTICAL WORKFLOW")
print("=" * 60)
# Step 1: Get all exam categories
print("\n[Step 1] Fetching all exam categories...")
categories_response = client.list_exam_categories(language="English")
categories = categories_response.get("categories", [])
print(f"Found {len(categories)} exam categories:")
for category in categories[:5]: # Show first 5
print(f" • {category['title']}")
print(f" ID: {category['id']}")
print(f" Target Exams: {category['targets_count']}")
# Step 2: Search for SSC exams
print("\n[Step 2] Searching for 'SSC' exams...")
search_results = client.search(
term="SSC",
search_on="targets,courses,testSeries",
language="English"
)
# Extract and display search results
if "count" in search_results:
print("Search Results Summary:")
for content_type, count_info in search_results["count"].items():
value = count_info.get("value", 0)
relation = count_info.get("relation", "eq")
print(f" • {content_type}: {value} ({relation})")
# Display actual results per content type
if "results" in search_results:
results_data = search_results["results"]
# Show targets (exams)
if "targets" in results_data and results_data["targets"]:
print(f"\nFound {len(results_data['targets'])} Target Exams:")
for target in results_data["targets"][:3]:
title = target.get("properties", {}).get("title", "Unknown")
slug = target.get("slug", "")
print(f" • {title} (ID: {target.get('_id')})")
# Show test series
if "testSeries" in results_data and results_data["testSeries"]:
print(f"\nFound {len(results_data['testSeries'])} Test Series:")
for series in results_data["testSeries"][:3]:
name = series.get("name", "Unknown")
total_tests = series.get("paidTestCount", 0) + series.get("freeTestCount", 0)
attempts = series.get("totalAttempts", 0)
print(f" • {name}")
print(f" Total Tests: {total_tests}, Attempts: {attempts:,}")
# Show courses
if "courses" in results_data and results_data["courses"]:
print(f"\nFound {len(results_data['courses'])} Courses:")
for course in results_data["courses"][:3]:
name = course.get("name", "Unknown")
print(f" • {name}")
# Step 3: Get upcoming classes for a specific category (Banking Exams)
print("\n[Step 3] Fetching upcoming classes for Banking category...")
# Find Banking Exams category
banking_category = next(
(cat for cat in categories if "Banking" in cat["title"]),
None
)
if banking_category:
classes_response = client.get_upcoming_classes(
super_group_id=banking_category["id"],
limit=5,
language="English"
)
lessons = classes_response.get("lessons", [])
print(f"Found {len(lessons)} upcoming/live classes for {banking_category['title']}:")
for lesson in lessons[:3]: # Show first 3
name = lesson.get("name", "Unknown")
start_time = lesson.get("start_time", "")
series = lesson.get("series_name", "Unknown Series")
instructors = lesson.get("instructors", [])
is_live = lesson.get("is_live_now", False)
print(f"\n • {name}")
print(f" Series: {series}")
if instructors:
instructor_names = ", ".join([i.get("name", "Unknown") for i in instructors])
print(f" Instructor: {instructor_names}")
if start_time:
try:
scheduled_time = datetime.fromisoformat(start_time.replace('Z', '+00:00'))
print(f" Scheduled: {scheduled_time.strftime('%Y-%m-%d %H:%M UTC')}")
except (ValueError, AttributeError):
print(f" Scheduled: {start_time}")
if is_live:
print(" Status: 🔴 LIVE NOW")
# Step 4: Search for specific exam preparation content
print("\n[Step 4] Searching for 'Railways' exam content...")
railways_search = client.search(
term="Railways",
search_on="articles,tests,pyp",
language="English"
)
if "count" in railways_search:
print("Railways Content Available:")
for content_type, count_info in railways_search["count"].items():
value = count_info.get("value", 0)
if value > 0:
print(f" • {content_type}: {value} items")
print("\n" + "=" * 60)
print("Workflow completed successfully!")
print("=" * 60)
if __name__ == "__main__":
main()Global search across Testbook's content including exams (targets), courses, test series, tests, articles, quizzes, videos, free tests, previous year papers, and goal cards. Returns result counts per content type and actual result items.
| Param | Type | Description |
|---|---|---|
| termrequired | string | Search query term (e.g. 'SSC', 'Banking', 'Railways'). |
| language | string | Language for results. Accepts: English, Hindi. |
| search_on | string | Comma-separated list of content types to search. Accepted values: targets, courses, testSeries, tests, articles, quizzes, videos, freeTests, pyp, goalCards. |
{
"type": "object",
"fields": {
"count": "object containing result counts per content type",
"order": "array of content type names in display order",
"results": "object containing arrays of result items per content type",
"totalCount": "object with total result count value and relation"
},
"sample": {
"count": {
"tests": {
"value": 10000,
"relation": "gte"
},
"courses": {
"value": 192,
"relation": "eq"
},
"targets": {
"value": 325,
"relation": "eq"
},
"articles": {
"value": 7292,
"relation": "eq"
}
},
"order": [
"targets",
"courses",
"testSeries"
],
"results": {
"targets": [
{
"_id": "5e6189da5f66e94f14a21f58",
"slug": "ssc-cgl-exam",
"properties": {
"title": "SSC CGL"
}
}
],
"testSeries": [
{
"id": "68e369fddfe41a9c30482098",
"name": "SSC GD Constable 2026 Mock Test Series",
"slug": "ssc-gd-constable",
"freeTestCount": 59,
"paidTestCount": 3019,
"totalAttempts": 1469406
}
]
},
"totalCount": {
"value": 27899,
"relation": "gte"
}
}
}About the Testbook API
What the API Covers
The Testbook API surfaces content from one of India's largest competitive exam preparation platforms, covering government exams like UPSC, SSC, Banking, Railways, and Teaching. Three endpoints cover global content search, exam category enumeration, and upcoming live class schedules. Both Hindi and English language variants are supported across all endpoints.
Search Endpoint
The search endpoint accepts a term string (e.g. 'SSC', 'Banking') and an optional search_on parameter that filters results to specific content types: targets, courses, testSeries, tests, and article. The response includes a count object with per-type tallies, an order array that reflects display ordering, a results object with typed arrays of matching items, and a totalCount object with the aggregate result figure and its relation type.
Exam Categories and Upcoming Classes
The list_exam_categories endpoint returns an array of category objects, each containing an id, a title (e.g. "SSC Exams", "Banking Exams"), a logo URL, and a count of target exams within that category. The id field from each category can be passed directly as the super_group_id parameter to get_upcoming_classes. That endpoint supports skip and limit for pagination and returns a count integer plus a lessons array — each lesson carrying schedule data, instructor details, and series information.
The Testbook API is a managed, monitored endpoint for testbook.com — not a raw scraper you maintain. Every endpoint is automatically health-checked on a schedule, and when testbook.com changes and a check fails, the API is automatically queued for repair and re-verified. It is built to keep working as the site underneath it changes.
This isn't an official testbook.com API — it's an independent, maintained REST wrapper over public data. Where the source has no official API (or only a limited one), Parse gives you a stable contract over a source that never promised one, and keeps it current. Need a new endpoint or field? You can revise it yourself in plain English and the agent rebuilds it against the live site in minutes — contributing the change back to the shared API is free.
Will this API break when the source site changes?+
Is this an official API from the source site?+
Can I fix or extend this API myself if I need a new endpoint or field?+
What happens if I call an endpoint that has an issue?+
- Build a government exam discovery tool that searches across SSC, Banking, and Railways content using the
searchendpoint - Populate a mobile app's exam category browser using titles, logos, and target counts from
list_exam_categories - Display a live class schedule widget filtered by exam super group using
get_upcoming_classeswith thesuper_group_idparameter - Track instructor-led free lessons and their upcoming schedules for a study planner app
- Build a Hindi-language exam prep interface by passing
language: Hindiacross all three endpoints - Aggregate test series availability across multiple exam categories using the
testSeriesfilter in thesearchendpoint
| Tier | Price | Credits/month | Rate limit |
|---|---|---|---|
| Free | $0/mo | 100 | 5 req/min |
| Hobby | $30/mo | 1,000 | 20 req/min |
| Developer | $100/mo | 5,000 | 100 req/min |
One credit = one API call regardless of which marketplace API you call. Exceeding the rate limit returns a 429 response. Authenticate with the X-API-Key header.
Does Testbook have an official public developer API?+
What does the `search` endpoint return beyond a list of results?+
results object, the response includes a count object that breaks down matches per content type (targets, courses, testSeries, tests, article), an order array reflecting how types are ranked for display, and a totalCount object with an aggregate figure and a relation field (e.g. "eq" or "gte") indicating whether the count is exact or a lower bound.Can I retrieve detailed course content, mock test questions, or individual lesson videos through this API?+
How does pagination work for upcoming classes?+
get_upcoming_classes endpoint accepts skip (number of results to skip) and limit (maximum results to return) as optional integer parameters. The response includes a count field reflecting how many lessons were returned in that page, which you can compare against limit to determine whether additional pages exist.