Code examples.
Common tasks against the live API. Create a key at app.data.co and run them as written; LAW.co record endpoints return live records, and field names follow the API reference.
Find datasets you can actually use
Filter the catalog by channel so your code only targets datasets offered over REST, then check each dataset's access status and the entitled flag for your plan.
curl -G https://api.data.co/v1/datasets \
-H "Authorization: Bearer $DATACO_API_KEY" \
--data-urlencode "access=rest" \
--data-urlencode "category=legal"Illustrative example · final API surface documented in /docs
Paginate a filtered search
Cursors are opaque and bound to the query that produced them. Reusing a cursor with different filters returns 400 invalid_params, so keep the filters fixed while paging.
import os, requests
URL = "https://api.data.co/v1/datasets/lawco-court-opinions/records"
H = {"Authorization": f"Bearer {os.environ['DATACO_API_KEY']}"}
params = {"q": "breach of contract", "date_from": "2015-01-01", "limit": 50}
while True:
env = requests.get(URL, headers=H, params=params, timeout=15).json()
if env["error"]:
raise RuntimeError(env["error"]["code"])
for rec in env["data"]:
handle(rec)
if not env["meta"]["next_cursor"]:
break
params["cursor"] = env["meta"]["next_cursor"]Illustrative example · final API surface documented in /docs
Fetch a record and keep provenance
A single opinion returns its text, metadata and provenance, plus AI enrichment only if your plan is entitled to it. Store the provenance with anything you persist.
curl https://api.data.co/v1/legal/cases/lawco:case:<id> \
-H "Authorization: Bearer $DATACO_API_KEY"Illustrative example · final API surface documented in /docs
Call an MCP tool
The MCP server at https://api.data.co/mcp is stateless Streamable HTTP. A tool call is a JSON-RPC request; the result is citation-ready, with license and access_restrictions listed so the agent knows what it may quote.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search_case_law",
"arguments": { "query": "adverse possession", "date_from": "2010-01-01", "limit": 5 }
}
}Illustrative example · final API surface documented in /docs
Habits the examples follow
- Check error before reading data
- Read meta.next_cursor, not a top-level cursor
- Treat meta.demo as a signal to label or refuse
- Show meta.warnings to developers, not end users
- Never retry rights or entitlement errors
- Keep request ids in logs
From question to cited answer, step by step
A typical legal research feature strings these calls together. First, discover: call GET /v1/datasets with category=legal and access=rest, and keep only datasets whose rest channel is available and whose entitled flag is true for your plan. Cache this list; catalog reads are not metered and change rarely.
Second, search: send the user's question to POST /v1/legal/search with whatever filters the user supplied — jurisdiction, court, date_from and date_to as YYYY-MM-DD. Show meta.warnings to your developers in logs, because they explain provider limits such as sampled search. If meta.demo is true, badge the results as demo in your interface.
Third, fetch: when the user opens a result, call GET /v1/legal/cases/{id} for the full opinion text, metadata and provenance. Enrichment is included only if your plan is entitled to it, and it carries its own provenance with isDerived set. Display it apart from the opinion text and label it as AI-generated.
Fourth, cite and record: show the citation, court, date and canonical URL from the record, and store the record id, licenseId and the response's request_id with anything you save. That gives you a citation trail for users and a reconciliation trail for usage.
Which errors to retry
| Code | HTTP | Retry? | What to do |
|---|---|---|---|
| rate_limited | 429 | Yes | Wait for Retry-After, then retry the same request. |
| quota_exceeded | 429 | No | Monthly quota is used up; pause or change plan. |
| upstream | 502 | Yes, with backoff | The provider failed; retry a few times, then surface it. |
| not_configured | 503 | No | The channel is not live yet; show it as unavailable. |
| rights_restricted | 403 | No | The license blocks this channel. |
| missing_scope / not_entitled | 403 | No | Fix the key's scopes or the plan. |
| invalid_params | 400 | No | Fix the input; param names the field. |
| unsupported_operation | 501 | No | The provider cannot do this yet. |
| internal_error | 500 | Once | Quote the request id to support if it repeats. |
Questions buyers ask
Are these examples runnable today?
Yes. Create a key at app.data.co and run them against production. The dataset pages also offer a preview over synthetic demo records.
Why does search return different results each time?
LAW.co's current upstream keyword search randomly samples up to 50 results. A meta.warnings entry says so. A deterministic, paginated provider endpoint has been proposed.
Where are the MCP tool schemas?
On the MCP page, and from the server itself via tools/list. They mirror the REST operations.
Do I need the full record for search results?
No. Search results carry metadata, a snippet and provenance, but not the full text. Fetch a single record by id when the user needs the opinion itself.
API and MCP access is live.
Sign up at app.data.co, create an organization and issue a scoped key for REST and MCP.