Skip to main content

CourtPy

Version PyPI Latest Release GitHub Latest Release
Status Build Status Development Status Project Stability
Documentation Hosted By
Tools Documentation Linter Dependency Manager Pre-commit CI Editor Settings Repository Template Dependency Maintainer
Compatibility Compatible Python Versions Linux MacOS Windows
Stats PyPI Download Rate (per month) GitHub Stars GitHub Contributors GitHub Issues GitHub Forks

What is CourtPy?

CourtPy collects court opinions, turns them into data, and analyzes them. It is designed for empirical legal research, where the data has to be explained and the results reproduced, and for researchers who do not write much code.

  • Collect opinions from CourtListener, from its free bulk data or its API, or use opinions you downloaded from Lexis-Nexis.
  • Parse them into a table, one row for each case, with rules written in spreadsheets (CSV files) that anyone can read and change: the parties and their roles, the court, the judges, the disposition, the issues discussed, the citations, and more.
  • Code derived variables, such as whether a case is criminal, whether the decision reversed the court below, and which side won.
  • Identify the judges on each case, with what the Federal Judicial Center reports about them, to study the composition of panels or how each judge voted.
  • Merge other researchers' data into the cases: the Supreme Court's Martin-Quinn scores, the NOMINATE scores of Congress, and the Supreme Court Database, each downloaded from its source.
  • Analyze the table with amos, which compares models, preprocessing, and statistical methods, and records everything needed to reproduce the results.

Why use CourtPy?

  • Transparent. What CourtPy finds in an opinion comes from rules in CSV files, not from code, so a study's methods can be read, reported, and changed by anyone who can use a spreadsheet. The rules are part of the study, so others can check them.
  • Free data at scale. CourtPy reads CourtListener's bulk data, which covers millions of opinions, needs no account, and has no limits on downloads. Its API client handles CourtListener's rate limits by waiting and resuming.
  • Reproducible. A whole study (which cases, how they were parsed and coded, and how they were analyzed) fits in one settings file, and amos records every step and the version of every package.
  • Safe with secrets. Your CourtListener API key is kept in your computer's keyring (or a private file in your user folder), never in a project, so it cannot be shared or committed by accident.

CourtPy is not a citation extractor or a legal research tool. For finding and resolving citations in text, see eyecite.

Getting started

Requirements

CourtPy requires Python 3.11 or later. It runs on Linux, macOS, and Windows. It is built on amos, pandas, and requests, which are installed automatically.

Extracting cases from CourtListener's bulk data needs a good connection and room for the bulk files (about 63 GB for the opinions, clusters, and dockets), unless they are streamed. Downloading with the API needs a free CourtListener account.

Installation

To install CourtPy, use pip:

pip install courtpy

To install the latest development version from GitHub instead:

pip install git+https://github.com/WithPrecedent/courtpy

To work on CourtPy itself, see the contribution guide.

Usage

Collecting opinions from CourtListener

CourtListener offers its opinions in two ways, and CourtPy uses both. Both save each case as a JSON file in a folder named for its court (such as court_listener/ca1/4567890.json), and the parser reads them the same way.

The bulk data is the whole CourtListener database, published as compressed CSV files every three months. It needs no API key and has no limits, so it is the best way to collect many cases. The files are large (the opinions file is more than 50 GB), so extracting cases takes a few hours. The files are kept outside your project so that they can be used again:

import courtpy

courtpy.BulkData().extract(
    courts = "federal_circuits",
    start_date = "2019-01-01",
    end_date = "2019-12-31",
    folder = "court_listener")

The API is best for small or very recent sets of cases. It needs an API key: sign in to CourtListener and open the "API" page of your profile. A free account can make 5 requests a minute, 50 an hour, and 125 a day (as of 2026). CourtPy asks for 20 cases at a time, waits when CourtListener asks it to, and saves its place, so a download stopped by the daily limit resumes where it stopped when it is run again:

courtpy.CourtListener().download("ca1", "2026-09-01", "2026-09-30", "court_listener")

Courts are named by their CourtListener ids (such as ca1, cadc, cafc, and scotus) or by groups: federal_appellate, federal_circuits, and supreme_court.

Keeping your API key secret

Your API key is never written in a project's files or in CourtPy itself. Store it once:

courtpy key set

That asks for the key (without showing it) and stores it in your computer's keyring (Windows Credential Manager, the macOS Keychain, or the Secret Service on Linux), or, if there is none, in a private file in your user folder. From then on, CourtPy finds it whenever it is needed. courtpy key show says where it was found (with most of it hidden), and courtpy key delete removes it.

CourtPy looks for the key in this order: the COURTLISTENER_API_KEY environment variable, the keyring, the private file, and a .env file in the current folder (with a line such as COURTLISTENER_API_KEY=your-key; keep such a file out of version control). In Python, use courtpy.secrets.set_api_key(...) and courtpy.secrets.get_api_key().

Using opinions from Lexis-Nexis

Lexis-Nexis downloads many cases into one text file. Divide such files into one file for each case, and then parse them as the source "lexis_nexis":

courtpy.lexis.split("downloads", folder = "lexis_nexis")
table = courtpy.parse("lexis_nexis", source = "lexis_nexis")

Parsing and coding

courtpy.parse reads a folder of saved cases and returns a pandas.DataFrame, one row for each case, labeled by its id:

table = courtpy.parse("court_listener")
dataset = courtpy.code(table)  # parties, case types, and outcomes
courtpy.save_cases(dataset.data, "cases.csv")

The rules are in the CSV files in CourtPy's instructions folder, with a guide to writing your own. The "federal" rules make more than 250 variables, including:

Variables Meaning
party1, party2 The parties on each side of "v."
party1_appellant, party2_respondent, party1_united_states, ... Each party's role, and whether it is the United States.
court_num The circuit (1 to 11, 12 for the D.C. Circuit, 13 for the Federal Circuit, 99 for the Supreme Court).
panel_judges, panel_size, author, concurring, dissenting The judges.
disposition_reverse, decision_reverse, opinion_reverse, ... The disposition: from the header, from the decision that the opinion states ("AFFIRMED."), and from the opinion's words anywhere.
agency The federal agency involved, if any.
civil_..., criminal_..., general_..., procedure_..., standard_... Issues discussed in the opinion (such as criminal_firearm, civil_titlevii, and standard_de_novo).
references_case, references_statute, ... Citations in the opinion.
published, year, date_filed, word_count Other information.

The coders (which are amos techniques) add type_criminal, each party's role in the appeal and in the case (such as party2_criminal_defendant), and the outcomes: outcome_reversal, outcome_party1_won, outcome_criminal_defendant_won, and others.

Adding the judges and the politics of the day

The rules find the judges' names as an opinion writes them ("LYNCH"). To study the judges, make a roster once from the Federal Judicial Center's biographical directory (three small files, which CourtPy downloads), and more coders can use it:

courtpy.judges.Roster.from_fjc().save()
dataset = courtpy.code(table, [
    "code_parties", "code_case_type", "code_outcome",
    "code_judges", "code_politics", "judge_votes"])

Cases, judges, years, and other researchers' data are tables whose rows are different things, so these coders are the mergers and shapers of amos: a merger adds the columns of another table to the rows that it matches, without ever adding or losing a row, and records how many rows it matched.

Coder Adds
merge_judges What the roster says about the judge that each row names, such as the author of each opinion (author_party, author_woman, author_age, ...).
code_judges The judges of each panel who were found in the roster (panel_names and panel_found) and the panel's composition, such as panel_party (the mean party of the presidents who appointed its judges, from -1 for Democrats to 1 for Republicans), panel_woman, panel_age, and panel_prosecutor.
judge_votes A table with one row for each judge on each case, with the judge's attributes (judge_party, ...), the other judges' (colleagues_party, ...), and the judge's vote (vote_reversal, the outcome or its opposite for a judge who dissented).
code_politics The party of the president in the case's year (politics_president_party) and, from a table that courtpy.politics.build_table makes, the Supreme Court's Martin-Quinn scores and the median NOMINATE scores of the Senate and House.
code_scdb For cases of the Supreme Court, the coding of the Supreme Court Database, such as scdb_issueArea, scdb_decisionDirection, and scdb_partyWinning.

CourtPy downloads the scores and the database from their sources the first time they are needed. See examples/judge_votes.ini for a study of judges' votes.

Describing a whole study in one file

A study is an amos project, described in one settings file. CourtPy adds loaders to amos: load_court_listener, load_lexis_nexis, and load_cases. Named first in the wrangler, a loader collects the cases (downloading them, if asked), parses them, and codes them, so the project needs no other data. See examples/federal_appeals.ini:

[general]
seed = 43
label = outcome_reversal

[files]
input_folder = data
interim_folder = data

[appeals_project]
appeals_workers = wrangler, analyst, critic

[wrangler]
techniques = load_court_listener, filter_rows, keep_columns, drop_missing

[load_court_listener_parameters]
source = court_listener
download = bulk
courts = federal_circuits
start_date = 2019-01-01
end_date = 2019-12-31
save = cases.csv
reuse = true

[analyst]
design = experiment
criterion = roc_auc
steps = split, model
split_techniques = stratified
model_techniques = baseline, logit, random_forest

[critic]
techniques = scorecard

The loader finds (and saves) the cases in the input folder ("data") and saves the coded table in the interim folder. With reuse = true, later runs load that table instead of parsing the cases again.

Run it from a terminal with courtpy run federal_appeals.ini --export, or in Python:

project = courtpy.Project.create("federal_appeals.ini")
print(project.result.tables["scorecard"])
project.export()

courtpy.Project is amos.Project, so everything in the amos documentation works.

Commands

Command Does
courtpy key set, show, delete Manages your CourtListener API key.
courtpy download bulk --courts ca1 --start 2019-01-01 --end 2019-12-31 Extracts cases from the bulk data.
courtpy download api --courts ca1 --start 2026-09-01 Downloads cases with the API.
courtpy split FILES --folder lexis_nexis Divides Lexis-Nexis files of many cases.
courtpy parse FOLDER --output cases.csv Parses and codes saved cases into a table.
courtpy rules my_rules.csv Checks a file of rules and lists the variables it makes.
courtpy run study.ini --export Runs a whole study.

Add --help to any command for its options. See the documentation for a tutorial, the advanced user guide, a guide to writing rules, and recipes.

Contributing

Contributors are always welcome. Feel free to grab an issue to work on or make a suggested improvement. If you wish to contribute, please read the Contribution Guide and Code of Conduct.

Similar Projects

  • CourtListener and the Free Law Project's tools, including eyecite (which finds and resolves legal citations in text) and juriscraper (which collects opinions from court websites).
  • amos, which CourtPy uses to analyze the cases it parses.

License

Use of this repository is authorized under the Apache Software License 2.0.

Metadata

Release files for courtpy 0.2.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for courtpy 0.2.2
File Size Uploaded
courtpy-0.2.2.tar.gz 154.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for courtpy 0.2.2
File Interpreter ABI Platform
courtpy-0.2.2-py3-none-any.whl Python 3 none any Details

Total release size: 276.7 kB

Release files / courtpy-0.2.2.tar.gz

Download URL courtpy-0.2.2.tar.gz
Size 154.8 kB
Tags Source
SHA-256 checksum
How to use checksums
510af236b4cb49a97082b1c3c18cd99a52e76602e879b6f089620a86ba3b6838
BLAKE2b-256 checksum
How to use checksums
6675292945e6ed2fbe9e53500521dedc95d4d2f077481559dcd5d89b2c990df2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / courtpy-0.2.2-py3-none-any.whl

Download URL courtpy-0.2.2-py3-none-any.whl
Size 121.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
43df7c64c0a87353826f9beabdcae0f6e570a6d7ccdcb872dbb37b69460a6427
BLAKE2b-256 checksum
How to use checksums
220cb7f4ce1a48cc645e3b98fa9ec08c2b94751d45120b07db4d34d896bd497f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.2.2 This release

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page