Async Python client for Indian court data — eCourts, HC Services, Supreme Court
Project description
bharat-courts
Async Python SDK for Indian court data — search cases, download orders, and access cause lists from eCourts and the Supreme Court.
What is this?
Access to court records is fundamental to legal transparency and the rule of law. India's eCourts platform holds millions of case records across 25+ High Courts and the Supreme Court — but programmatic access is difficult.
bharat-courts makes it easy. It provides a clean async Python interface to:
- Search cases by number, party name, or advocate across any High Court
- Download court orders and judgment PDFs
- Get daily cause lists — see which cases are being heard today, by bench
- Look up case types and bench info for all 25 High Courts
- Automate CAPTCHA handling with built-in OCR
Built for legal researchers, civic tech projects, legal aid organizations, journalists, and anyone working to make Indian judicial data more accessible. All responses are returned as typed Python dataclasses with JSON serialization.
Installation
pip install bharat-courts
# With automatic CAPTCHA solving (recommended)
pip install bharat-courts[ocr]
# With CLI
pip install bharat-courts[cli]
# Everything (OCR + CLI + dev tools)
pip install bharat-courts[all]
Requires Python 3.11+
Quick Start
Search cases by party name
import asyncio
from bharat_courts import get_court, HCServicesClient
from bharat_courts.captcha.ocr import OCRCaptchaSolver
async def main():
delhi = get_court("delhi")
solver = OCRCaptchaSolver()
async with HCServicesClient(captcha_solver=solver) as client:
cases = await client.case_status_by_party(
delhi,
party_name="state",
year="2024",
)
for case in cases[:5]:
print(f"{case.cnr_number}: {case.petitioner} v {case.respondent}")
print(f" Status: {case.status}")
asyncio.run(main())
DLHC010XXXXXXXXX: PETITIONER NAME v RESPONDENT NAME & ORS.
Status: Pending
DLHC010XXXXXXXXX: PETITIONER NAME v UNION OF INDIA
Status: Disposed
...
Search by case number
cases = await client.case_status(
delhi,
case_type="134", # W.P.(C) — use list_case_types() to discover codes
case_number="1",
year="2024",
)
Get today's cause list
pdfs = await client.cause_list(delhi, civil=True)
for pdf in pdfs:
print(f"{pdf.bench}")
print(f" PDF: {pdf.pdf_url}")
BENCH 1 (Division Bench)
PDF: https://hcservices.ecourts.gov.in/hcservices/cases_qry/cases/display_causelist_pdf.php?...
BENCH 2 (Single Bench)
PDF: https://hcservices.ecourts.gov.in/hcservices/cases_qry/cases/display_causelist_pdf.php?...
List benches and case types (no CAPTCHA needed)
benches = await client.list_benches(delhi)
# {'1': 'Principal Bench at Delhi'}
case_types = await client.list_case_types(delhi)
# {'134': 'W.P.(C)(CIVIL WRITS)-134', '27': 'W.P.(CRL)...', ...}
Court registry
from bharat_courts import get_court, list_high_courts
# Look up any court
delhi = get_court("delhi") # Delhi High Court (state_code="26")
bombay = get_court("bombay") # Bombay High Court (state_code="1")
sci = get_court("sci") # Supreme Court of India
# List all High Courts
for court in list_high_courts():
print(f"{court.code}: {court.name} (state_code={court.state_code})")
JSON serialization
All models support to_dict() and to_json():
case = cases[0]
print(case.to_json(indent=2))
{
"case_number": "3/2024",
"case_type": "3",
"cnr_number": "DLHC010XXXXXXXXX",
"petitioner": "PETITIONER NAME",
"respondent": "RESPONDENT NAME & ORS.",
"status": "Pending",
"court_name": "Delhi High Court"
}
Supported Portals
| Portal | Client | Status |
|---|---|---|
| HC Services | HCServicesClient |
Fully working |
| Judgment Search | JudgmentSearchClient |
Basic search |
| Supreme Court | SCIClient |
Basic search |
HC Services — Full API
| Method | CAPTCHA | Description |
|---|---|---|
list_benches() |
No | Available benches for a High Court |
list_case_types() |
No | Case type codes (W.P.(C) = 134, etc.) |
case_status() |
Yes | Search by case number |
case_status_by_party() |
Yes | Search by party name + year |
court_orders() |
Yes | Get orders for a case |
cause_list() |
Yes | Today's cause list PDFs by bench |
download_order_pdf() |
No | Download order/judgment PDF |
CAPTCHA Handling
HC Services uses Securimage CAPTCHAs that are pinned to the PHP session. The built-in OCR solver (ddddocr) has ~60% accuracy — failed attempts are automatically retried with fresh sessions (up to 3 attempts by default).
from bharat_courts.captcha.ocr import OCRCaptchaSolver
# Automatic OCR (requires pip install bharat-courts[ocr])
solver = OCRCaptchaSolver()
For higher accuracy, implement a custom solver:
from bharat_courts.captcha.base import CaptchaSolver
class MyCaptchaSolver(CaptchaSolver):
async def solve(self, image_bytes: bytes) -> str:
# Send to a CAPTCHA service, ML model, etc.
return "solved_text"
async with HCServicesClient(captcha_solver=MyCaptchaSolver()) as client:
...
Or use the manual solver for interactive use:
from bharat_courts.captcha.manual import ManualCaptchaSolver
# Opens the CAPTCHA image and prompts on stdin
solver = ManualCaptchaSolver()
CLI
# List all courts
bharat-courts courts
bharat-courts courts --type hc # High Courts only
# Search case status (requires CAPTCHA — uses ManualCaptchaSolver by default)
bharat-courts search delhi --case-type 134 --case-number 1 --year 2024
# Get cause list
bharat-courts cause-list delhi
bharat-courts cause-list delhi --date 01-03-2026
# Get court orders
bharat-courts orders delhi --case-type 134 --case-number 1 --year 2024
# Search judgments
bharat-courts judgments delhi --from-date 01-01-2024 --to-date 31-01-2024
# Supreme Court
bharat-courts sci --year 2024 --month 6
# Install AI agent skills (Claude Code, Copilot, etc.)
bharat-courts install-skills
Configuration
Environment variables with BHARAT_COURTS_ prefix:
| Variable | Default | Description |
|---|---|---|
BHARAT_COURTS_REQUEST_DELAY |
1.0 |
Seconds between requests |
BHARAT_COURTS_TIMEOUT |
30 |
Request timeout (seconds) |
BHARAT_COURTS_MAX_RETRIES |
3 |
Retry count on failure |
BHARAT_COURTS_LOG_LEVEL |
INFO |
Logging level |
Or use a .env file. See .env.example.
Supported Courts
All 25 High Courts with verified eCourts state codes:
| Court | Code | State Code |
|---|---|---|
| Allahabad HC | allahabad |
13 |
| Andhra Pradesh HC | andhra |
2 |
| Bombay HC | bombay |
1 |
| Calcutta HC | calcutta |
16 |
| Chhattisgarh HC | chhattisgarh |
18 |
| Delhi HC | delhi |
26 |
| Gauhati HC | gauhati |
6 |
| Gujarat HC | gujarat |
17 |
| Himachal Pradesh HC | himachal |
5 |
| J&K HC | jammu |
12 |
| Jharkhand HC | jharkhand |
7 |
| Karnataka HC | karnataka |
3 |
| Kerala HC | kerala |
4 |
| Madhya Pradesh HC | mp |
23 |
| Madras HC | madras |
10 |
| Manipur HC | manipur |
25 |
| Meghalaya HC | meghalaya |
21 |
| Orissa HC | orissa |
11 |
| Patna HC | patna |
8 |
| Punjab & Haryana HC | punjab |
22 |
| Rajasthan HC | rajasthan |
9 |
| Sikkim HC | sikkim |
24 |
| Telangana HC | telangana |
29 |
| Tripura HC | tripura |
20 |
| Uttarakhand HC | uttarakhand |
15 |
| Supreme Court | sci |
0 |
Bombay and Allahabad HCs also have bench-specific entries (e.g., bombay-nagpur, allahabad-lucknow).
Contributing
Contributions are welcome! Here's how to get set up.
Prerequisites
- Python 3.11+ — check with
python3 --version - git
Dev environment setup
# 1. Fork and clone
git clone https://github.com/<your-username>/bharat-courts.git
cd bharat-courts
# 2. Create a virtual environment
python3 -m venv .venv
source .venv/bin/activate # Linux/macOS
# .venv\Scripts\activate # Windows
# 3. Install with all extras (OCR, CLI, dev tools)
pip install -e ".[all]"
# 4. Verify everything works
pytest # 43 unit tests, no network needed
ruff check . && ruff format --check . # lint + format check
Running tests
# Unit tests (fast, offline)
pytest
# Single test file
pytest tests/test_hcservices_parser.py
# Single test
pytest tests/test_hcservices_parser.py::test_parse_case_status_json
# With verbose output
pytest -v
# Live integration tests against real eCourts portals (requires ddddocr + network)
python examples/live_test_all.py
Code style
The project uses ruff for linting and formatting:
# Check for issues
ruff check .
# Auto-fix what's possible
ruff check --fix .
# Format code
ruff format .
Config is in pyproject.toml — Python 3.11 target, 100-char line length, rules: E/F/I/N/W.
Project structure
src/bharat_courts/
├── __init__.py # Public API exports
├── models.py # Dataclasses: CaseInfo, CaseOrder, CauseListPDF, etc.
├── config.py # Pydantic Settings (BHARAT_COURTS_ env prefix)
├── http.py # Rate-limited async HTTP client (httpx)
├── courts.py # Registry of 25+ HCs with eCourts codes
├── captcha/
│ ├── base.py # CaptchaSolver ABC
│ ├── manual.py # Stdin/callback solver
│ └── ocr.py # ddddocr-based solver
├── hcservices/ # HC Services portal (primary, fully working)
│ ├── client.py # HCServicesClient
│ ├── endpoints.py # URL + form builders
│ └── parser.py # JSON + HTML response parsers
├── judgments/ # Judgment Search portal (basic)
│ ├── client.py
│ ├── endpoints.py
│ └── parser.py
├── sci/ # Supreme Court (basic)
│ ├── client.py
│ └── parser.py
└── cli.py # Click CLI entry point
Areas where help is needed
- Better CAPTCHA solving — the ddddocr OCR is ~60% accurate; a fine-tuned model or alternative approach would help
- District court support — eCourts has district court portals with a different API
- Judgment Search portal — the
JudgmentSearchClientneeds more thorough testing and pagination support - Supreme Court client —
SCIClientis basic; the SCI website structure changes frequently - More High Court coverage — test the client against courts beyond Delhi/Bombay/Allahabad
- Documentation — API docs, more examples, tutorials
Submitting changes
- Fork the repo and create a branch (
git checkout -b my-feature) - Make your changes
- Run
pytestandruff check .to ensure tests pass and code is clean - Commit with a descriptive message
- Open a pull request
How it works
The eCourts HC Services portal (hcservices.ecourts.gov.in) uses a PHP backend with:
- Session cookies —
GET main.phpestablishesHCSERVICES_SESSID - Securimage CAPTCHAs — pinned to the session (same image within one session)
- AJAX POST requests —
cases_qry/index_qry.phpwithaction_codeparameter - JSON responses —
{"con": ["[{...}]"], "totRecords": N, "Error": ""}
This library handles all of this transparently — session management, CAPTCHA solving with retry, request/response parsing, and rate limiting.
License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bharat_courts-0.1.1.tar.gz.
File metadata
- Download URL: bharat_courts-0.1.1.tar.gz
- Upload date:
- Size: 37.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dadaefb35cd96dbcc426f75fef32664da7f30c47842ce335500792bb272ac1d9
|
|
| MD5 |
b536e78ff89d4fd1e40d5a2faf086f77
|
|
| BLAKE2b-256 |
c893192a3123e078ba7ee4c9074e2610f6c1271aa3cf6c63772c5b01c8a483e3
|
Provenance
The following attestation bundles were made for bharat_courts-0.1.1.tar.gz:
Publisher:
python-publish.yml on iamshouvikmitra/bharat-courts
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bharat_courts-0.1.1.tar.gz -
Subject digest:
dadaefb35cd96dbcc426f75fef32664da7f30c47842ce335500792bb272ac1d9 - Sigstore transparency entry: 1004916887
- Sigstore integration time:
-
Permalink:
iamshouvikmitra/bharat-courts@95eef5005562ee013a3f79b46cf6b0a43a261fa6 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/iamshouvikmitra
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@95eef5005562ee013a3f79b46cf6b0a43a261fa6 -
Trigger Event:
release
-
Statement type:
File details
Details for the file bharat_courts-0.1.1-py3-none-any.whl.
File metadata
- Download URL: bharat_courts-0.1.1-py3-none-any.whl
- Upload date:
- Size: 36.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
92aa5b6c7b8c9369d0be8f98d0e5a67c19b1f90af6fd0fd3d7109c65355892ff
|
|
| MD5 |
b98a30867caf52a351e76b99bf8f0591
|
|
| BLAKE2b-256 |
cff1928e7f6f6bc558d44068c02c51a46e9db49dd40085415b7db15c074ce4d1
|
Provenance
The following attestation bundles were made for bharat_courts-0.1.1-py3-none-any.whl:
Publisher:
python-publish.yml on iamshouvikmitra/bharat-courts
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bharat_courts-0.1.1-py3-none-any.whl -
Subject digest:
92aa5b6c7b8c9369d0be8f98d0e5a67c19b1f90af6fd0fd3d7109c65355892ff - Sigstore transparency entry: 1004916894
- Sigstore integration time:
-
Permalink:
iamshouvikmitra/bharat-courts@95eef5005562ee013a3f79b46cf6b0a43a261fa6 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/iamshouvikmitra
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@95eef5005562ee013a3f79b46cf6b0a43a261fa6 -
Trigger Event:
release
-
Statement type: