SCTY Lab

One search across policy sources.

civic searches bills, rules, dockets, court opinions and state legislation in one call. Every record says where it came from: the source, the request, the HTTP status, the time and a content hash.

View on GitHub ↗

The output is the record

civic has no database, no cache and no state. Each run returns one envelope: a run line with every call it made and every error, then one line per record. Save it as JSON or JSONL and it is a complete account of that search. Add --raw-dir to keep each raw response as well.

{
  "type": "record",
  "source": "federal_register",
  "title": "Medicare Program; FY 2027 Hospice Wage Index…",
  "published_at": "2026-09-30",
  "movement": "final_rule",
  "provenance": {
    "endpoint": "https://www.federalregister.gov/api/v1/documents.json",
    "params": {"conditions[term]": "respite care", "per_page": 2},
    "http_status": 200,
    "retrieved_at": "2026-11-15T16:14:13Z",
    "terms": "U.S. government work, public domain (17 U.S.C. 105)",
    "item_sha256": "sha256:a78ff93c…"
  }
}
One record from the quickstart below, shortened. Keys never appear in the saved parameters.

Quickstart

Two dependencies, httpx and python-dotenv. No model calls.

uv tool install git+https://github.com/SCTY-Inc/civic-cli
civic doctor
civic search "respite care" --sources federal_register --format jsonl

The Federal Register needs no key. Most other sources need a free API key: GovInfo, Regulations.gov, Open States, LegiScan and Exa. Put them in your environment or in a .env file in the folder you run from. civic doctor shows which sources can run. In our test on 15 November 2026 without keys, only the Federal Register answered. CourtListener returned 401, Semantic Scholar returned 429, and Census did not return JSON.

Why

If an agent tells you a bill moved, can you check where it read that?

civic used to do more. It had a Gemini pipeline that researched, wrote and reviewed reports, with presets, a compare mode and a cache. In October 2026 all of that came out, and the code went from 2,744 lines to 1,176. What stayed is the part a caller cannot rebuild later: adapters for each source and a record that carries its own provenance.

A source that fails does not stop the search. Its error goes into errors[] next to the records from sources that answered. Our CourtListener test shows why this matters. civic doctor lists CourtListener as ready because it needs no key, but the service now refuses requests without a token. The run still finished and recorded the 401.

If you delete it tomorrow, you lose nothing

civic keeps nothing. The envelopes and raw responses you saved are ordinary JSON files, and each record links to its official source. The data stays under each provider's terms.