Edu APIaustlii.edu.au ↗
Access AustLII's Australian legal databases via API. Browse court decisions by year, list cases, and retrieve RTF, PDF, and print-friendly download links.
What is the Edu API?
The AustLII API provides 4 endpoints to discover, browse, and retrieve Australian legal case documents from AustLII (australasian Legal Information Institute). The list_databases endpoint enumerates every available jurisdiction and court database with its path identifier, which you then pass to get_database_years, list_cases, and get_case to navigate and download decisions in RTF, PDF, or eco-friendly print formats.
No input parameters required.
curl -X GET 'https://api.parse.bot/scraper/6c24f681-35dd-4b59-b1c9-216b3fc16289/list_databases' \ -H 'X-API-Key: $PARSE_API_KEY'
Typed, relational, agent-ready
A generated client with real types, enums, and the links between objects — the structure a flat JSON response can't carry. Autocompletes in your editor and reads cleanly to coding agents.
- Fully typed · autocompletes
- Objects link to objects
- Typed errors & pagination
Typed Python client. Set up the SDK in your uv project, then pull this API’s typed client:
uv add parse-sdk uv run parse init uv run parse add --marketplace austlii-edu-au-api
uv run parse add --marketplace pulls a pinned snapshot of this canonical API — it won’t change underneath you. To customize it, subscribe and swap to your own copy.
"""
AustLII Legal Case Scraper - ParseClient Implementation
Get your API key from: https://parse.bot/settings
"""
import os
import requests
from typing import Optional, Any
class ParseClient:
"""Client for interacting with the AustLII Legal Case Scraper API."""
def __init__(self, api_key: Optional[str] = None):
"""
Initialize the ParseClient.
Args:
api_key: API key for Parse API. If not provided, reads from PARSE_API_KEY environment variable.
"""
self.base_url = "https://api.parse.bot"
self.scraper_id = "6c24f681-35dd-4b59-b1c9-216b3fc16289"
self.api_key = api_key or os.getenv("PARSE_API_KEY")
if not self.api_key:
raise ValueError("API key must be provided or set in PARSE_API_KEY environment variable")
def _call(self, endpoint: str, method: str = "POST", **params) -> dict[str, Any]:
"""
Make a request to the Parse API.
Args:
endpoint: The API endpoint name
method: HTTP method (GET or POST)
**params: Parameters to pass to the endpoint
Returns:
The JSON response from the API
"""
url = f"{self.base_url}/scraper/{self.scraper_id}/{endpoint}"
headers = {
"X-API-Key": self.api_key,
"Content-Type": "application/json"
}
if method == "GET":
response = requests.get(url, headers=headers, params=params)
elif method == "POST":
response = requests.post(url, headers=headers, json=params)
else:
raise ValueError(f"Unsupported HTTP method: {method}")
response.raise_for_status()
return response.json()
def list_databases(self) -> dict[str, Any]:
"""
List all available legal databases on AustLII organized by jurisdiction and category.
Returns:
Dictionary containing list of databases with their metadata
"""
return self._call("list_databases", method="GET")
def get_database_years(self, path: str) -> dict[str, Any]:
"""
List all available years for a specific database.
Args:
path: The database path (e.g. 'cgi-bin/viewdb/au/cases/cth/HCA')
Returns:
Dictionary containing list of available years for the database
"""
return self._call("get_database_years", method="GET", path=path)
def list_cases(self, path: str, year: Optional[str] = None) -> dict[str, Any]:
"""
List cases for a specific database and year.
Args:
path: The database path (e.g. 'cgi-bin/viewdb/au/cases/cth/HCA')
year: The year (e.g. '2024'). If not provided, lists all cases
Returns:
Dictionary containing list of cases for the specified database and year
"""
params = {"path": path}
if year:
params["year"] = year
return self._call("list_cases", method="GET", **params)
def get_case(self, url: str) -> dict[str, Any]:
"""
Retrieve detailed metadata and download links for a specific case document.
Args:
url: The URL of the case document page
Returns:
Dictionary containing case metadata and download links
"""
return self._call("get_case", method="GET", url=url)
def main():
"""Practical workflow demonstrating the ParseClient with real use case."""
# Initialize the client
client = ParseClient()
print("=" * 90)
print("AustLII Legal Case Scraper - Practical Usage Example")
print("=" * 90)
# Step 1: List all available databases
print("\n[Step 1] Fetching all available legal databases from AustLII...")
try:
databases_response = client.list_databases()
databases = databases_response.get("data", {}).get("databases", [])
except Exception as e:
print(f"Error fetching databases: {e}")
return
print(f"✓ Found {len(databases)} databases across all jurisdictions\n")
# Group databases by jurisdiction for display
jurisdictions = {}
for db in databases:
jurisdiction = db.get("jurisdiction", "Unknown")
if jurisdiction not in jurisdictions:
jurisdictions[jurisdiction] = []
jurisdictions[jurisdiction].append(db)
# Display first few jurisdictions
print("Available Jurisdictions:")
for i, (jurisdiction, dbs) in enumerate(list(jurisdictions.items())[:3]):
print(f" • {jurisdiction}: {len(dbs)} database(s)")
# Find the High Court of Australia database as an example
print("\n[Step 2] Searching for High Court of Australia database...")
hca_db = None
for db in databases:
if "High Court of Australia" in db.get("database_name", ""):
hca_db = db
break
if not hca_db:
print("High Court of Australia database not found. Using first available database.")
hca_db = databases[0] if databases else None
if not hca_db:
print("✗ No databases found!")
return
print(f"✓ Selected: {hca_db['database_name']}")
print(f" Jurisdiction: {hca_db['jurisdiction']}")
print(f" Category: {hca_db['category']}")
# Step 3: Get available years for the selected database
db_path = hca_db["path"]
print(f"\n[Step 3] Fetching available years for: {hca_db['database_name']}...")
try:
years_response = client.get_database_years(path=db_path)
years = years_response.get("data", {}).get("years", [])
except Exception as e:
print(f"Error fetching years: {e}")
return
print(f"✓ Found {len(years)} years with available cases")
print(f" Years available: {', '.join(years[:5])}{'...' if len(years) > 5 else ''}")
# Step 4: List cases for the most recent year
if years:
recent_year = years[0]
print(f"\n[Step 4] Listing cases from {recent_year}...")
try:
cases_response = client.list_cases(path=db_path, year=recent_year)
cases = cases_response.get("data", {}).get("cases", [])
total_count = cases_response.get("data", {}).get("count", 0)
except Exception as e:
print(f"Error fetching cases: {e}")
return
print(f"✓ Found {total_count} cases in {recent_year}")
# Step 5: Get detailed information for multiple cases
print(f"\n[Step 5] Retrieving details for the first {min(3, len(cases))} cases...")
print("-" * 90)
for i, case in enumerate(cases[:3], 1):
try:
case_details = client.get_case(url=case["url"])
case_data = case_details.get("data", {})
print(f"\nCase {i}:")
print(f" Title: {case_data.get('title', 'N/A')[:80]}")
downloads = case_data.get("downloads", {})
priority_url = case_data.get("priority_download_url", "N/A")
print(f" Priority Download Format: {priority_url.split('.')[-1].upper() if priority_url != 'N/A' else 'N/A'}")
available_formats = [fmt.replace('_', ' ').title() for fmt in downloads.keys() if downloads[fmt]]
print(f" Available Formats: {', '.join(available_formats)}")
except Exception as e:
print(f"\n ✗ Error retrieving case details: {e}")
print("\n" + "-" * 90)
else:
print("✗ No years found for this database.")
print("\n" + "=" * 90)
print("✓ Workflow completed successfully!")
print("=" * 90)
if __name__ == "__main__":
main()Lists all available legal databases on AustLII organized by jurisdiction and category. Returns database names, URLs, and path identifiers that can be used with other endpoints.
No input parameters required.
{
"type": "object",
"fields": {
"databases": "array of objects with jurisdiction, category, database_name, url, and path"
},
"sample": {
"data": {
"databases": [
{
"url": "https://www.austlii.edu.au/cgi-bin/viewdb/au/cases/cth/HCA/",
"path": "cgi-bin/viewdb/au/cases/cth/HCA",
"category": "Cth Case law",
"jurisdiction": "Commonwealth of Australia",
"database_name": "High Court of Australia 1903-"
}
]
},
"status": "success"
}
}About the Edu API
Database Discovery and Navigation
The list_databases endpoint requires no parameters and returns an array of database objects, each carrying a jurisdiction, category, database_name, url, and path. The path field (e.g. cgi-bin/viewdb/au/cases/cth/HCA) is the key input for every subsequent endpoint. get_database_years accepts that path and returns the available years for that court database in descending order, letting you confirm temporal coverage before fetching case lists.
Listing and Filtering Cases
list_cases accepts the same path parameter and an optional year string (four digits, e.g. 2024). When year is omitted, the endpoint returns the database's default listing. The response includes cases — an array of objects with title and url — plus a count integer so you know how many results came back without iterating the array. Supplying a year filter narrows results to decisions from that calendar year.
Case Document Retrieval
get_case accepts the url from a list_cases result and returns structured metadata: the title, a downloads object keyed by format (rtf, pdf, eco_friendly_print) each mapping to its download URL, the source_url that was queried, and a priority_download_url that resolves to the best available format — RTF first, then eco-friendly print, then PDF. This means your application can always request priority_download_url and receive a usable link regardless of which formats a given case exposes.
The Edu API is a managed, monitored endpoint for austlii.edu.au — not a raw scraper you maintain. Every endpoint is automatically health-checked on a schedule, and when austlii.edu.au changes and a check fails, the API is automatically queued for repair and re-verified. It is built to keep working as the site underneath it changes.
This isn't an official austlii.edu.au API — it's an independent, maintained REST wrapper over public data. Where the source has no official API (or only a limited one), Parse gives you a stable contract over a source that never promised one, and keeps it current. Need a new endpoint or field? You can revise it yourself in plain English and the agent rebuilds it against the live site in minutes — contributing the change back to the shared API is free.
Will this API break when the source site changes?+
Is this an official API from the source site?+
Can I fix or extend this API myself if I need a new endpoint or field?+
What happens if I call an endpoint that has an issue?+
- Building a case law archive that mirrors AustLII databases by jurisdiction using
list_databasespath identifiers - Tracking new High Court decisions by year using
get_database_yearsandlist_caseswith a year filter - Automating bulk RTF downloads of court decisions via
priority_download_urlfor offline legal research - Generating a structured catalogue of Australian federal and state court decisions with titles and URLs
- Feeding case document URLs into a document analysis pipeline using
get_casedownload links - Monitoring a specific database's annual case volume by comparing
countvalues returned bylist_casesacross years
| Tier | Price | Credits/month | Rate limit |
|---|---|---|---|
| Free | $0/mo | 100 | 5 req/min |
| Hobby | $30/mo | 1,000 | 20 req/min |
| Developer | $100/mo | 5,000 | 100 req/min |
One credit = one API call regardless of which marketplace API you call. Exceeding the rate limit returns a 429 response. Authenticate with the X-API-Key header.
Does AustLII provide an official developer API?+
What does `get_case` return and how does format priority work?+
title, a downloads object with up to three keys — rtf, pdf, and eco_friendly_print — each pointing to a direct download URL, plus a priority_download_url that resolves to RTF if available, then eco-friendly print, then PDF. Not every case exposes all three formats; priority_download_url always surfaces the best available one.Does the API support full-text search across AustLII case content?+
Are non-case legal materials like legislation or journals available?+
list_databases returns all databases organised by category, which may include legislation and journal databases alongside case law. However, list_cases and get_case are oriented toward case documents; response fields like title and downloads assume a case document structure. Non-case material may not expose the same download formats. You can fork the API on Parse and revise to handle legislation-specific document structures.Does `list_cases` paginate results for large databases?+
count field alongside the cases array, but the current response shape does not include pagination parameters like page or offset. For databases with large case volumes this means results may be capped at the source's default listing size. You can fork the API on Parse and revise to add pagination support.