# Court Records API - Case Law & Federal Docket Search Scraper (`captainhandsome/courtlistener-case-search`) Actor

Search US judicial opinions and federal RECAP/PACER dockets through the CourtListener API by keyword, court and filing date. A case law scraper that filters dockets by nature of suit - patent, trademark, antitrust, securities - and exports parties, judge, attorneys and firms. 55 columns per record.

- **URL**: https://apify.com/captainhandsome/courtlistener-case-search.md
- **Developed by:** [Joseph McRell](https://apify.com/captainhandsome) (community)
- **Categories:** Business, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.10 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Court Case and Docket Search (CourtListener / RECAP)

Search more than 8 million published US judicial opinions, and the federal court dockets contributed to RECAP, and get back 55 flat, spreadsheet-ready columns: case name, court, judge, filing and termination dates, nature of suit, parties, attorneys and law firms, reporter and LEXIS citations, the docket's recent filings, a preview of the document text, and free direct links to the opinion and filing PDFs. It reads the Free Law Project's official CourtListener API, so there is no API key to obtain, no login, and no scraping. Set `max_items` before each run to keep both the output and the bill predictable.

**Read this before you buy.** This Actor is built for business and corporate litigation research. The Free Law Project's terms prohibit using CourtListener data for FCRA purposes: you may not use it as a factor in deciding anyone's eligibility for employment, credit, insurance, housing or government benefits, and you may not build a consumer report or a background-screening product from it. That restriction travels with the data and applies to you as a user of this Actor. If people-search or background screening is what you need, this is the wrong tool.

### What data can I extract?

Two corpora, one row shape:

- **Judicial opinions** - over 8.3 million published decisions from federal and state courts, with reporter, LEXIS and neutral citations, how often the opinion has been cited and how many decisions it cites itself, precedential status, the full case caption, the court's own syllabus and procedural posture where it publishes them, how many separate opinions were filed and of what kind, whether any was per curiam, the Supreme Court Database ID on Supreme Court cases, and PDF links to both the court's own copy and CourtListener's.
- **Federal dockets (RECAP)** - case metadata contributed from PACER, with nature of suit, statutory cause, jurisdiction type, jury demand, parties of record, counsel and their firms, the assigned and referred-to judges, bankruptcy chapter and trustee, and the docket filings that matched your search: entry numbers, docket text, the newest entry's date, page count and a free direct link to its PDF in RECAP.
- **Shared across both** - case name, court name, court ID and citation abbreviation, the court's jurisdiction level, homepage and PACER court ID, docket number, judge, filing, argument and termination dates, a preview of the actual document text, and a direct link to the case on CourtListener.

The two corpora do not share a vocabulary upstream. An opinion has a `judge`, a `citation` array and a `caseNameFull`; a docket has an `assignedTo`, a `cause` and a `case_name_full`. Both are normalised onto one row here, and a field that genuinely exists on only one side comes back null on the other rather than being filled with a guess. Every result is written to the default dataset and can be downloaded as JSON, CSV, Excel or XML.

### Input example

```json
{
  "search_type": "dockets",
  "query": "infringement",
  "nature_of_suit": "830",
  "filed_after": "2020-01-01",
  "max_items": 100
}
```

`search_type` chooses the corpus: `opinions` or `dockets`. `query` is free text across case name and full text. `court` takes a CourtListener court ID such as `cafc`, `txwd` or `ca9`, and the `court_id` you get back on every row is drawn from the same vocabulary, so a result can be fed straight into a narrower follow-up run. `nature_of_suit` takes a federal civil cover-sheet code - 110 Insurance, 190 Contract, 410 Antitrust, 430 Banks, 470 RICO, 820 Copyright, 830 Patent, 840 Trademark, 850 Securities - and applies to dockets only. `filed_after` and `filed_before` are `YYYY-MM-DD`. `max_items` is a hard output ceiling. `include_court_details` is on by default and adds the court's jurisdiction level, homepage and PACER court ID; the lookup is cached per court, so it costs one extra request per distinct court in a run, not one per row.

### Output example

A docket row, as returned by a run of the input above:

```json
{
  "result_type": "dockets",
  "case_name": "VirtaMove, Corp. v. Oracle Corporation",
  "case_name_full": null,
  "court": "District Court, W.D. Texas",
  "court_id": "txwd",
  "court_citation": "W.D. Tex.",
  "court_jurisdiction": "Federal District",
  "court_website": "http://www.txwd.uscourts.gov/",
  "court_pacer_id": 181,
  "docket_number": "7:24-cv-00339",
  "docket_id": 69489785,
  "cluster_id": null,
  "date_filed": "2024-12-20",
  "date_argued": null,
  "date_terminated": "2025-10-02",
  "date_reargument_denied": null,
  "judge": "Alan D. Albright",
  "referred_to_judge": null,
  "panel": null,
  "per_curiam": null,
  "suit_nature": "830 Patent",
  "cause": "35:100 Patent Infringement",
  "jurisdiction_type": "Federal Question",
  "jury_demand": "Plaintiff",
  "bankruptcy_chapter": null,
  "bankruptcy_trustee": null,
  "citation": null,
  "lexis_citation": null,
  "neutral_citation": null,
  "scdb_id": null,
  "cite_count": null,
  "cited_opinion_count": null,
  "precedential_status": null,
  "posture": null,
  "syllabus": null,
  "opinion_count": null,
  "opinion_types": null,
  "opinion_download_url": null,
  "opinion_pdf_url": null,
  "attorneys": "Qi (Peter) Tong, Daniel B Kolko, Reza Mirzaie, Christian W. Conkle, Neil A. Rubin, Marc A. Fenster, Amy Hayden, James Milkey",
  "firms": "Russ August & Kabat",
  "parties": "Oracle Corporation, VirtaMove, Corp.",
  "matched_filings": 3,
  "more_filings_available": true,
  "filing_numbers": "12, 1, 9",
  "filing_descriptions": "NOTICE of Dismissal of Indirect Infringement Claims by Oracle Corporation (Adams, Darryl) | COMPLAINT FOR PATENT INFRINGEMENT ( Filing fee $ 405 receipt number ATXWDC-19609043), filed by VirtaMove, Corp.. (Attachments: # 1 Exhibit 1, # 2 Exhibit 2, # 3 Exhibit 3, # 4 Exhibit 4, # 5 Civil Cov ... | SUMMONS Returned Executed by VirtaMove, Corp.. Oracle Corporation served on 1/7/2025, answer due 1/28/2025. (Hayden, Amy) (Entered: 01/14/2025)",
  "latest_filing_date": "2025-03-11",
  "latest_filing_number": 12,
  "latest_filing_description": "NOTICE of Dismissal of Indirect Infringement Claims by Oracle Corporation (Adams, Darryl)",
  "latest_filing_page_count": 3,
  "latest_filing_url": "https://www.courtlistener.com/docket/69489785/12/virtamove-corp-v-oracle-corporation/",
  "latest_filing_pdf_url": "https://storage.courtlistener.com/recap/gov.uscourts.txwd.1172818303/gov.uscourts.txwd.1172818303.12.0.pdf",
  "text_snippet": "Case 7:24-cv-00339-ADA Document 12 Filed 03/11/25 Page 1 of 3 IN THE UNITED STATES DISTRICT COURT FOR THE WESTERN DISTRICT OF TEXAS MIDLAND/ODESSA DIVISION VIRTAMOVE, CORP., Plaintiff, Case No. 7:24-cv-00339-ADA v. JURY TRIAL DEMANDED ORACLE CORP.",
  "pacer_case_id": "1172818303",
  "url": "https://www.courtlistener.com/docket/69489785/virtamove-corp-v-oracle-corporation/"
}
```

An opinion row, from a run with `"search_type": "opinions"` and `"court": "cafc"`. The populated and null fields are close to the mirror image of the docket row above:

```json
{
  "result_type": "opinions",
  "case_name": "Biomedical Patent Management Corp. v. California",
  "case_name_full": "BIOMEDICAL PATENT MANAGEMENT CORPORATION, Plaintiff-Appellant, v. State of CALIFORNIA, DEPARTMENT OF HEALTH SERVICES, Defendant-Appellee",
  "court": "Court of Appeals for the Federal Circuit",
  "court_id": "cafc",
  "court_citation": "Fed. Cir.",
  "court_jurisdiction": "Federal Appellate",
  "court_website": "http://www.cafc.uscourts.gov/",
  "court_pacer_id": 13,
  "docket_number": "2006-1515",
  "docket_id": 909836,
  "cluster_id": 210268,
  "date_filed": "2007-10-23",
  "date_argued": null,
  "date_terminated": null,
  "date_reargument_denied": null,
  "judge": "Rader and Gajarsa, Circuit Judges, and O'malley, District Judge",
  "referred_to_judge": null,
  "panel": "Arthur J. Gajarsa, Randall Ray Rader",
  "per_curiam": false,
  "suit_nature": null,
  "cause": null,
  "jurisdiction_type": null,
  "jury_demand": null,
  "bankruptcy_chapter": null,
  "bankruptcy_trustee": null,
  "citation": "505 F.3d 1328, 85 U.S.P.Q. 2d (BNA) 1074, 2007 U.S. App. LEXIS 24736, 2007 WL 3071687",
  "lexis_citation": "2007 U.S. App. LEXIS 24736",
  "neutral_citation": null,
  "scdb_id": null,
  "cite_count": 38,
  "cited_opinion_count": 21,
  "precedential_status": "Published",
  "posture": null,
  "syllabus": null,
  "opinion_count": 1,
  "opinion_types": "combined-opinion",
  "opinion_download_url": "http://www.cafc.uscourts.gov/images/stories/opinions-orders/06-1515.pdf",
  "opinion_pdf_url": "https://storage.courtlistener.com/pdf/2007/10/23/Biomedical_Patent_Management_Corporation_v._State_of_California_Dept._Of.pdf",
  "attorneys": "Andrew J. Dhuey, of Berkeley, CA, argued for plaintiff-appellant. With him on the brief was Richard Kirk Cannon., Susan J. King, Deputy Attorney General, United States Department of Justice, of San Francisco, CA, argued for defendant-appellee.",
  "firms": null,
  "parties": null,
  "matched_filings": null,
  "more_filings_available": null,
  "filing_numbers": null,
  "filing_descriptions": null,
  "latest_filing_date": null,
  "latest_filing_number": null,
  "latest_filing_description": null,
  "latest_filing_page_count": null,
  "latest_filing_url": null,
  "latest_filing_pdf_url": null,
  "text_snippet": "505 F.3d 1328 (2007) BIOMEDICAL PATENT MANAGEMENT CORPORATION, Plaintiff-Appellant, v. State of CALIFORNIA, DEPARTMENT OF HEALTH SERVICES, Defendant-Appellee. No. 2006-1515. United States Court of Appeals, Federal Circuit. October 23, 2007. *1329 *1330 Andrew J. Dhuey, of Berkeley, CA, argued",
  "pacer_case_id": null,
  "url": "https://www.courtlistener.com/opinion/210268/biomedical-patent-management-corp-v-california/"
}
```

Every row carries all 55 columns in the same order, so a CSV export of a mixed result set lines up. A field that genuinely exists on only one corpus comes back null on the other.

### Common use cases

- Tracking new patent, trademark, copyright or antitrust suits against named companies or across a whole nature-of-suit code.
- Mapping which firms and which named partners appear opposite you, or opposite a target, in a given district.
- Venue and judge analytics: how many 830 Patent cases a district takes, and which judges they land with.
- Building a litigation-exposure picture of an acquisition target or a counterparty from public filings.
- Assembling a citation-weighted reading list for a legal question by sorting opinions on `cite_count`.
- Feeding a case-law research agent that needs structured metadata plus a canonical link to the source.

### Use with AI agents and MCP

Apify's MCP server can discover and call this Actor from an AI workflow. An example intent:

> Find federal patent dockets filed since 2020 that mention infringement, and tell me which law firms appear most often.

Use the JSON from **Input example** as the tool arguments. The input schema is strict and rejects unsupported parameters, and the dataset schema documents every output field, including which corpus populates it, so an agent can tell "this field is null because dockets do not carry it" from "this case has no value". Point agents at `url` when they need the authoritative text, because the rows carry metadata only.

### Pricing and cost control

Output is billed per result at **$0.003 per result** (about $3.00 per 1,000 results), plus a $0.0002 Actor-start charge billed once per gigabyte of memory at run start. Use `max_items` to cap both output volume and charges. The price shown on the Apify Store listing is authoritative.

`max_items` is a hard ceiling on rows and therefore on the result charge, so the maximum a run can cost is something you decide before you start it. Start at 10 to 25 rows, look at what comes back for your query and court, and scale once the filters are right. Narrowing with `court` and `nature_of_suit` is cheaper than pulling a broad query and filtering afterwards.

### Reliability

The Actor calls the official CourtListener v4 search API rather than scraping the site, and it follows the API's own cursor pagination, so filters are applied once and carried forward by the server instead of being re-sent and doubled. Requests are paced with a short delay between pages.

The free tier is shared and rate limited. If CourtListener returns a 429, the run stops, logs a warning and keeps everything already pushed rather than failing and charging you for nothing. An upstream 4xx or 5xx is logged with the status and the start of the response body, and ends the run the same way. Every run finishes by logging how many rows it pushed, so a short result set is always distinguishable from a silent failure.

One known defect is worth stating plainly: searches against bankruptcy courts can end early. See **Limitations**.

### Limitations and responsible use

- **Document previews, not full text.** `text_snippet` carries the opening ~1000 characters of the opinion or the matched filing, and `opinion_pdf_url` and `latest_filing_pdf_url` link straight to the PDFs where CourtListener holds them. Fetching and parsing whole document bodies is a separate and much heavier operation, and it is not included.
- **Only a sample of each docket's filings.** `matched_filings`, `filing_numbers`, `filing_descriptions` and the `latest_filing_*` columns describe the filings that matched your query, which CourtListener caps at a few per docket; `more_filings_available` tells you when there are others. `latest_filing_date` is the whole docket's last activity only when your query was broad enough to match recent entries. Pulling a complete docket sheet is not included.
- **`nature_of_suit` applies to dockets only.** Opinions do not carry the code, so passing it with `search_type: opinions` filters nothing.
- **RECAP is not all of PACER.** It holds what contributors have uploaded, so coverage is deep in heavily litigated districts and patchy elsewhere. Absence from RECAP is not evidence that a case does not exist.
- **`attorneys`, `firms` and `parties` are often empty.** They are populated only where a docket has been parsed in full, and they are capped at the first eight entries; `citation` is capped at the first four. A long list of counsel is therefore truncated, not complete.
- **State coverage is opinions only.** RECAP is a federal system, so state trial dockets are not in it.
- **Names are as filed.** Parties are not normalised or entity-resolved, so one company appears under several spellings, and `judge` on an opinion is sometimes a surname list rather than a single name.
- **FCRA and similar uses are prohibited.** Do not use this data for employment, credit, insurance, housing, tenant or other consumer screening, and do not resell it as a background-check product.

CourtListener is operated by the Free Law Project, a US non-profit, and the underlying judicial records are generally public domain. Respect their terms, and if you are building a product on this at any scale, speak to them about a commercial arrangement - their funding is why the data exists. Verify anything consequential against the source document before acting on it.

### FAQ

#### Does it need my own API key?

No. CourtListener's search endpoint is open, and the Actor calls it unauthenticated.

#### Why are so many fields null in my results?

Because you are looking at one corpus. Opinions carry citations, cite counts and precedential status; dockets carry nature of suit, cause, parties and counsel. A null in a docket row's `citation` means dockets do not have citations, not that the case has none. Run the other `search_type` if you need the other half.

#### Can I search a specific court?

Yes. Pass a CourtListener court ID to `court`, such as `cafc`, `txwd`, `txed`, `ca9` or `dcd`. Every returned row carries `court_id` in the same vocabulary, so you can discover IDs from a broad run and then narrow.

#### Can I export the results?

Yes. Download the default dataset as JSON, CSV, Excel or XML, or pull it through the Apify API.

#### How do I control cost?

Set `max_items` to the most rows you want. It is a hard ceiling on the per-result charge. Start small, check the filters, then scale.

#### Can an AI agent call it?

Yes. The input schema is strict and the dataset schema is documented field by field, so it can be driven through Apify's MCP tooling.

#### Can I use this for background checks on individuals?

No. The Free Law Project's terms prohibit FCRA uses, and that prohibition passes to you. Use it for corporate and commercial litigation research.

# Changelog

This Actor's version history is a separate document: https://apify.com/captainhandsome/courtlistener-case-search/changelog.md

# Actor input Schema

## `search_type` (type: `string`):

Published judicial opinions, or federal court dockets from RECAP.

## `query` (type: `string`):

Free text across case name and full text, e.g. 'patent infringement' or a company name.

## `court` (type: `string`):

CourtListener court ID, e.g. cafc (Federal Circuit), txed (E.D. Texas), ca9 (Ninth Circuit).

## `nature_of_suit` (type: `string`):

Federal civil suit code. Business codes: 110 Insurance, 190 Contract, 410 Antitrust, 430 Banks, 470 RICO, 820 Copyright, 830 Patent, 840 Trademark, 850 Securities. Dockets only.

## `filed_after` (type: `string`):

YYYY-MM-DD.

## `filed_before` (type: `string`):

YYYY-MM-DD.

## `max_items` (type: `integer`):

Hard cap on results returned. Also caps your spend.

## `include_court_details` (type: `boolean`):

Look each court up once to add its jurisdiction level (Federal District, State Supreme, Federal Bankruptcy and so on), homepage and PACER court ID. The lookup is cached, so it costs one extra request per distinct court in the run rather than one per row. Turn it off for the fastest possible run.

## Actor input object example

```json
{
  "search_type": "opinions",
  "query": "patent infringement",
  "max_items": 100,
  "include_court_details": true
}
```

# Actor output Schema

## `results` (type: `string`):

One flat row per opinion or docket.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "query": "patent infringement"
};

// Run the Actor and wait for it to finish
const run = await client.actor("captainhandsome/courtlistener-case-search").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "query": "patent infringement" }

# Run the Actor and wait for it to finish
run = client.actor("captainhandsome/courtlistener-case-search").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "query": "patent infringement"
}' |
apify call captainhandsome/courtlistener-case-search --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,captainhandsome/courtlistener-case-search"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/ZbspUwNbsaGEUMhd6/builds/Y4cdyM4XHOdoVN2tb/openapi.json
