Civil Service India APIcivilserviceindia.com ↗
Retrieve UPSC Civil Services Mains previous-year question paper PDF links by optional subject from civilserviceindia.com via a single API endpoint.
What is the Civil Service India API?
The Civil Service India API exposes one endpoint, list_question_papers, that returns every previous-year UPSC Civil Services Mains question paper listed for a given optional subject on civilserviceindia.com. Each response includes up to 8 structured fields per paper — year, paper number (I or II), label, href, and absolute PDF URL — along with page-level metadata such as the page title, all available years, and years the site does not publish.
curl -X GET 'https://api.parse.bot/scraper/f8af046c-89e0-4229-9911-450a6e869266/list_question_papers?subject=Agriculture' \ -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 civilserviceindia-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.
"""Walkthrough: fetch UPSC question papers for a subject and browse by year."""
from parse_apis.civilserviceindia_com_api import CivilServiceIndia, SubjectNotFound
client = CivilServiceIndia()
# Fetch all Agriculture papers and inspect what's available.
try:
agriculture = client.subjects.get(subject="Agriculture")
except SubjectNotFound:
print("Subject not found on the site")
raise
print(agriculture.page_title, f"— {agriculture.count} papers total")
print("Years on site:", agriculture.years_available[:5], "…")
print("Gaps (unlisted years):", agriculture.years_not_listed)
# Browse individual paper rows.
for paper in agriculture.papers or []:
print(paper.year, paper.label, paper.pdf_url)
# Narrow to a specific year range using year_from / year_to.
recent = client.subjects.get(subject="Agriculture", year_from="2020", year_to="2025")
print(f"\n2020-2025: {recent.count} papers")
for paper in recent.papers or []:
suffix = f" (Paper {paper.paper})" if paper.paper else ""
print(f" {paper.year}{suffix}: {paper.pdf_url}")
print("\nexercised: subjects.get / Paper fields / year filtering / SubjectNotFound")
Returns every previous-year question paper listed on the subject's question-papers page, one row per paper (year, paper number I/II where the label carries one, the site's label, the exact href as written in the page, and the absolute pdf_url). The whole list comes from a single page load, so one call returns all papers; there is no pagination. year_from / year_to optionally narrow the rows to an inclusive year range (filtered locally); years_available and years_not_listed always describe the full list on the page regardless of the filter, so callers can see which years the site does not publish (for Agriculture the site lists 2004, 2005 and 2009-2025 only). An unknown subject slug returns a not-found error.
| Param | Type | Description |
|---|---|---|
| subject | string | Subject slug as it appears in the site's subject URL path (letters, digits, hyphens), e.g. Agriculture. |
| year_to | string | 4-digit year; only papers up to and including this year are returned. Omitted = no upper bound. |
| year_from | string | 4-digit year; only papers from this year onward are returned. Omitted = no lower bound. |
{
"type": "object",
"fields": {
"count": "number of rows in papers after year filtering",
"papers": "array of paper rows: year (integer), paper (\"I\"/\"II\" or null when the label has no paper number), label, href (exact anchor href as written on the page), pdf_url (absolute URL), format (link type shown by the site, e.g. PDF)",
"subject": "subject slug that was fetched",
"page_title": "H1 heading of the subject page",
"years_available": "sorted integer years present in the full list on the page",
"years_not_listed": "integer years between the earliest and latest listed year that the site does not publish"
},
"sample": {
"data": {
"count": 36,
"papers": [
{
"href": "agriculture-2025-paper-I.pdf",
"year": 2025,
"label": "Question Paper of 2025 [Paper-I]",
"paper": "I",
"format": "PDF",
"pdf_url": "https://www.civilserviceindia.com/subject/Agriculture/agriculture-2025-paper-I.pdf"
},
{
"href": "../../upsc_questions/CSE_AGRICULTURE_MAIN_2005.pdf",
"year": 2005,
"label": "Question Paper of 2005",
"paper": null,
"format": "PDF",
"pdf_url": "https://www.civilserviceindia.com/upsc_questions/CSE_AGRICULTURE_MAIN_2005.pdf"
}
],
"subject": "Agriculture",
"page_title": "Agriculture Question Papers",
"years_available": [
2004,
2005,
2009,
2010,
2011,
2012,
2013,
2014,
2015,
2016,
2017,
2018,
2019,
2020,
2021,
2022,
2023,
2024,
2025
],
"years_not_listed": [
2006,
2007,
2008
]
},
"status": "success"
}
}About the Civil Service India API
What the API Returns
The single list_question_papers endpoint fetches the full question-paper listing for one optional subject on civilserviceindia.com and returns a structured JSON payload. Each entry in the papers array includes the year as an integer, a paper field set to "I", "II", or null (when the source label carries no paper number), the raw label text as it appears on the page, the href exactly as written in the anchor, and a resolved pdf_url with the full absolute path to the PDF.
Filtering and Metadata
The subject input accepts any slug that appears in the site's subject URL path, such as Agriculture, History, or Geography. Two optional date parameters, year_from and year_to, let you narrow results to a specific range — only rows whose year falls within those bounds are included in the returned papers array, and the count field reflects the filtered total. The response also carries years_available (all years present in the full listing) and years_not_listed (integer years between the earliest and latest listed year for which the site publishes no paper), making it straightforward to detect coverage gaps for a given subject.
Scope and Format
The endpoint covers optional subjects as organised on civilserviceindia.com's subject question-paper pages. All papers are returned in a single response with no pagination. The page_title field echoes the H1 heading of the fetched subject page, which can be used to confirm the subject matched correctly.
The Civil Service India API is a managed, monitored endpoint for civilserviceindia.com — not a raw scraper you maintain. Every endpoint is automatically health-checked on a schedule, and when civilserviceindia.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 civilserviceindia.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 UPSC preparation tool that lets students download question papers by subject and year range using
year_fromandyear_to - Audit coverage gaps in a specific optional subject by inspecting the
years_not_listedfield - Generate a subject-wise index of all available UPSC Mains PDFs with their absolute
pdf_urlvalues - Track which paper numbers (I or II) are available for each year in a given optional subject
- Populate a study-resource database with structured metadata including
year,label, andpdf_urlfor each paper - Compare question-paper availability across multiple optional subjects by calling the endpoint once per subject slug
| Tier | Price | Credits/month | Rate limit |
|---|---|---|---|
| Free | $0/mo | 200 | 5 req/min |
| Hobby | $30/mo | 1,000 | 20 req/min |
| Developer | $100/mo | 5,000 | 100 req/min |
| Team | $300/mo | 20,000 | 300 req/min |
| Company | $1,000/mo | 100,000 | 500 req/min |
Each endpoint has a fixed posted price per successful call — most fall between 1 and 10 credits — shown on this API's page before you run it. Exceeding the rate limit returns a 429 response. Authenticate with the X-API-Key header.
Does civilserviceindia.com have an official developer API?+
What does the `paper` field return, and when is it null?+
paper field returns "I" or "II" when the source label explicitly identifies a paper number. It returns null when the listing label carries no paper number — some subjects list a single undivided paper per year.