Exploring the CourtListener API
Using the CourtListener REST API to search U.S. court records and download publicly available data
CourtListener, a website operated by the Free Law Project, offers the general public access to millions of court records, mostly related to cases filed in U.S. federal courts. The majority of the documents in CourtListener’s database originate on PACER, the U.S. government’s official pay-per-use online repository of legal documents, which are uploaded to CourtListener’s RECAP archive by users via a browser extension. CourtListener has recently started offering instant access to an API which can be used to programmatically query this data, with both free and paid tiers.
import json
import pandas as pd
import requests
import sys
import time
URL_ROOT = "https://www.courtlistener.com/api/rest/v4/"
def retry (url, api_key, retries=10, backoff=1.8, pause=20, max_pause=900):
print (url)
while retries > 0:
try:
result = requests.get (url, headers={
"User-Agent" : "placeholder",
"Authorization" : "Token " + api_key
}, timeout=10).json ()
if "detail" not in result:
return result
else:
print (result)
except:
pass
retries = retries - 1
if retries > 0:
print ("error, sleeping " + str (pause) + " seconds...")
time.sleep (pause)
pause = min (max_pause, pause * backoff)
return None
def courtlistener_docket_entries (docket, api_key):
results = []
url = URL_ROOT + "docket-entries/?docket=" + str (docket)
while url:
data = retry (url, api_key)
if "results" in data:
results.extend (data["results"])
url = data["next"] if "next" in data else None
print ("so far: " + str (len (results)))
return results
def courtlistener_search (query, api_key, qtype="r", options=None):
results = []
url = URL_ROOT + "search?q=" + query + "&type=" + qtype
if options:
for k, v in options.items ():
url = url + "&" + k + "=" + v
while url:
data = retry (url, api_key)
if "results" in data:
results.extend (data["results"])
url = data["next"] if "next" in data else None
print ("so far: " + str (len (results)))
return results
if __name__ == "__main__":
if sys.argv[1] == "docket":
data = courtlistener_docket_entries (sys.argv[3], sys.argv[2])
with open (sys.argv[4], "w") as file:
json.dump (data, file, indent=2)
elif sys.argv[1] == "search":
if len (sys.argv) > 5:
options = json.loads (sys.argv[5])
else:
options = None
data = courtlistener_search (sys.argv[3], sys.argv[2], options=options)
with open (sys.argv[4], "w") as file:
json.dump (data, file, indent=2)The above Python code contains wrapper functions for accessing two of the endpoints provided by the CourtListener API, specifically the endpoint used to retrieve a list of docket entries for a given CourtListener docket ID, and the endpoint used to search CourtListener’s database. Both endpoints return records in batches of 20, and provide a pagination mechanism for retrieving additional batches. The above code is relatively basic and could be improved in multiple ways, both by adding support for additional endpoints, and by adapting the general-purpose retry function to be aware of CourtListener’s relevant rate limits.
docket = 67654013
data = courtlistener_docket_entries (docket, api_key)
print (len (data))
with open ("67654013.json", "w") as file:
json.dump (data, file, indent=2)The above code snippet downloads all docket entries for docket ID 67654013, “X Corp v. Center for Countering Digital Hate, Inc.”, filed in 2023 in the Northern District of California. The data is returned in JSON format and saved to a local file. (The CourtListener API also provides the option to return the data in XML or HTML format.)
The image above shows one of the 102 records returned by the CourtListener API for docket ID 67654013. The record includes a variety of fields, including the URL for the human-readable version of the same CourtListener record, as well as links to attached documents in PDF format, hosted on both on CourtListener itself and the Internet Archive’s Wayback Machine. The filing date, a brief description, and the plain text of any attached documents are also included, among other data.
query = ""
options = {
"party_name" : "Scientology"
}
data = courtlistener_search (query, api_key, options=options)
with open ("scientology2.json", "w") as file:
json.dump (data, file, indent=2)The search endpoint is slightly more complex, and accepts a variety of options corresponding to those available in the search forms on the CourtListener website. Searches for cases, filings, oral arguments, and judges are all supported via the API. Note that less specific searches may return extremely large numbers of results and thus quickly exhaust request quotas. As a test example, I searched for all cases with the Church of Scientology as a party.
This search yielded a total of 138 distinct federal cases with The Church of Scientology as one of the parties, with filing dates ranging from 1977 to 2026. (Note that this includes only cases filed in federal court, and any state litigation involving the church is therefore absent.) The volume of new cases peaked in the early 1990s, although 2026 has already seen five additional cases filed.
As mentioned earlier, this article only covers a subset of the available functionality provided by the CourtListener API, and the two use cases discussed here are relatively simple. A wide variety of other data is available, and people with a mix of legal knowledge and basic coding skills should be able to come up with numerous interesting applications for the large volume of information stored by CourtListener. (Also, if you use PACER and are interested in expanding CourtListener’s database, you can install the RECAP extension to automatically upload documents you view to CourtListener.)
On an entirely unrelated note, I recently finished an album; you can check it out below if you’re interested.



