# CourtListener Legal Search Feed Scraper (`automation-lab/courtlistener-legal-search-feed-scraper`) Actor

⚖️ Monitor recent US court opinions by keyword through CourtListener's public feeds. Export stable IDs, courts, dates, links, and matched excerpts.

- **URL**: https://apify.com/automation-lab/courtlistener-legal-search-feed-scraper.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.03 / 1,000 legal opinions

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## CourtListener Legal Search Feed Scraper

Monitor recent US legal opinions by keyword with CourtListener's public custom-search feeds.

This Apify Actor turns Atom XML into clean, deduplicated dataset rows for legal research, compliance monitoring, journalism, and legal-tech workflows.

Give it topics such as `antitrust`, a party name, or an existing CourtListener search-feed URL.

Get case titles, courts, publication dates, stable opinion IDs, source links, and matched excerpts.

> **Scope:** this Actor extracts the recent public opinion-search feed. It does not promise complete historical backfills, docket details, REST API records, or full opinion text.

### What does CourtListener Legal Search Feed Scraper do?

The Actor queries CourtListener's anonymous custom-search Atom feed.

It converts each matching opinion into a structured record suitable for JSON, CSV, Excel, API, webhook, and database workflows.

It can:

- ⚖️ search recent legal opinions by one or more keywords;
- 🔗 process existing CourtListener custom-search feed URLs;
- 🏛️ extract case title and court;
- 📅 preserve opinion publication and feed update timestamps;
- 🆔 expose a stable numeric opinion ID for upserts;
- 🧹 remove duplicate opinions found by multiple searches;
- 📝 clean the feed's HTML excerpt into readable text;
- 📦 stop at a user-defined global result limit.

### Why use this CourtListener opinion scraper?

CourtListener already provides a useful public feed, but raw Atom XML is awkward in recurring data pipelines.

This Actor handles XML parsing, retries, normalization, limits, deduplication, dataset storage, and exports.

Use it when you need a lightweight monitoring feed rather than a broad docket or full-text data service.

No CourtListener login, API token, browser, or proxy is required for the supported feed.

### Who is it for?

#### Legal research teams

Watch topics, doctrines, parties, industries, or statutes and review new matching opinions.

#### Compliance teams

Schedule searches for regulatory, sanctions, privacy, competition, or enforcement terms.

#### Journalists

Track companies, public bodies, and high-interest legal subjects across recent opinions.

#### Legal-tech developers

Feed normalized opinion records into alerting systems, RAG intake queues, knowledge bases, or case-monitoring products.

#### Data engineers

Use stable `opinionId` values to upsert records and avoid duplicate downstream processing.

### What legal opinion data can I extract?

| Field | Type | Meaning |
|---|---|---|
| `query` | string | Keyword or feed query associated with the record |
| `caseTitle` | string | Case name from the Atom entry |
| `opinionUrl` | string | Canonical CourtListener opinion page |
| `opinionId` | string | Stable numeric CourtListener opinion identifier |
| `court` | string | Court name provided by the feed |
| `publishedAt` | string | Opinion publication timestamp |
| `feedUpdatedAt` | string | Search feed update timestamp |
| `summary` | string | Cleaned matched excerpt when available |
| `sourceUrl` | string | Exact CourtListener feed URL requested |

### How to scrape recent CourtListener opinions

1. Open the Actor input page.
2. Enter one or more legal search keywords.
3. Optionally add existing CourtListener search feed URLs.
4. Choose the maximum number of unique opinions.
5. Click **Start**.
6. Review the **Legal opinions** dataset view.
7. Export results or connect an integration.

The default `antitrust` example is intentionally small and should finish quickly.

### Input

```json
{
  "keywords": ["antitrust", "data privacy"],
  "maxItems": 40
}
```

You can also provide a custom feed URL:

```json
{
  "feedUrls": [
    {
      "url": "https://www.courtlistener.com/feed/search/?type=o&q=Google"
    }
  ],
  "maxItems": 20
}
```

At least one keyword or feed URL is required.

A maximum of 50 combined searches is accepted per run.

Only HTTPS CourtListener URLs under `/feed/search/` are allowed.

### Output example

```json
{
  "query": "antitrust",
  "caseTitle": "Sensory, Inc. v. Google LLC",
  "opinionUrl": "https://www.courtlistener.com/opinion/10927366/sensory-inc-v-google-llc/",
  "opinionId": "10927366",
  "court": "District Court, District of Columbia",
  "publishedAt": "2026-07-13T00:00:00-07:00",
  "feedUpdatedAt": "2026-07-13T00:00:00-07:00",
  "summary": "INTRODUCTION Before the court is another antitrust case brought against Defendant Google LLC...",
  "sourceUrl": "https://www.courtlistener.com/feed/search/?type=o&q=antitrust"
}
```

Exact records change as CourtListener updates its search feed.

### How deduplication works

CourtListener gives each Atom entry a stable opinion URL/ID.

The Actor uses that stable entry identity to deduplicate across every keyword and feed URL in one run.

If one opinion matches both `antitrust` and `Google`, it is saved once under the first processed query.

Use `opinionId` as the downstream database key.

### How much does it cost to monitor CourtListener opinions?

This Actor uses pay-per-event pricing.

A small one-time **Start** event covers run initialization.

A **Legal opinion** event is charged for each dataset record produced.

Tiered per-result discounts apply automatically by Apify plan.

| Apify tier | Price per legal opinion |
|---|---:|
| Free | $0.000058381 |
| Bronze | $0.000050766 |
| Silver | $0.000039598 |
| Gold | $0.00003046 |
| Platinum | $0.000020306 |
| Diamond | $0.000014215 |

The one-time Start event is $0.005 on every tier. The live pricing tab remains authoritative before you start a run.

You can set a maximum total charge in Apify for additional control.

Runs that naturally return fewer recent matches only charge for the records produced, plus the start event.

### Scheduling legal search monitoring

Choose **Schedules** in Apify Console to run the same searches hourly, daily, or weekly.

For each scheduled run:

- fetch the dataset;
- upsert rows by `opinionId`;
- alert only on IDs not seen before;
- retain `feedUpdatedAt` for freshness checks.

This pattern turns a recent search feed into a practical topic monitor.

### Integrations

#### Webhooks

Send a run-finished webhook to Slack, Microsoft Teams, Zapier, Make, or your own API.

#### Google Sheets

Export new rows for researchers who prefer a shared review queue.

#### Databases

Upsert by `opinionId` into PostgreSQL, BigQuery, Snowflake, or another warehouse.

#### Alerting

Filter rows by court or phrase, then route high-priority matches to email or incident tooling.

#### AI and RAG intake

Use the summary and opinion URL to triage relevant opinions before a separate authorized document-retrieval step.

### API usage with Node.js

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/courtlistener-legal-search-feed-scraper').call({
    keywords: ['antitrust', 'data privacy'],
    maxItems: 40,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

### API usage with Python

```python
from apify_client import ApifyClient

client = ApifyClient('YOUR_APIFY_TOKEN')
run = client.actor('automation-lab/courtlistener-legal-search-feed-scraper').call(
    run_input={'keywords': ['antitrust'], 'maxItems': 20}
)
items = client.dataset(run['defaultDatasetId']).list_items().items
print(items)
```

### API usage with cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~courtlistener-legal-search-feed-scraper/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keywords":["antitrust"],"maxItems":20}'
```

After the run completes, fetch items from its default dataset.

### Use with Apify MCP

Connect the Actor to Claude and other MCP clients through Apify MCP Server:

`https://mcp.apify.com/?tools=automation-lab/courtlistener-legal-search-feed-scraper`

Example prompts:

- “Run the CourtListener opinion monitor for antitrust and return new cases by court.”
- “Extract recent legal opinions mentioning a named company and summarize the result metadata.”
- “Schedule this legal search and key downstream records by opinion ID.”

#### Claude Code

Register the Actor as an HTTP MCP server:

```bash
claude mcp add --transport http courtlistener-legal-search "https://mcp.apify.com/?tools=automation-lab/courtlistener-legal-search-feed-scraper"
```

#### Claude Desktop

Add this entry to the `mcpServers` object in Claude Desktop's configuration:

```json
{
  "mcpServers": {
    "courtlistener-legal-search": {
      "url": "https://mcp.apify.com/?tools=automation-lab/courtlistener-legal-search-feed-scraper"
    }
  }
}
```

#### Cursor

Open **Settings → Tools & MCP → Add custom MCP** and use the same `mcpServers` JSON entry shown for Claude Desktop.

#### VS Code

Open the MCP configuration for your workspace, choose **Add Server → HTTP**, and enter the Apify MCP URL above.

Authenticate the MCP connection with your Apify token when prompted.

### Tips for reliable legal monitoring

- 🎯 Use precise phrases when broad keywords produce noisy excerpts.
- 🧪 Start with one keyword and 20 items before creating a larger watchlist.
- 🆔 Store `opinionId`, not only the title, as your durable key.
- 🕐 Compare `feedUpdatedAt` between scheduled runs.
- 🏛️ Filter the exported dataset by `court` downstream.
- 🔗 Keep `sourceUrl` so researchers can reproduce the search.
- 📚 Use another source when you require historical backfills or docket-level data.

### Limits and freshness

CourtListener's public custom-search feed is a recent, sparse monitoring surface.

It is not guaranteed to expose every historical opinion matching a query.

The Actor cannot paginate beyond what the public feed returns.

`maxItems` limits output; it does not make additional historical records available.

Some opinions may have brief excerpts, and summary content depends on CourtListener.

The Actor does not fetch or interpret the linked full opinion.

### Error handling

Transient requests are retried up to three times with bounded exponential delay.

Each request has a 30-second timeout.

Persistent non-200 or non-Atom responses fail with the affected source URL.

Unsupported hostnames and paths are rejected before fetching.

Malformed entries missing a title, URL, or publication timestamp are logged and skipped.

### Data quality

All core values come directly from CourtListener's Atom feed.

Summary HTML is converted to plain text for easier export.

URLs remain canonical CourtListener links.

Dates remain ISO 8601 strings with source timezone offsets.

Court names are preserved as published by CourtListener.

No AI inference is used to invent missing legal metadata.

### Legality: is it legal to scrape CourtListener?

This Actor accesses an anonymous public Atom feed published by CourtListener and the Free Law Project.

You are responsible for using the data in accordance with applicable law, CourtListener terms, attribution expectations, and your organization's policies.

Do not treat extracted metadata or excerpts as legal advice.

Do not assume a search feed is a complete record of litigation or precedential authority.

For high-stakes work, verify information against the linked source and official court materials.

### FAQ

#### Does this scrape full opinion text?

No. It returns feed metadata, a matched excerpt when available, and the canonical opinion URL.

#### Does it include dockets or RECAP documents?

No. The supported v1 surface is the recent legal-opinion custom-search feed.

#### Do I need a CourtListener API key?

No. The public Atom feed is anonymous.

#### Can it backfill all matching historical cases?

No. Use it as a recent monitoring feed, not a complete historical search API.

#### Why did my query return only a few records?

The public feed may have few recent matches. Try a broader phrase and verify the same search on CourtListener.

#### Why was my feed URL rejected?

For safety and product consistency, URLs must use `courtlistener.com/feed/search/` over HTTPS.

#### How do I avoid repeat alerts?

Persist `opinionId` values and notify only when a scheduled run returns an unseen ID.

### Related legal and regulatory scrapers

- [FTC Enforcement Actions Scraper](https://apify.com/automation-lab/ftc-enforcement-actions-scraper) for FTC enforcement-library records.
- [CFPB Consumer Complaint Database Scraper](https://apify.com/automation-lab/cfpb-consumer-complaint-database-scraper) for consumer complaint data.

These sources cover different datasets and should not be treated as CourtListener substitutes.

### Support

If a run fails, share the run URL and a redacted copy of the input through the Actor's Issues tab.

Include the affected keyword or CourtListener feed URL.

For result-quality questions, compare the output with the current public feed because source records change over time.

### Start monitoring CourtListener opinions

Use the default `antitrust` input for a quick test.

Then replace it with the parties, doctrines, statutes, industries, or compliance topics your team follows.

Schedule repeat runs and upsert records by `opinionId` to build a focused legal-opinion alert workflow.

# Actor input Schema

## `keywords` (type: `array`):

Topics, parties, laws, or phrases to look up in CourtListener's recent opinion search feed.

## `feedUrls` (type: `array`):

Optional custom-search Atom feed URLs under courtlistener.com/feed/search/. Existing CourtListener filters in each URL are preserved.

## `maxItems` (type: `integer`):

Maximum unique legal opinions saved across all keywords and feed URLs.

## Actor input object example

```json
{
  "keywords": [
    "antitrust"
  ],
  "maxItems": 20
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

# 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 = {
    "keywords": [
        "antitrust"
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/courtlistener-legal-search-feed-scraper").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 = {
    "keywords": ["antitrust"],
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/courtlistener-legal-search-feed-scraper").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 '{
  "keywords": [
    "antitrust"
  ],
  "maxItems": 20
}' |
apify call automation-lab/courtlistener-legal-search-feed-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/courtlistener-legal-search-feed-scraper"
        }
    }
}

```

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/hWGcyQwHhbk2YxG1T/builds/gKmFDSir9zhLFfaa8/openapi.json
