Live clients¶
Portal scrapers for the official eCourts and court websites. All are async context managers and accept a pluggable CaptchaSolver.
HCServicesClient¶
HCServicesClient ¶
HCServicesClient(
config: BharatCourtsConfig | None = None,
captcha_solver: CaptchaSolver | None = None,
http_client: RateLimitedClient | None = None,
)
Async client for HC Services (hcservices.ecourts.gov.in).
Usage::
async with HCServicesClient() as client:
cases = await client.case_status(
court=get_court("delhi"),
case_type="WP(C)",
case_number="12345",
year="2024",
)
Source code in src/bharat_courts/hcservices/client.py
case_status async ¶
case_status(
court: Court,
*,
case_type: str,
case_number: str,
year: str,
bench_code: str = "1"
) -> list[CaseInfo]
Look up case status by case number.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
court | Court | Court object (use get_court() to obtain). | required |
case_type | str | Numeric case type code (e.g. "134" for W.P.(C) in Delhi). Use :meth: | required |
case_number | str | Case number without type/year. | required |
year | str | Registration year (e.g. "2024"). | required |
bench_code | str | Bench code from :meth: | '1' |
Returns:
| Type | Description |
|---|---|
list[CaseInfo] | List of matching CaseInfo objects. |
Source code in src/bharat_courts/hcservices/client.py
case_status_by_party async ¶
case_status_by_party(
court: Court,
*,
party_name: str,
year: str,
bench_code: str = "1",
status_filter: str = "Both"
) -> list[CaseInfo]
Search cases by party name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
court | Court | Court object. | required |
party_name | str | Petitioner or respondent name (min 3 chars). | required |
year | str | Registration year (mandatory, e.g. "2024"). | required |
bench_code | str | Bench code from :meth: | '1' |
status_filter | str | "Pending", "Disposed", or "Both". | 'Both' |
Returns:
| Type | Description |
|---|---|
list[CaseInfo] | List of matching CaseInfo objects. |
Source code in src/bharat_courts/hcservices/client.py
court_orders async ¶
court_orders(
court: Court,
*,
case_type: str,
case_number: str,
year: str,
bench_code: str = "1"
) -> list[CaseOrder]
Get court orders for a case.
Uses a case number search to get the encrypted order URL path, then constructs the PDF download URL from display_pdf.php.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
court | Court | Court object. | required |
case_type | str | Numeric case type code (e.g. "134"). | required |
case_number | str | Case number. | required |
year | str | Registration year. | required |
bench_code | str | Bench code from :meth: | '1' |
Returns:
| Type | Description |
|---|---|
list[CaseOrder] | List of CaseOrder objects with PDF URLs. |
Source code in src/bharat_courts/hcservices/client.py
cause_list async ¶
cause_list(
court: Court,
*,
civil: bool = True,
bench_code: str = "1",
causelist_date: str = ""
) -> list[CauseListPDF]
Get cause list PDFs for a court.
The HC Services portal returns a table of PDF links, one per bench/judge. Each entry contains the bench name, cause list type, and PDF URL.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
court | Court | Court object. | required |
civil | bool | True for civil cases, False for criminal. | True |
bench_code | str | Bench code from list_benches() (default "1" = principal). | '1' |
causelist_date | str | Date in DD-MM-YYYY format (defaults to today). | '' |
Returns:
| Type | Description |
|---|---|
list[CauseListPDF] | List of CauseListPDF objects with bench info and PDF URLs. |
Source code in src/bharat_courts/hcservices/client.py
list_benches async ¶
list_benches(court: Court) -> dict[str, str]
Get available benches for a High Court.
Returns:
| Type | Description |
|---|---|
dict[str, str] | Dict mapping bench code to bench name, e.g. |
dict[str, str] | {"1": "Principal Bench at Delhi", "2": "Lucknow Bench"}. |
Source code in src/bharat_courts/hcservices/client.py
list_case_types async ¶
list_case_types(
court: Court, *, bench_code: str = "1"
) -> dict[str, str]
Get available case types for a High Court bench.
Returns:
| Type | Description |
|---|---|
dict[str, str] | Dict mapping case type code to name, e.g. |
dict[str, str] | {"134": "W.P.(C)(CIVIL WRITS)-134", "27": "W.P.(CRL)..."}. |
Source code in src/bharat_courts/hcservices/client.py
download_order_pdf async ¶
Download an order/judgment PDF.
The display_pdf.php endpoint requires a valid Referer header and session cookies from the same client that performed the search.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pdf_url | str | URL from CaseOrder.pdf_url. | required |
Returns:
| Type | Description |
|---|---|
bytes | Raw PDF bytes. |
Source code in src/bharat_courts/hcservices/client.py
DistrictCourtClient¶
DistrictCourtClient ¶
DistrictCourtClient(
config: BharatCourtsConfig | None = None,
captcha_solver: CaptchaSolver | None = None,
http_client: RateLimitedClient | None = None,
)
Async client for District Courts (services.ecourts.gov.in).
Usage::
async with DistrictCourtClient() as client:
districts = await client.list_districts("8") # Bihar
complexes = await client.list_complexes("8", "1") # Patna
cases = await client.case_status(
state_code="8", dist_code="1",
court_complex_code="1080010", est_code="2",
case_type="1", case_number="1", year="2024",
)
Source code in src/bharat_courts/districtcourts/client.py
list_states async ¶
Get available states/UTs.
Returns:
| Type | Description |
|---|---|
dict[str, str] | Dict mapping state code to state name. |
Source code in src/bharat_courts/districtcourts/client.py
list_districts async ¶
Get districts for a state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state_code | str | State code (e.g. "8" for Bihar). | required |
Returns:
| Type | Description |
|---|---|
dict[str, str] | Dict mapping district code to district name. |
Source code in src/bharat_courts/districtcourts/client.py
list_complexes async ¶
Get court complexes for a district.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state_code | str | State code. | required |
dist_code | str | District code. | required |
Returns:
| Name | Type | Description |
|---|---|---|
dict[str, str] | Dict mapping complex value ( | |
Use | dict[str, str] | func: |
dict[str, str] | and determine if establishment selection is needed. |
Source code in src/bharat_courts/districtcourts/client.py
list_establishments async ¶
Get establishments for a court complex.
Only needed when the complex flag is 'Y'.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state_code | str | State code. | required |
dist_code | str | District code. | required |
court_complex_code | str | Raw complex code (without @ests@flag). | required |
Returns:
| Type | Description |
|---|---|
dict[str, str] | Dict mapping establishment code to name. |
Source code in src/bharat_courts/districtcourts/client.py
list_cause_list_courts async ¶
list_cause_list_courts(
state_code: str,
dist_code: str,
court_complex_code: str,
est_code: str = "",
) -> dict[str, str]
Get the courts dropdown for cause-list lookup.
The cause-list form requires both court_no (the option's value, e.g. "1@2") and court_name (the option's display text, e.g. "District & Sessions Judge - DJ Div. Patna Sadar"). Use this method to discover them; pass either the code through directly to :meth:cause_list, which will look up the matching name automatically.
Source code in src/bharat_courts/districtcourts/client.py
list_case_types async ¶
list_case_types(
state_code: str,
dist_code: str,
court_complex_code: str,
est_code: str = "",
) -> dict[str, str]
Get available case types for a court.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state_code | str | State code. | required |
dist_code | str | District code. | required |
court_complex_code | str | Court complex code. | required |
est_code | str | Establishment code (if needed). | '' |
Returns:
| Type | Description |
|---|---|
dict[str, str] | Dict mapping case type code to name. Codes are returned in the |
dict[str, str] | portal's compound |
dict[str, str] |
|
dict[str, str] | string back as |
dict[str, str] | meth: |
Source code in src/bharat_courts/districtcourts/client.py
case_status async ¶
case_status(
*,
state_code: str,
dist_code: str,
court_complex_code: str,
est_code: str = "",
case_type: str,
case_number: str,
year: str
) -> list[CaseInfo]
Look up case status by case number.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state_code | str | State code (e.g. "8" for Bihar). | required |
dist_code | str | District code (e.g. "1" for Patna). | required |
court_complex_code | str | Court complex code (e.g. "1080010"). | required |
est_code | str | Establishment code (if needed). | '' |
case_type | str | Case type code from :meth: | required |
case_number | str | Case number. | required |
year | str | Registration year. | required |
Returns:
| Type | Description |
|---|---|
list[CaseInfo] | List of matching CaseInfo objects. |
Source code in src/bharat_courts/districtcourts/client.py
case_status_by_party async ¶
case_status_by_party(
*,
state_code: str,
dist_code: str,
court_complex_code: str,
est_code: str = "",
party_name: str,
year: str,
status_filter: str = "Both"
) -> list[CaseInfo]
Search cases by party name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state_code | str | State code. | required |
dist_code | str | District code. | required |
court_complex_code | str | Court complex code. | required |
est_code | str | Establishment code (if needed). | '' |
party_name | str | Petitioner/respondent name (min 3 chars). | required |
year | str | Registration year (mandatory). | required |
status_filter | str | "Pending", "Disposed", or "Both". | 'Both' |
Returns:
| Type | Description |
|---|---|
list[CaseInfo] | List of matching CaseInfo objects. |
Source code in src/bharat_courts/districtcourts/client.py
court_orders async ¶
court_orders(
*,
state_code: str,
dist_code: str,
court_complex_code: str,
est_code: str = "",
case_type: str,
case_number: str,
year: str
) -> list[CaseOrder]
Get court orders for a case.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state_code | str | State code. | required |
dist_code | str | District code. | required |
court_complex_code | str | Court complex code. | required |
est_code | str | Establishment code (if needed). | '' |
case_type | str | Case type code. | required |
case_number | str | Case number. | required |
year | str | Registration year. | required |
Returns:
| Type | Description |
|---|---|
list[CaseOrder] | List of CaseOrder objects. |
Source code in src/bharat_courts/districtcourts/client.py
cause_list async ¶
cause_list(
*,
state_code: str,
dist_code: str,
court_complex_code: str,
est_code: str = "",
court_no: str,
court_name: str = "",
causelist_date: str = "",
civil: bool = True
) -> list[CauseListEntry]
Get cause list for a court.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state_code | str | State code. | required |
dist_code | str | District code. | required |
court_complex_code | str | Court complex code. | required |
est_code | str | Establishment code (if needed). | '' |
court_no | str | Court code from :meth: | required |
court_name | str | Court display name (the option's text). The portal validates against this — sending an empty | '' |
causelist_date | str | Date in DD-MM-YYYY format (defaults to today). | '' |
civil | bool | True for civil, False for criminal. | True |
Returns:
| Type | Description |
|---|---|
list[CauseListEntry] | List of CauseListEntry objects. |
Source code in src/bharat_courts/districtcourts/client.py
JudgmentSearchClient¶
JudgmentSearchClient ¶
JudgmentSearchClient(
config: BharatCourtsConfig | None = None,
captcha_solver: CaptchaSolver | None = None,
http_client: RateLimitedClient | None = None,
)
Async client for the Judgment Search portal (judgments.ecourts.gov.in).
Usage::
async with JudgmentSearchClient() as client:
sr = await client.search("section 498A")
print(sr.total_count, len(sr.items))
for j in sr.items:
print(j.case_number, j.court_name, j.judgment_date)
pdf = await client.download_pdf(j) # populates j.pdf_bytes
Source code in src/bharat_courts/judgments/client.py
search async ¶
search(
search_text: str,
*,
page: int = 1,
page_size: int = 10,
search_opt: str = "PHRASE",
court_type: str = "2",
max_captcha_attempts: int = 5
) -> SearchResult
Search for judgments by keyword.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
search_text | str | Keywords / phrase to search for. | required |
page | int | 1-indexed page number. | 1 |
page_size | int | Rows per page (portal supports 10/25/50/100/1000). | 10 |
search_opt | str |
| 'PHRASE' |
court_type | str |
| '2' |
max_captcha_attempts | int | Max CAPTCHA solve retries before giving up. | 5 |
Returns:
| Type | Description |
|---|---|
SearchResult |
|
Raises:
| Type | Description |
|---|---|
CaptchaError | if the CAPTCHA solver couldn't produce a valid solution within |
Source code in src/bharat_courts/judgments/client.py
search_all async ¶
search_all(
search_text: str,
*,
page_size: int = 25,
search_opt: str = "PHRASE",
court_type: str = "2",
max_captcha_attempts: int = 5
) -> AsyncIterator[SearchResult]
Iterate through every page of results, yielding one SearchResult per page. Re-authenticates if the session token expires mid-walk.
Source code in src/bharat_courts/judgments/client.py
download_pdf async ¶
download_pdf(
judgment: JudgmentResult, *, court_type: str = "2"
) -> JudgmentResult
Download the PDF for a judgment result.
Mutates judgment in-place: sets pdf_bytes if the download succeeds. The judgment.pdf_url slot stores the row's relative path (from open_pdf(...)), not a directly-fetchable URL — we resolve it through the portal's openpdfcaptcha endpoint before downloading.
Raises:
| Type | Description |
|---|---|
RuntimeError | if the download didn't return PDF bytes. |
Source code in src/bharat_courts/judgments/client.py
download_pdfs async ¶
download_pdfs(
judgments: list[JudgmentResult],
*,
court_type: str = "2",
stop_on_error: bool = False
) -> list[JudgmentResult]
Download PDFs for multiple judgments. Skips ones that already have pdf_bytes set. Errors are logged unless stop_on_error.
Source code in src/bharat_courts/judgments/client.py
CalcuttaHCClient¶
CalcuttaHCClient ¶
CalcuttaHCClient(
config: BharatCourtsConfig | None = None,
captcha_solver: CaptchaSolver | None = None,
http_client: RateLimitedClient | None = None,
)
Async client for Calcutta High Court (calcuttahighcourt.gov.in).
Provides order/judgment search and PDF download for cases from September 2020 onwards (CIS system).
Usage::
async with CalcuttaHCClient() as client:
case_info, orders = await client.search_orders(
case_type="12", case_number="12886", year="2024",
)
if case_info:
print(case_info.case_number, case_info.petitioner, "vs", case_info.respondent)
for order in orders:
print(order.order_date, order.judge, order.neutral_citation)
if order.pdf_url:
pdf = await client.download_order_pdf(order.pdf_url)
Source code in src/bharat_courts/calcuttahc/client.py
search_orders async ¶
search_orders(
*,
case_type: str,
case_number: str,
year: str,
establishment: str = "appellate",
max_captcha_attempts: int = 5
) -> tuple[CaseInfo | None, list[CaseOrder]]
Search for orders/judgments by case number.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
case_type | str | Numeric case type code (e.g. "12" for WPA). | required |
case_number | str | Case registration number (e.g. "12886"). | required |
year | str | Case year (e.g. "2024"). | required |
establishment | str | Bench name — "appellate", "original", "jalpaiguri", or "portblair". | 'appellate' |
max_captcha_attempts | int | Max CAPTCHA solve retries. Default 5 (with OCR ~75% accuracy this gives ~0.1% all-fail rate; each retry opens a fresh session, ~3-4s overhead). | 5 |
Returns:
| Type | Description |
|---|---|
CaseInfo | None | Tuple of |
list[CaseOrder] |
|
tuple[CaseInfo | None, list[CaseOrder]] | full case number); the list carries per-order rows. If no |
tuple[CaseInfo | None, list[CaseOrder]] | case matched and no metadata could be recovered, returns |
tuple[CaseInfo | None, list[CaseOrder]] |
|
Source code in src/bharat_courts/calcuttahc/client.py
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | |
download_order_pdf async ¶
Download an order/judgment PDF.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pdf_url | str | URL from CaseOrder.pdf_url. | required |
Returns:
| Type | Description |
|---|---|
bytes | Raw PDF bytes. |
Raises:
| Type | Description |
|---|---|
RuntimeError | if the response does not start with the |
Source code in src/bharat_courts/calcuttahc/client.py
SCIClient¶
SCIClient ¶
SCIClient(
config: BharatCourtsConfig | None = None,
http_client: RateLimitedClient | None = None,
)
Async client for the Supreme Court of India (www.sci.gov.in).
Usage::
async with SCIClient() as client:
recent = await client.list_recent_judgments()
for j in recent[:3]:
print(j.judgment_date, j.case_number, j.title)
pdf = await client.download_pdf(j) # populates j.pdf_bytes
Source code in src/bharat_courts/sci/client.py
list_recent_judgments async ¶
list_recent_judgments(
*, limit: int = 50
) -> list[JudgmentResult]
Return the homepage's "Latest Judgements / Orders" feed.
The portal surfaces the 50 most recent items inline on the homepage; this method scrapes that list. No CAPTCHA needed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
limit | int | Maximum number of items to return (the homepage caps this at 50). Pass less to truncate. | 50 |
Source code in src/bharat_courts/sci/client.py
download_pdf async ¶
download_pdf(judgment: JudgmentResult) -> JudgmentResult
Download the PDF bytes for a judgment.
Mutates judgment in-place: sets pdf_bytes on success. judgment.pdf_url is the /sci-get-pdf/?diary_no=... URL the portal viewer iframe uses.
Raises:
| Type | Description |
|---|---|
RuntimeError | if the response isn't a PDF. |
Source code in src/bharat_courts/sci/client.py
search_by_year async ¶
search_by_year(
year: int, month: int | None = None
) -> list[JudgmentResult]
Date-range search by year/month.
Not implemented. The legacy host (main.sci.gov.in) that served this form has been permanently 503 for years; the live site (www.sci.gov.in) only exposes an equivalent through a CAPTCHA-protected case-number/diary-number form, which this client does not yet wire up. Use :meth:list_recent_judgments for the most recent items.
Source code in src/bharat_courts/sci/client.py
search_by_party async ¶
search_by_party(party_name: str) -> list[JudgmentResult]
Party-name search.
Not implemented. Same situation as :meth:search_by_year.