API documentation
Read-only access to aggregated U.S. state legislative data, available two ways: a REST API and an MCP server for AI agents. Same data, same shapes. REST base URL https://staging.capitolmath.ai/v1; full machine-readable spec at /v1/openapi.json.
Overview
Authentication
Every request needs an API key, sent as a bearer token. Keys look like vd_live_… and are shown once at creation. Only members with a Full license create keys — Owners and Admins included — on their Profile page, and a member's keys are revoked if their license is changed to Basic. The organization's Owners and Admins can view and revoke every key under Organization (see Roles and licenses). No account yet? Register — the free Browse plan includes API access.
curl -H "Authorization: Bearer vd_live_…" "https://staging.capitolmath.ai/v1/bill-search?location=TX&outcome=passed"
Chambers
Chambers are normalized across states to upper (senate) and lower (house / assembly). Bills, committees and legislator detail carry both the raw source label (chamber) and the normalized origin_chamber; filter bills with ?origin-chamber=upper|lower. /official-search returns and filters on branch instead (upper-chamber | lower-chamber | executive). Bill outcomes use outcome (in_progress, passed, vetoed, dead).
Pagination
List endpoints are page-based. Pass ?page= (1-based) and follow pagination.next_url — an absolute URL — to the next page. When next_url is null, you have reached the last page. Page size defaults to your plan’s maximum page size — 25 on Browse, 100 on paid plans; request a smaller page with ?page-size= (larger values are clamped to that maximum) to keep responses light — handy for MCP or bandwidth-constrained clients.
{
"pagination": {
"page": 1, "page_size": 100, "returned_count": 100,
"total_count": 2222, "total_pages": 23,
"next_url": "https://staging.capitolmath.ai/v1/bill-search?location=TX&page=2", "prev_url": null
},
"data": [ /* records */ ]
}Rate limits & quota
Limits depend on your plan and are shared by every key in the organization:
- Browse (free) — 5,000 calls / month at 30 / minute
- State — 25,000 calls / month at 60 / minute
- Developer — 150,000 calls / month at 120 / minute
- Pro — 150,000 calls / month at 120 / minute
- Scale — 500,000 calls / month at 200 / minute
Months are UTC calendar months. Authenticated responses carry X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (epoch seconds), plus X-Quota-Limit, X-Quota-Remaining and X-Quota-Period (YYYY-MM); an unlimited quota sends X-Quota-Limit: unlimited. Exceeding either returns 429: rate_limited (with a Retry-After header and error.retry_after) or quota_exceeded. Every request served counts toward the quota, including ones that fail with a 4xx; requests refused for being over it don't. The organization's Owners and Admins, and everyone holding an active key in it, are emailed when usage reaches 80% of the quota and again at 100%.
State plan
The State plan covers one state, which the organization's Owner chooses when subscribing (and can change on the Organization page). Its keys are held to that state:
- A request naming another jurisdiction — a
locationparameter or path segment, includingUS— is refused withforbidden. Wherelocationis optional, leaving it out means the covered state. - A legislator, committee, event or executive from another jurisdiction is refused the same way.
- Saved-view searches return the view's results within the covered state.
related-text, which compares a bill across jurisdictions, isn't available.
CSV exports from the site follow the same rule.
Errors
Errors use a consistent envelope and standard status codes:
{ "error": { "code": "unauthorized", "message": "Invalid API key." } }invalid_request— 400: a missing, malformed or retired parameterunauthorized— 401: missing or invalid API keyforbidden— 403: the organization is suspended or inactive, or the request is outside the state a State plan covers (the response names it incovered_location)not_found— 404conflict— 409: stopping a watch on a bill the key's owner owns a tracked item forrate_limited— 429quota_exceeded— 429internal— 500
Some errors add fields beside code and message — retry_after on rate_limited, covered (the jurisdictions with a loaded calendar) on /event-search.
REST Endpoints
{location} is a two-letter jurisdiction code — a USPS state code (e.g. TX), or US for Congress, which is why it is not called state. Topics use short, lowercase labels: taxes, budget, affordability, crime, immigration, ai, climate, housing, healthcare, agriculture, mining, traffic, wages, education, elections, defense, foreign-policy, government, finance, public-lands, technology, civil-rights, other. Hit Try it live on any endpoint to run it against real data.
Search bills
/v1/bill-searchSearch bills across one or more jurisdictions, newest session first. `location` takes two-letter codes — a USPS state code, or US for Congress, which is why it is not called `state`. Combine any of the filters below. topic values are lowercase and case-sensitive. The furthest_stage a row returns carries a chamber suffix on floor votes (floor_vote:upper, floor_vote:lower, floor_vote:none); the furthest-stage parameter takes the plain stage, floor_vote. effective gives an enacted bill's next and last effective dates and how its main provisions' date was worked out (basis: published, stated, derived, contingent or unresolved); it is null where effective dates aren't live yet or the bill has none. takes-effect-window (last_30, last_90, last_365, next_30, next_90, this_year) or takes-effect-from / takes-effect-to keep only bills with an effective date in that window, counted in each jurisdiction's own time; effective.matched_date says which date matched. effective-status filters by basis. These filters answer a 400 that names the live jurisdictions when the request names none of them. Each row's tracking lists your organization's tracked items for the bill as the key's creator sees them, one per client that tracks it and one for your own work, each with its position, priority, status, owner and tags; it is null when the key's creator can't read tracked items. tracked=true keeps only the bills you track, tracked=false only those you don't.
https://staging.capitolmath.ai/v1/bill-search?location=TX&outcome=passed&topic=healthcareParameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| location * | string | TX or [TX,US] |
| page | number | 1 |
| page-size | number | 25 |
| session | string | 2025, FL:2026F, or [2025,FL:2026F] |
| outcome | string | passed or [passed,vetoed] |
| topic | string | healthcare or [crime,housing] |
| origin-chamber | string | upper or lower |
| is-resolution | string | y |
| furthest-stage | string | in_committee |
| currently-with | string | upper-floor, or a committee name; repeat the key for several |
| committee | string | cmte-US-hsag00 |
| committee-action | string | under_review |
| economic-lean-min | number | 2 |
| economic-lean-max | number | 4 |
| social-lean-min | number | 2 |
| social-lean-max | number | 4 |
| text-like | string | eminent domain |
| sponsor-name-like | string | Hughes |
| last-updated-after-date | string | 2026-08-01 |
| last-updated-before-date | string | 2026-08-25 |
| last-updated-within-days | number | 30 |
| takes-effect-window | string | next_90 |
| takes-effect-from | string | 2027-01-01 |
| takes-effect-to | string | 2027-01-31 |
| effective-status | string | derived |
| tracked | boolean | true |
Response
{
"pagination": {
"page": 1,
"page_size": 100,
"returned_count": 100,
"total_count": 347,
"total_pages": 4,
"next_url": "https://staging.capitolmath.ai/v1/bill-search?location=TX&outcome=passed&topic=healthcare&page=2",
"prev_url": null
},
"data": [
{
"session": 2026,
"session_key": "TX:2026",
"bill_number": "HB 18",
"bill_name": "Relating to the establishment and administration of certain programs and services providing health care services to rural counties.",
"key_words": "Health Care Providers; Health--Rural; Hospitals; …",
"topics": ["healthcare"],
"chamber": "House",
"origin_chamber": "lower",
"outcome": "Passed",
"furthest_stage": "executive_signature",
"is_resolution": false,
"summary": "The bill author has informed the committee of continuing concerns regarding the provision of health care services in rural counties …",
"location": "TX",
"location_name": "Texas",
"currently_with": "Governor / Executive",
"current_committee": null,
"economic_score": 4.1,
"social_score": 3.2,
"last_updated": null,
"bill_sponsors": [
{
"name": "Gary VanDeaver",
"legislator_id": "cmqvegasa00afsffx…",
"photo_url": "https://cdn.capitolmath.ai/…",
"legislator_url": "https://staging.capitolmath.ai/v1/legislator-detail/cmqvegasa00afsffx…"
}
],
"effective": null,
"detail_url": "https://staging.capitolmath.ai/v1/bill-detail/TX/2026/HB%2018",
"tracking": [
{ "id": "ti_3c7a19e2b0", "client": null, "position": "support", "priority": "high", "status": { "id": "st_engaged", "name": "Engaged", "closed": false }, "owner": { "id": "user_dana", "name": "Dana Ortiz" }, "tags": [{ "id": "tag_rural", "name": "Rural health" }] }
]
}
]
}Search with your saved views
/v1/saved-view-searchRuns one or more of the saved views built in the legislation browser and returns the bills they match. A key sees the saved views its creator sees in the app — their own and those shared with them — and no others. Use it to reuse a view saved in the app by name instead of re-expressing it as bill-search parameters; the view is read at request time, so editing it in the app changes what this returns. A saved view carries its own jurisdiction(s) and facet selection, which is why this is its own endpoint rather than a bill-search parameter — the two would disagree over which location and facets win. `kind=bill` takes only the Legislation page's views and `kind=tracked` only Tracked items views (the bills their items are on); left out, a name may be either, and a Legislation view wins over a Tracked items view of the same name. Naming several views returns bills matching ANY of them; an unknown name is rejected with 400 rather than quietly returning nothing. Results come in the view's saved order: a view saved on the Legislation page as a table sorted by a column returns in that sort; otherwise newest session first. With several names, the first that records a sort decides. A Tracked items view works here too, standing for the bills of the items it lists for the key's creator; /v1/tracked-items?saved-view= returns the items themselves. Not available in the live playground below, which isn't tied to an organization. Saved views were called saved filters until the rename: /v1/saved-filter-search and the saved-filter parameter still work.
https://staging.capitolmath.ai/v1/saved-view-search?saved-view=Datacenter%20newParameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| saved-view * | string | Datacenter new or [A,B] |
| kind | string | tracked |
| page | number | 1 |
| page-size | number | 25 |
Response
{
"pagination": {
"page": 1,
"page_size": 100,
"returned_count": 2,
"total_count": 2,
"total_pages": 1,
"next_url": null,
"prev_url": null
},
"data": [
{
"session": 2026,
"bill_number": "AB 2713",
"bill_name": "California AI Transparency Act: system provenance data.",
"topics": ["ai"],
"chamber": "CA Assembly",
"origin_chamber": "lower",
"outcome": "Dead",
"furthest_stage": "in_committee",
"is_resolution": false
}
]
}Get a bill
/v1/bill-detail/{location}/{year}/{number}Full detail for one bill: summary, sponsors, derived scores, per-chamber floor history, and the most-recent roll call. Pass include-bill-text=latest (or history for every printing) to also return the legislative text (omitted by default — it can be multi-megabyte). URL-encode spaces in the bill number. furthest_stage carries a chamber suffix on floor votes (floor_vote:upper, floor_vote:lower, floor_vote:none), while bill-search's furthest-stage parameter takes the plain floor_vote. placeholder is true while the bill only states an intent to legislate and changes no law (California's "It is the intent of the Legislature to enact legislation relating to…"); its political lean and topics are then null, and no whip projection is made. has_whip_count tells you whether /v1/whip-count will return a projection for this bill without your having to call it and handle a 404: it is true only when the bill still faces a floor vote AND a model has been fitted for that chamber-session, and that chamber's whip counts aren't withdrawn. whip_count_withdrawn is null unless they are: then it gives the date they were withdrawn and their panel on the accuracy scoreboard. effective is when the bill's law applies, where effective dates are live (null elsewhere, as for this Texas bill, and for a bill with none): act is the main provisions' date and provisions the sections with dates of their own, each with its resolution and the basis it was worked out from — the state's published date, the enacted text's clause (quoted), or the constitutional or statutory rule applied step by step to the signing or adjournment. next_effective, last_effective and today are counted in the jurisdiction's own time. amendments lists the bill's amendments, each with its sponsors, its outcome as the legislature's record states it, where it was decided, its roll call and the legislature's own statement of its effect. Amendments are released one legislature at a time: amendments is null where the bill's legislature isn't released yet. amendments.coverage says how the session's amendments were recorded (the legislature's own listing, or read from the bill's actions), whether only part of a listing was read, and limits: what the list or its outcomes leave out or may get wrong. recommended means a committee's report names the amendment and its chamber hasn't decided it; it is never adopted.
https://staging.capitolmath.ai/v1/bill-detail/TX/2026/HB%2018Parameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| location * | string | TX |
| year * | number | 2026 |
| number * | string | HB 18 |
| include-bill-text | string | latest or history |
Response
{
"data": {
"session": 2026,
"bill_number": "HB 18",
"bill_name": "Relating to the establishment and administration of certain programs and services providing health care services to rural counties.",
"topics": ["healthcare"],
"chamber": "House",
"origin_chamber": "lower",
"outcome": "Passed",
"furthest_stage": "executive_signature",
"is_resolution": false,
"summary": "The bill author has informed the committee of continuing concerns regarding the provision of health care services in rural counties …",
"location": "TX",
"location_name": "Texas",
"currently_with": "Governor / Executive",
"bill_text": [
{ "version_label": "Enrolled", "version_date": "2025-05-20", "is_latest": true,
"text": "AN ACT relating to … (bill_text is only present when include-bill-text is set)" }
],
"bipartisan_index": 72.06,
"popularity_index": 83.33,
"economic_score": 4.1,
"social_score": 3.2,
"velocity": 1.01,
"bill_sponsors": [
{
"name": "Gary VanDeaver",
"chamber": "House",
"origin_chamber": "lower",
"party": "R",
"legislator_id": "cmqvegasa00afsffx…",
"legislator_url": "https://staging.capitolmath.ai/v1/legislator-detail/cmqvegasa00afsffx…"
}
],
"current_committee": null,
"floor_vote_history": [
{
"chamber": "House",
"origin_chamber": "lower",
"timestamp": "2025-05-28T07:00:00.000Z",
"most_recent": true,
"result": "pass"
}
],
"last_roll_call": [
{
"name": "Daniel Alders",
"party": "R",
"chamber": "House",
"origin_chamber": "lower",
"timestamp": "2025-05-28T07:00:00.000Z",
"vote": "yes",
"legislator_id": "cmqvef2o80000sffx…",
"legislator_url": "https://staging.capitolmath.ai/v1/legislator-detail/cmqvef2o80000sffx…"
}
],
"has_whip_count": false,
"whip_count_withdrawn": null,
"placeholder": false,
"effective": null
}
}Accuracy scoreboard (public)
/v1/accuracyPublic, with no key: how accurate whip counts have been, as the scoreboard at /accuracy publishes it. Every whip count is recorded before anyone sees it and scored against the official roll call; the scoreboard is published on a fixed schedule whatever it says, under the methodology at /accuracy/methodology. Returns the latest public publication, or a past one with publication=YYYY-MM-DD. Each published chamber has its figures at each lead time: the margin over the party-line baseline (each member given their party's projected position) with its 95% interval, uncertain-band and overall accuracy, both baselines, the Brier score, counts and resolution rate, per model version, and shown vs never shown. whip_counts is withdrawn, with withdrawn_since, for a chamber below its baseline. Chambers not yet published are listed by name only. 404 before the first publication. /accuracy.json returns the latest without filters. publication.dataset links every prediction of the published chambers with its outcome (GET /v1/accuracy/dataset, gzipped JSON Lines, CC BY 4.0, with its sha256), including predictions never shown; publication.ledger_anchor_url is the prediction ledger's proof in the daily public audit anchor (GET /v1/accuracy/anchor).
https://staging.capitolmath.ai/v1/accuracy?state=NY&lead=7dParameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| publication | string | 2027-04-12 |
| state | string | NY |
| chamber | string | upper or lower |
| session | number | 2027 |
| lead | string | 7d (or 30d, 90d, at_introduction) |
Response
{
"data": {
"feature": "whip_count",
"publication": { "date": "2027-04-12", "published_at": "2027-04-12T12:45:00.000Z", "published_late": false, "methodology_version": "cal-1.0", "methodology_url": "https://staging.capitolmath.ai/accuracy/methodology/cal-1.0", "ledger_head": { "seq": 1204331, "hash": "9c1f…" },
"ledger_anchor_url": "https://staging.capitolmath.ai/v1/accuracy/anchor",
"dataset": { "url": "https://staging.capitolmath.ai/v1/accuracy/dataset?publication=2027-04-12", "format": "jsonl.gz", "rows": 48210, "bytes": 5210443, "sha256": "41aa…", "license": "CC BY 4.0", "license_url": "https://creativecommons.org/licenses/by/4.0/" } },
"claim": { "published": 1, "above": 1, "below": 0, "inconclusive": 0, "insufficient": 0 },
"minimum_resolved_chamber_votes": 20,
"chambers": [
{
"state": "NY", "chamber": "upper", "session": 2027, "name": "New York Senate",
"whip_counts": "offered", "withdrawn_since": null, "verdict": "above_baseline",
"lead_times": {
"7d": {
"insufficient_data": null, "verdict": "above_baseline",
"counts": { "issued": 8812, "resolved": 1952, "unresolvable": 140, "void": 12, "pending": 6708 }, "resolution_rate": 0.93,
"resolved_chamber_votes": 31,
"tested": { "figure": "uncertain_band", "margin": 0.006, "margin_ci": [-0.001, 0.013], "chamber_votes": 31 },
"accuracy": 0.871, "accuracy_ci": [0.86, 0.882], "uncertain_accuracy": 0.674, "uncertain_accuracy_ci": [0.651, 0.697], "uncertain_n": 512,
"baselines": { "party_line": 0.866, "member_habit": 0.851 }, "vs_party_line": 0.005, "vs_party_line_ci": [0.001, 0.009],
"brier": 0.094, "log_loss": 0.31, "model_versions": ["ip-2027.u.p1.20270208"]
}
}
}
],
"not_yet_published": [{ "state": "CA", "chamber": "lower", "session": 2028, "name": "California Assembly" }],
"five_worst": [{ "state": "NY", "chamber": "upper", "session": 2027, "name": "New York Senate", "tested_margin": 0.006 }],
"refusal": { "eligible": 24, "refused": 8, "rate": 0.333 }
}
}Whip count (vote projection)
/v1/whip-count/{location}/{year}/{number}A forecast, not a record: where each seated member is projected to land on a bill that has not yet had its floor vote, uncertain members first. Four limits shape how to read it. The projection is for the PASSAGE motion specifically — concurrence and urgency-clause votes are different questions with different coalitions. It is valid only conditional on the bill reaching that vote, since floor votes exist only for bills leadership let onto the floor. Only the ideological dimension is modeled, so accuracy is weaker on close cross-cutting votes than the headline suggests. And about three quarters of floor votes are near-unanimous, which the model is not fitted on — contested.likely_uncontested flags those. Lead with the top-level confidence object. Its headline is members_in_play — how many members the model cannot call — not an accuracy percentage: measured reliability runs about 99% at high margin and 46–49% for members near the cutpoint, so a pooled figure reads highest exactly where the projection is least useful. confidence.state is no_member_read (half or more of the members cannot be called, so the tally cannot indicate an outcome — this takes priority over the others), not_expected_close (predicted lopsided: near-certain, says little), working_count (contested, undecided members in bands measured at 80% or better), or coin_flip (the deciding members sit where this model has been right about half the time — read the tally as a range). Check confidence.differs_from_usual before acting on a projection: it counts the members projected against their own most common vote, and it is the only part of the response the bill is driving. Across every fitted chamber a projection matches a per-member constant — predict each member's usual vote and ignore the bill — to within 0.2 points; California's Senate model scores 98.4% against a 98.4% member-modal baseline. When matches_member_modal is true the projection reproduces habitual voting and says nothing bill-specific, and member_modal_note states that in the response. Per-member confidence is read off the published model.reliability curve (observed accuracy on held-out roll calls at that margin), so it can be checked rather than trusted. model.accuracy_url is the chamber's panel on the public accuracy scoreboard: how accurate its whip counts have been on real votes, against the party line. Early in a session, until the chamber has had the 20 contested votes its own model needs, the previous session's model is carried forward: carried says so, and how many newly seated members it can't project. When the chamber has already taken the motion the response is status: "already_voted" with the recorded roll call and no members array. A placeholder bill — one that only states an intent to legislate — returns status: "placeholder" with a note and no members array. A chamber whose whip counts did worse than their baseline on the public accuracy scoreboard returns status: "withdrawn", with the date and the scoreboard link in withdrawn, and no members array; they return after doing better at two publications. Not available in the live playground, which cannot fit models on demand.
https://staging.capitolmath.ai/v1/whip-count/CA/2026/AB%201900?chamber=lowerParameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| location * | string | CA |
| year * | number | 2026 |
| number * | string | AB 1900 |
| chamber | string | upper or lower (default upper) |
Response
{
"data": {
"bill": { "location": "CA", "year": 2026, "number": "AB 1900", "name": "Guaranteed Health Care for All." },
"chamber": "lower",
"motion": "passage",
"threshold": 40,
"projected": { "yes": 58, "no": 19, "uncertain": 2 },
"conditional_on": "bill reaching a passage vote on the Assembly floor",
"contested": { "probability": 1, "base_rate": 0.22, "likely_uncontested": false },
"confidence": {
"state": "coin_flip",
"label": "Coin flip",
"members_in_play": 2,
"in_play_accuracy": 0.5,
"explanation": "The members who decide this one sit where this model has been right about half the time. Read the tally as a range, not a number.",
"differs_from_usual": 3,
"matches_member_modal": false
},
"members": [
{ "legislator_id": "…", "name": "Jasmeet Kaur Bains", "party": "D", "district": "35",
"projection": "yes", "confidence": 0.51, "band": "uncertain" }
],
"model": {
"version": "ip-2026.l.20260904", "accuracy_url": "https://staging.capitolmath.ai/accuracy#ca-lower", "trained_through": "2026-08-28",
"contested_vote_threshold": 0.9, "holdout_accuracy": 0.897, "baseline_accuracy": 0.797,
"reliability": [ { "lo": 0, "hi": 0.2, "n": 3053, "accuracy": 0.513 } ]
}
}
}Search committees
/v1/committee-search/{location}A legislature's committees, with the roster size for a session and how many bills have been referred to each. The jurisdiction is in the path — a committee belongs to exactly one legislature — and US reaches Congress. Retired committees are excluded by default: a renamed or merged committee is a different committee, kept rather than deleted, so is-active=false is how you reach them.
https://staging.capitolmath.ai/v1/committee-search/US?level=parent&origin-chamber=lowerParameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| location * | string | US or TX |
| page | number | 1 |
| page-size | number | 25 |
| name-like | string | Agriculture |
| chamber | string | House, Senate, Assembly, Joint |
| origin-chamber | string | lower |
| level | string | parent or subcommittee |
| parent-id | string | cmte-US-hsag00 |
| reviews-bills | boolean | true |
| is-active | boolean | false |
| session | number | 2025 |
| member-id | string | a legislator ID |
| changed-since | string | 2026-01-01 |
Response
{
"pagination": { "page": 1, "page_size": 100, "returned_count": 51, "total_count": 51, "total_pages": 1,
"next_url": null, "prev_url": null },
"data": [
{
"committee_id": "cmte-US-hsag00",
"name": "House Agriculture Committee",
"location": "US",
"location_name": "US Federal",
"chamber": "House",
"origin_chamber": "lower",
"parent_committee_id": null,
"parent_committee_url": null,
"subcommittee_count": 6,
"member_count": 53,
"session": 2025,
"reviews_bills": true,
"bills_received": 1104,
"last_action_date": "2026-09-10",
"is_active": true,
"last_updated": "2026-09-14T19:11:48.045Z",
"committee_url": "https://staging.capitolmath.ai/v1/committee-detail/cmte-US-hsag00"
}
]
}Committee detail
/v1/committee-detail/{id}One committee: its roster for a session, its subcommittees, and six counts of the bills that have passed through it — each with the bill-search URL that returns exactly those bills. The five counts below received partition it, in precedence order. A parent's counts follow bills into its subcommittees (held_directly is the share at its own table), holding_tracked is false where the jurisdiction never reports which committee holds a bill, and sessions_covered says which years the counts span — they are not narrowed by session, which selects the roster only.
https://staging.capitolmath.ai/v1/committee-detail/cmte-US-hsag00Parameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| id * | string | cmte-US-hsag00 |
| session | number | 2025 |
Response
{
"data": {
"committee_id": "cmte-US-hsag00",
"name": "House Agriculture Committee",
"location": "US",
"location_name": "US Federal",
"chamber": "House",
"origin_chamber": "lower",
"source_code": "hsag00",
"description": "The House Committee on Agriculture has legislative jurisdiction over agriculture, food, rural development, and forestry.",
"official_url": "https://agriculture.house.gov/",
"is_active": true,
"parent_committee_id": null,
"parent_committee_name": null,
"parent_committee_url": null,
"sessions": [2025],
"session": 2025,
"members": [
{
"legislator_id": "cmqu1kaa500gosfq42a8ep161",
"name": "Glenn Thompson",
"party": "R",
"role": "Chair",
"district": "15",
"legislator_url": "https://staging.capitolmath.ai/v1/legislator-detail/cmqu1kaa500gosfq42a8ep161"
}
],
"subcommittees": [
{
"committee_id": "cmte-US-hsag14",
"name": "House Conservation, Research, and Biotechnology Subcommittee",
"member_count": 19,
"bills_received": 256,
"committee_url": "https://staging.capitolmath.ai/v1/committee-detail/cmte-US-hsag14"
}
],
"bills": {
"received": 1104,
"approved": 30,
"under_review": 473,
"tabled": 546,
"referred_elsewhere": 28,
"unrecorded": 27,
"held_directly": { "under_review": 231, "tabled": 87 },
"includes_subcommittees": true,
"holding_tracked": true,
"sessions_covered": { "first": 2023, "last": 2025 },
"search_urls": {
"received": "https://staging.capitolmath.ai/v1/bill-search?location=US&committee=cmte-US-hsag00",
"under_review": "https://staging.capitolmath.ai/v1/bill-search?location=US&committee=cmte-US-hsag00&committee-action=under_review"
}
},
"events_url": "https://staging.capitolmath.ai/v1/event-search?location=US&committee=cmte-US-hsag00",
"last_updated": "2026-09-14T19:11:48.045Z"
}
}Search the calendar
/v1/event-searchWhat is scheduled — hearings, markups, floor sessions and session deadlines. Nothing else here can answer it: the bill history holds no future-dated rows. location is a query parameter rather than a path segment, unlike committee-search: a committee belongs to one legislature but a week does not, and omitting it searches every jurisdiction with a calendar. Read coverage before reading an empty result — a search returning nothing can mean “no meetings match” or “that calendar has never been fetched”, which are opposite answers; a request naming only uncovered jurisdictions is a 400 rather than an empty page. Upcoming by default: without starts-after the floor is today, and starts-before must be later than starts-after (or today, when starts-after is omitted) or the request is a 400. changed_since is accepted as an alias for changed-since.
https://staging.capitolmath.ai/v1/event-search?location=US&has-bills=true&starts-after=2025-01-01Parameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| location | string | US, or [TX,US] |
| page | number | 1 |
| page-size | number | 25 |
| starts-after | string | 2025-01-01 (defaults to today) |
| starts-before | string | 2026-10-01 |
| kind | string | hearing, markup, business_meeting, sine_die |
| calendar | string | house_committee, senate_floor, session |
| status | string | cancelled, postponed, rescheduled |
| committee | string | cmte-US-hsag00 |
| bill-number | string | H.R. 410 (needs location) |
| session | string | 2025, FL:2026F, or [2025,FL:2026F] |
| text-like | string | algal blooms |
| has-bills | boolean | true |
| is-projected | boolean | false |
| changed-since | string | 2026-09-01 |
| order | string | asc or desc |
Response
{
"pagination": { "page": 1, "page_size": 100, "returned_count": 100, "total_count": 383, "total_pages": 4,
"next_url": "…", "prev_url": null },
"coverage": [
{ "location": "US", "location_name": "US Federal", "events": 2702, "session_dates": 1,
"unusable_session_dates": 0, "last_fetched_at": "2026-09-14T19:08:12.000Z" }
],
"data": [
{
"event_id": "cal-US-336587",
"title": "Business meeting to consider S.93 …",
"starts_at": "2025-02-05T15:00:00.000Z",
"ends_at": null,
"all_day": false,
"timezone": "America/New_York",
"local_date": "2025-02-05",
"status": "held",
"kind": "business_meeting",
"calendar": "senate_committee",
"location": "US",
"location_name": "US Federal",
"session": 2025,
"room": "253, Russell Senate Office Building",
"committee_id": "cmte-US-sscm00",
"committee_name": "Sen Commerce, Science, and Transportation Committee",
"committee_url": "https://staging.capitolmath.ai/v1/committee-detail/cmte-US-sscm00",
"is_projected": false,
"bill_count": 17,
"bills": [
{ "bill_number": "S. 161", "bill_name": "She DRIVES Act", "session": 2025, "location": "US",
"detail_url": "https://staging.capitolmath.ai/v1/bill-detail/US/2025/S.%20161" }
],
"source_url": "https://www.congress.gov/event/119th-congress/senate-event/336587",
"last_updated": "2026-09-08T20:08:41.000Z",
"event_url": "https://staging.capitolmath.ai/v1/event-detail/cal-US-336587"
}
]
}Calendar by saved view
/v1/saved-view-event-search“When are the bills on my watchlist being heard?” — the calendar counterpart to saved-view-search. The view selects bills; this returns the meetings those bills are on the agenda for. A separate endpoint rather than a parameter on event-search, for the same reason saved-view-search is separate from bill-search: the view already carries its own jurisdiction and selection, so location, bill-number and text-like are rejected with a 400 rather than silently dropped — a location that disagreed with the view could only ever return an empty page. has-bills=true is accepted and is what the endpoint does anyway; has-bills=false and is-projected are rejected, because every result here has a matching bill by construction. Everything describing the meeting still applies. Upcoming by default: without starts-after the floor is today, and starts-before must be later than starts-after (or today, when starts-after is omitted) or the request is a 400. changed_since is accepted as an alias for changed-since. Naming several views returns meetings matching any of them. Saved views were called saved filters until the rename: /v1/saved-filter-event-search and the saved-filter parameter still work, and through the old path the response also carries saved_filter, the old name of saved_view.
https://staging.capitolmath.ai/v1/saved-view-event-search?saved-view=Datacenter%20new&page-size=25Parameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| saved-view * | string | Datacenter new, or [A,B] |
| page | number | 1 |
| page-size | number | 25 |
| starts-after | string | 2025-01-01 (defaults to today) |
| starts-before | string | 2026-10-01 |
| kind | string | hearing, markup, business_meeting |
| calendar | string | house_committee, senate_floor |
| status | string | cancelled, postponed, rescheduled |
| committee | string | cmte-US-hsag00 |
| session | string | 2025, FL:2026F, or [2025,FL:2026F] (the meeting's session) |
| has-bills | boolean | true (false is rejected here) |
| changed-since | string | 2026-09-01 |
| order | string | asc or desc |
Response
{
"pagination": { "page": 1, "page_size": 25, "returned_count": 25, "total_count": 77, "total_pages": 4,
"next_url": "…", "prev_url": null },
"saved_view": {
"labels": ["Fed health"],
"locations": ["US"],
"bills_on_calendar": 224
},
"coverage": [
{ "location": "US", "location_name": "US Federal", "events": 2702, "session_dates": 2,
"unusable_session_dates": 0, "last_fetched_at": "2026-09-14T22:08:36.875Z" }
],
"data": [
{
"event_id": "cal-US-336587",
"title": "Business meeting to consider S.93 …",
"starts_at": "2025-02-05T15:00:00.000Z",
"local_date": "2025-02-05",
"status": "held",
"kind": "business_meeting",
"calendar": "senate_committee",
"location": "US",
"location_name": "US Federal",
"committee_id": "cmte-US-sscm00",
"committee_name": "Sen Commerce, Science, and Transportation Committee",
"bill_count": 17,
"bills": [
{ "bill_number": "S. 161", "bill_name": "She DRIVES Act", "session": 2025, "location": "US",
"detail_url": "https://staging.capitolmath.ai/v1/bill-detail/US/2025/S.%20161" }
],
"event_url": "https://staging.capitolmath.ai/v1/event-detail/cal-US-336587"
}
]
}Saved calendar views
/v1/saved-calendar-view-searchRuns saved calendar views — the views saved on the Calendar page — and returns their meetings: the calendar counterpart of saved-view-search (bill views) and saved-view-official-search (legislator views). Each view is read as the page reads it: its jurisdictions (All is every one), window, calendars, kinds, agenda filter, search text and its Legislation filter — the saved bill views it includes, read as the meetings whose agendas carry their bills. Not saved-view-event-search, which takes saved bill views. A view keeps its own window unless starts-after or starts-before is given, which then applies to every view named. location, text-like, has-bills, bill-number and is-projected are rejected with a 400, because the view already decides them; everything else describing the meeting narrows every view alike. Naming several views returns meetings matching any of them. saved_view.views says how each one read.
https://staging.capitolmath.ai/v1/saved-calendar-view-search?saved-view=Upcoming%20hearings%20in%20our%20states&page-size=25Parameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| saved-view * | string | Upcoming hearings in our states, or [A,B] |
| page | number | 1 |
| page-size | number | 25 |
| starts-after | string | 2026-10-01 (replaces the views' windows) |
| starts-before | string | 2026-12-31 |
| kind | string | hearing, markup |
| calendar | string | house_committee, senate_committee |
| status | string | cancelled, rescheduled |
| committee | string | a committee id from committee-search |
| session | string | 2026, or a session key from bill-search |
| changed-since | string | 2026-10-01 |
| order | string | asc or desc |
Response
{
"pagination": { "page": 1, "page_size": 25, "returned_count": 2, "total_count": 2, "total_pages": 1,
"next_url": null, "prev_url": null },
"saved_view": {
"labels": ["Upcoming hearings in our states"],
"views": [
{ "label": "Upcoming hearings in our states", "window": "next90",
"locations": ["California", "Colorado", "Massachusetts", "New York", "Virginia"], "bill_views": 0 }
]
},
"coverage": [
{ "location": "CA", "location_name": "California", "events": 2161, "session_dates": 2,
"unusable_session_dates": 0, "last_fetched_at": "2026-09-28T19:10:00.000Z" }
],
"data": [
{
"event_id": "cal-CA-…",
"title": "Housing and Community Development",
"starts_at": "2026-10-21T16:00:00.000Z",
"kind": "hearing",
"calendar": "house_committee",
"location": "CA",
"bill_count": 6,
"event_url": "https://staging.capitolmath.ai/v1/event-detail/cal-CA-…"
}
]
}Event detail
/v1/event-detail/{id}One meeting, with the whole agenda rather than the five-bill preview a list row carries — a markup routinely covers dozens. It also adds the notice's own description, which for many federal meetings is the printed agenda in full, and the supersedes chain, so a caller polling changed-since can tell a moved hearing from a second one. The id may be a published meeting or a projected session deadline (sine-die-US-2025, or sine-die-FL-2026F for a special session), which resolves back to the session record rather than a copied date.
https://staging.capitolmath.ai/v1/event-detail/cal-US-336587Parameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| id * | string | cal-US-336587 |
Response
{
"data": {
"event_id": "cal-US-336587",
"title": "Business meeting to consider S.93 …",
"description": "Business meeting to consider S.93, to amend the Harmful Algal Blooms …",
"starts_at": "2025-02-05T15:00:00.000Z",
"ends_at": null,
"all_day": false,
"timezone": "America/New_York",
"local_date": "2025-02-05",
"status": "held",
"kind": "business_meeting",
"calendar": "senate_committee",
"location": "US",
"location_name": "US Federal",
"session": 2025,
"room": "253, Russell Senate Office Building",
"committee_id": "cmte-US-sscm00",
"committee_name": "Sen Commerce, Science, and Transportation Committee",
"committee_url": "https://staging.capitolmath.ai/v1/committee-detail/cmte-US-sscm00",
"parent_committee_id": null,
"parent_committee_name": null,
"parent_committee_url": null,
"is_projected": false,
"bill_count": 17,
"bills": [
{ "bill_number": "S. 161", "bill_name": "She DRIVES Act", "session": 2025, "location": "US",
"detail_url": "https://staging.capitolmath.ai/v1/bill-detail/US/2025/S.%20161", "position": 1, "note": null }
],
"supersedes_event_id": null,
"supersedes_event_url": null,
"superseded_by": [],
"source_url": "https://www.congress.gov/event/119th-congress/senate-event/336587",
"last_updated": "2026-09-08T20:08:41.000Z",
"event_url": "https://staging.capitolmath.ai/v1/event-detail/cal-US-336587"
}
}Search officials
/v1/official-search/{location}The officials currently serving one jurisdiction — its legislators AND its executive (the governor, or the President for US), who appears under branch=executive — searchable by branch, chamber, party, district, name, committee, leadership title, start year, or vacancy, and pull only what changed since a date. Pass US for Congress: a federal seat is its own jurisdiction, so /official-search/TX is the Texas statehouse and governor and does not include its congressional delegation. Members who have left are excluded; their seat appears as a vacant placeholder, and their own detail endpoint still works. chamber matches the jurisdiction's raw chamber label (e.g. Senate) and, when given, branch is ignored. changed-since (alias changed_since) matches the record's last_updated, covering every stored attribute of the legislator endpoints; the derived economic_score/social_score are not tracked by it.
https://staging.capitolmath.ai/v1/official-search/TX?branch=upper-chamber&party=RParameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| location * | string | TX or US |
| page | number | 1 |
| page-size | number | 25 |
| name-like | string | Hughes |
| branch | string | upper-chamber, lower-chamber, executive |
| chamber | string | Senate (raw chamber label; overrides branch) |
| party | string | R or [R,D] |
| district | string | 1 |
| committee-like | string | Finance |
| leadership-title-like | string | any |
| first-chamber-year | number | 2023 |
| years-in-chamber-min | number | 5 |
| years-in-chamber-max | number | 10 |
| is-vacant | boolean | false |
| changed-since | string | 2026-01-01 |
Response
{
"pagination": {
"page": 1,
"page_size": 100,
"returned_count": 18,
"total_count": 18,
"total_pages": 1,
"next_url": null,
"prev_url": null
},
"data": [
{
"name": "Bryan Hughes",
"chamber": "Senate",
"branch": "upper-chamber",
"party": "R",
"district": "1",
"leadership_title": null,
"start_date": "2017-01-10",
"last_updated": "2026-08-27T14:05:11.201Z",
"legislator_id": "cmqtt3pgl0000sf3sq1u07aez",
"legislator_url": "https://staging.capitolmath.ai/v1/legislator-detail/cmqtt3pgl0000sf3sq1u07aez"
}
]
}Officials by saved view
/v1/saved-view-official-searchRuns one or more of the saved views built on the Elected Officials page and returns the officials they match — exactly as the page shows them, because it applies the page's own rules to the same roster: committees as the page names them, status, years in chamber and the ideology ranges included, none of which official-search's parameters express. A key sees the saved views its creator sees in the app — their own and those shared with them — and no others. Each row adds `location` (the jurisdiction the view is for; US for Congress) and `is_active` (false for a retired member, which a view can ask for). Naming several views returns officials matching ANY of them, each once; an unknown name is rejected with 400 rather than quietly returning nothing. Results come in the view's saved order: a view saved on the Elected Officials page as a table sorted by a column returns in that sort; otherwise in the page's own order (chamber, then name). With several names, the first that records a sort decides. On the State plan a view for another jurisdiction contributes no one. A Tracked items view works here too, standing for the bills of the items it lists for the key's creator; /v1/tracked-items?saved-view= returns the items themselves. Not available in the live playground below, which isn't tied to an organization. Saved views were called saved filters until the rename: /v1/saved-filter-official-search and the saved-filter parameter still work.
https://staging.capitolmath.ai/v1/saved-view-official-search?saved-view=Texas%20RepublicansParameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| saved-view * | string | Texas Republicans or [A,B] |
| page | number | 1 |
| page-size | number | 25 |
Response
{
"pagination": {
"page": 1,
"page_size": 100,
"returned_count": 1,
"total_count": 1,
"total_pages": 1,
"next_url": null,
"prev_url": null
},
"data": [
{
"name": "Bryan Hughes",
"chamber": "Senate",
"branch": "upper-chamber",
"party": "R",
"district": "1",
"leadership_title": null,
"start_date": "2017-01-10",
"last_updated": "2026-08-27T14:05:11.201Z",
"legislator_id": "cmqtt3pgl0000sf3sq1u07aez",
"legislator_url": "https://staging.capitolmath.ai/v1/legislator-detail/cmqtt3pgl0000sf3sq1u07aez",
"location": "TX",
"is_active": true
}
]
}Get a legislator
/v1/legislator-detail/{id}A legislator's profile, including leadership title, start date, committee memberships, and external reference links. economic_score and social_score are computed from their contested floor votes and the bills they sponsored or cosponsored, precomputed where available (1 conservative … 5 liberal). sponsor_effectiveness (bills sponsored that passed) is ranked within the legislator's own jurisdiction and party_loyalty (party-line votes cast against their party; loyalty_rank 1 = most loyal) within their chamber; a rank is null below the minimum record the ranking requires, and either object is null when there is no record at all. `branch` is as on official-search rows: `executive` marks a governor or the President, whose record is at executive-detail. amendments_offered counts the amendments they sponsored, by outcome, and lists the latest ten; legislator-amendments lists them all.
https://staging.capitolmath.ai/v1/legislator-detail/cmqtt3pgl0000sf3sq1u07aezParameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| id * | string | cmqtt3pgl0000sf3sq1u07aez |
Response
{
"data": {
"legislator_id": "cmqtt3pgl0000sf3sq1u07aez",
"name": "Bryan Hughes",
"chamber": "Senate",
"origin_chamber": "upper",
"branch": "upper-chamber",
"party": "R",
"district": "1",
"website": "https://senate.texas.gov/member.php?d=1",
"bio": null,
"photo_url": null,
"leadership_title": null,
"start_date": "2017-01-10",
"last_updated": "2026-08-27T14:05:11.201Z",
"economic_score": 2.0,
"social_score": 2.3,
"sponsor_effectiveness": {
"bills_sponsored": 569,
"bills_passed": 211,
"effectiveness_pct": 37.1,
"rank": 44,
"ranked_of": 163
},
"party_loyalty": {
"party_line_votes": 233,
"votes_against_party": 3,
"against_party_pct": 1.3,
"loyalty_rank": 17,
"ranked_of": 28
},
"committees": [
{
"name": "State Affairs",
"type": "Senate Standing Committee",
"role": "Chair",
"url": null,
"committee_id": "cmte-TX-senate-state-affairs",
"committee_url": "https://staging.capitolmath.ai/v1/committee-detail/cmte-TX-senate-state-affairs"
}
],
"follow_the_money_link": "https://www.followthemoney.org/entity-details?eid=12345",
"ballotpedia_link": "https://ballotpedia.org/Bryan_Hughes",
"votesmart_link": "https://justfacts.votesmart.org/candidate/12345"
}
}Executive detail
/v1/executive-detail/{id}A governor or the President, with the signing record and the ideology scores derived from it. Separate from legislator-detail, which resolves an executive but returns null scores — it computes from votes an executive does not cast. Addressed by TERM, because the record is per term: pass a term id (terms[].executive_id), or the person's id from /official-search (where executives appear under branch=executive), which resolves to their current term. Read signing_record.tracked first: executive actions are parsed for five jurisdictions today, and elsewhere the counts are zero by construction, which is a coverage gap rather than an executive who signed nothing.
https://staging.capitolmath.ai/v1/executive-detail/cmu30lklq005qsf4lh545bwblParameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| id * | string | the person's id from /official-search (or an executive term id); resolves to their current term |
Response
{
"data": {
"executive_id": "…",
"official_id": "…",
"name": "Donald Trump",
"title": "President",
"party": "R",
"location": "US",
"location_name": "US Federal",
"photo_url": "https://cdn.capitolmath.ai/official_photos/…",
"bio": "Donald John Trump is an American politician, media personality, and businessman who is the 47th president of the United States. …",
"official_url": "https://www.whitehouse.gov/",
"contact": { "address": "1600 Pennsylvania Avenue NW, Washington, DC 20500", "phone": "202-456-1414" },
"links": [
{ "name": "Wikipedia", "url": "https://en.wikipedia.org/wiki/Donald_Trump" },
{ "name": "Ballotpedia", "url": "https://ballotpedia.org/Donald_Trump" }
],
"terms": [
{ "executive_id": "…", "start_date": "2025-01-20", "end_date": "2029-01-20", "current": true,
"executive_url": "https://staging.capitolmath.ai/v1/executive-detail/…" },
{ "executive_id": "…", "start_date": "2017-01-20", "end_date": "2021-01-20", "current": false,
"executive_url": "https://staging.capitolmath.ai/v1/executive-detail/…" }
],
"signing_record": {
"signed": 108, "vetoed": 2, "unsigned": 0, "tracked": true,
"first_action": "2025-01-29", "last_action": "2026-09-11"
},
"economic_score": 1.3,
"social_score": 1.7,
"score_basis": 110
}
}Executive signing history
/v1/executive-signing-history/{id}The counterpart to legislator-vote-history: a legislator's record is the votes they cast, an executive's is the bills they signed or refused to. Same envelope — pagination, the subject once at the top, then the rows. One row per bill carrying its final action, the view the profile's counts and the browse drill also use, so a page here and the number on the profile cannot disagree. Check executive.tracked before reading an empty page: executive actions are parsed for five jurisdictions, and elsewhere there is nothing to return rather than nothing to report. text-like is a plain case-insensitive substring match on the bill's title, number and keywords, not full-text search.
https://staging.capitolmath.ai/v1/executive-signing-history/cmu30lklq005qsf4lh545bwbl?action=vetoedParameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| id * | string | the person's id from /official-search (or an executive term id); resolves to their current term |
| page | number | 1 |
| page-size | number | 25 |
| action | string | signed, vetoed, unsigned |
| session | string | 2025, FL:2026F, or [2025,FL:2026F] |
| topic | string | healthcare |
| is-resolution | string | y or n |
| text-like | string | appropriations |
| order | string | asc or desc |
Response
{
"pagination": { "page": 1, "page_size": 100, "returned_count": 2, "total_count": 2, "total_pages": 1,
"next_url": null, "prev_url": null },
"executive": {
"executive_id": "…", "name": "Donald Trump", "title": "President",
"location": "US", "location_name": "US Federal", "tracked": true,
"executive_url": "https://staging.capitolmath.ai/v1/executive-detail/…"
},
"data": [
{
"action": "vetoed",
"action_date": "2026-01-08",
"session": 2025,
"bill_number": "H.R. 131",
"bill_name": "Finish the Arkansas Valley Conduit Act",
"key_words": "Colorado; Pipelines; Water resources funding; Water use and supply",
"topics": ["public-lands"],
"is_resolution": false,
"summary": "Finish the Arkansas Valley Conduit Act This bill reduces payments that communities within the Arkansas River Valley must pay …",
"location": "US",
"location_name": "US Federal",
"economic_score": 2.5,
"social_score": 3,
"detail_url": "https://staging.capitolmath.ai/v1/bill-detail/US/2025/H.R.%20131"
}
]
}Legislator vote history
/v1/legislator-vote-history/{id}Every yes/no floor vote the legislator cast, newest first, joined to its bill; vote-type=committee (or all) adds their committee votes where the jurisdiction's history records them. The legislator's identity is returned once at the top level. Rows describe the bill in the same vocabulary as /bill-search. Unlike bill-search, topic and outcome take ONE lowercase value here, not a list.
https://staging.capitolmath.ai/v1/legislator-vote-history/cmqtt3pgl0000sf3sq1u07aez?vote=yesParameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| id * | string | cmqtt3pgl0000sf3sq1u07aez |
| page | number | 1 |
| page-size | number | 25 |
| vote | string | yes |
| vote-type | string | floor, committee or all (default floor) |
| session | string | 2025, FL:2026F, or [2025,FL:2026F] |
| outcome | string | passed (one value) |
| topic | string | crime (one value) |
| origin-chamber | string | upper or lower |
| is-resolution | string | y |
| text-like | string | eminent domain |
| sponsor-name-like | string | King |
Response
{
"pagination": {
"page": 1,
"page_size": 100,
"returned_count": 100,
"total_count": 474,
"total_pages": 5,
"next_url": "https://staging.capitolmath.ai/v1/legislator-vote-history/cmqtt3pgl0000sf3sq1u07aez?vote=yes&page=2",
"prev_url": null
},
"legislator": {
"legislator_id": "cmqtt3pgl0000sf3sq1u07aez",
"name": "Bryan Hughes",
"legislator_url": "https://staging.capitolmath.ai/v1/legislator-detail/cmqtt3pgl0000sf3sq1u07aez"
},
"data": [
{
"session": 2026,
"bill_number": "HB 18",
"bill_name": "Relating to the establishment and administration of certain programs and services providing health care services to rural counties.",
"vote": "yes",
"vote_type": "floor",
"vote_date": "2025-05-25",
"key_words": "Health Care Providers; Health--Rural; Hospitals; …",
"topics": ["healthcare"],
"outcome": "Passed",
"furthest_stage": "executive_signature",
"origin_chamber": "lower",
"currently_with": "Governor / Executive",
"location": "TX",
"location_name": "Texas",
"is_resolution": false,
"summary": "The bill author has informed the committee of continuing concerns regarding the provision of health care services in rural counties …",
"bill_sponsors": [
{
"name": "Gary VanDeaver",
"legislator_id": "cmqvegasa00afsffx…",
"photo_url": null,
"legislator_url": "https://staging.capitolmath.ai/v1/legislator-detail/cmqvegasa00afsffx…"
}
],
"detail_url": "https://staging.capitolmath.ai/v1/bill-detail/TX/2026/HB%2018"
}
]
}Amendments a legislator offered
/v1/legislator-amendments/{id}Every amendment the legislator sponsored, newest first, each with its bill, outcome, roll call and effect, and how many there are of each outcome. Amendments are released one legislature at a time, so only released legislatures' amendments are listed, in the jurisdiction the legislator's record is scoped to. Each row's limits says what its session's list or outcomes leave out or may get wrong. A sponsor is counted only when the legislature's name for them was matched to this legislator. recommended means a committee's report names the amendment and its chamber hasn't decided it; it is never adopted. session and outcome take one value each.
https://staging.capitolmath.ai/v1/legislator-amendments/cmqtt3pgl0000sf3sq1u07aez?outcome=adoptedParameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| id * | string | cmqtt3pgl0000sf3sq1u07aez |
| page | number | 1 |
| page-size | number | 25 |
| session | number | 2026 (one value) |
| outcome | string | adopted (one value) |
Response
{
"pagination": { "page": 1, "page_size": 100, "returned_count": 1, "total_count": 1, "total_pages": 1, "next_url": null, "prev_url": null },
"legislator": {
"legislator_id": "cmqtt3pgl0000sf3sq1u07aez",
"name": "Bryan Hughes",
"legislator_url": "https://staging.capitolmath.ai/v1/legislator-detail/cmqtt3pgl0000sf3sq1u07aez"
},
"counts": { "adopted": 1, "not_adopted": 0, "recommended": 0, "offered": 0, "withdrawn": 0, "tabled": 0, "ruled_out": 0, "superseded": 0, "partial": 0, "unknown": 0 },
"data": [
{
"amendment_id": "…:am:upper:FA 1",
"bill": { "location": "TX", "session": 2026, "bill_number": "SB 12", "detail_url": "https://staging.capitolmath.ai/v1/bill-detail/TX/2026/SB%2012" },
"label": "Floor Amendment 1",
"number": "FA 1",
"kind": "floor",
"chamber": "upper",
"reading": null,
"outcome": "adopted",
"decided_in": "chamber",
"parts": null,
"offered_on": "2025-04-02",
"decided_on": "2025-04-02",
"committee": null,
"amends_amendment_id": null,
"sponsors": [{ "name": "Hughes", "kind": "member", "legislator_id": "cmqtt3pgl0000sf3sq1u07aez", "legislator_url": "https://staging.capitolmath.ai/v1/legislator-detail/cmqtt3pgl0000sf3sq1u07aez", "via_chair": false }],
"roll_call": null,
"effect": null,
"text_url": null,
"provenance": "derived",
"outcome_read_from": null,
"tier": 1,
"listed_under": null,
"acted_on": null,
"limits": null
}
]
}Get a legislator scorecard
/v1/scorecardOne of your organization's legislator scorecards, by name (matched without regard to case), computed live as on its own page. The payload follows the scorecard's Display options, set on its Review step: each tab it shows adds a section — Legislators `legislators`, Bills `bills`, Matrix `matrix` (each legislator's vote on each bill), Distribution `distribution`, Map `map`, Tiles photos on `legislators`, Methodology `methodology` — and a tab left off leaves its section out (`sections` lists what's present). Its saved filters restrict the legislators and bills returned, and its saved sorts order them; `filters` and `sort` say what was applied. `scoring` (grade bands, minimum scored bills, non-vote rule) is always present. A key sees the scorecards its creator sees in the app — their own and those shared with them — and no others; an unknown name is a 400 that lists the ones it can. Not available in the live playground below, which isn't tied to an organization.
https://staging.capitolmath.ai/v1/scorecard?name=California%202026%20Legislator%20ScorecardParameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| name * | string | California 2026 Legislator Scorecard |
Response
{
"data": {
"id": "cmuq0vovb0004sfvuv9364pb9",
"name": "California 2026 Legislator Scorecard",
"jurisdiction": "California",
"session_year": 2026,
"chambers": [{ "key": "upper", "label": "Senate" }, { "key": "lower", "label": "Assembly" }],
"computed_at": "2026-10-02T18:00:00.000Z",
"sections": ["legislators", "bills", "matrix", "methodology"],
"scoring": { "grade_bands": [{ "grade": "A", "min_score": 90 }, …], "min_scored_bills": 5, "non_vote_rule": "exclude" },
"party_colours": false,
"filters": { "party": ["R"] },
"sort": { "legislators": { "by": "score", "order": "desc" }, "bills": null },
"counts": { "legislators": 30, "scored": 29, "insufficient_record": 1, "bills": 98 },
"legislators": [
{ "id": "…", "name": "Alexandra M. Macedo", "party": "R", "chamber": { "key": "lower", "label": "Assembly" }, "district": "33",
"photo_url": "https://cdn.capitolmath.ai/…", "score": 61.4, "grade": "D", "insufficient_record": false,
"scored_bills": 57, "weight_earned": 35, "weight_possible": 57 }
],
"bills": [
{ "id": "…", "bill_number": "SB 50", "title": "Connected devices: device protection requests.", "position": "support",
"weight": 1, "tier": "standard", "rationale": null, "outcome": "Passed", "chamber_of_origin": "upper", "url": "https://staging.capitolmath.ai/legislation?bill=…" }
],
"matrix": [{ "legislator_id": "…", "votes": [{ "bill_id": "…", "status": "agree", "vote": "yes", "counted": true, "credit": 1 }] }],
"methodology": { "summary": ["…"], "version": 1, "input_digest": "…", "score_digest": "…" }
}
}List bulk datasets
/v1/datasetsEvery bulk dataset — bills, votes, bill texts (latest version), legislators, committees, calendar events and effective dates — with its newest nightly build and every file in it: one per jurisdiction (ALL for every jurisdiction together) and, for the datasets that have years, per year; in CSV (gzip) and Parquet. Each file lists its size, row count and SHA-256, so a script can skip files it already has. Any key can read the catalog; bulk_access says whether this key's plan can download (Pro and Scale). The columns match the endpoints': Bills has bill-search's, Legislators official-search's, Committees committee-search's, Events event-search's with the full agenda. Votes carries the vote itself and joins to Bills on (location, session, bill_number) and to Legislators on legislator_id. Effective dates has a row per date — the main provisions' (is_main) and each provision with a date of its own — with its resolution and basis as bill-detail's effective returns them (basis whole, as JSON, and its source, clause, rule and steps as columns), joins to Bills the same way (session_key tells apart the sittings of one year), and has files only for the jurisdictions where effective dates are live.
https://staging.capitolmath.ai/v1/datasets?dataset=billsParameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| dataset | string | bills (omit for every dataset) |
Response
{
"bulk_access": true,
"data": [
{
"dataset": "bills",
"name": "Bills",
"split_by": ["location", "year"],
"formats": ["csv", "parquet"],
"version": "2026-09-25",
"built_at": "2026-09-25T06:17:51.000Z",
"not_refreshed": [],
"rows": 385120,
"locations": ["AK", "AL", "ALL", "…"],
"years": [2026, 2025, 2024],
"files": [
{ "location": "TX", "year": 2026, "format": "parquet", "bytes": 1893421, "rows": 8412, "sha256": "…" }
]
}
]
}Download a bulk dataset file
/v1/datasets/{dataset}/downloadA link to one file, good for 15 minutes — fetch the file straight from storage; it never passes through the API. location is a jurisdiction code or ALL; year is required for the datasets split by year (bills, votes, bill_texts, events, effective_dates); format is csv (gzip, the default) or parquet; version picks one of the last seven nightly builds (default: the newest). Pro and Scale plans only — other keys get 403 forbidden. One request against the monthly quota, whatever the file's size.
https://staging.capitolmath.ai/v1/datasets/bills/download?location=TX&year=2026&format=parquetParameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| location * | string | TX, or ALL |
| year | number | 2026 (required for bills, votes, bill_texts, events, effective_dates) |
| format | string | csv (default) or parquet |
| version | string | 2026-09-25 (default: the newest build) |
Response
{
"data": {
"url": "https://… (expires in 15 minutes)",
"expires_at": "2026-09-25T18:40:12.000Z",
"dataset": "bills",
"version": "2026-09-25",
"location": "TX",
"year": 2026,
"format": "parquet",
"bytes": 1893421,
"rows": 8412,
"sha256": "…",
"filename": "capitolmath-bills-tx-2026-2026-09-25.parquet"
}
}Read the organization's audit log
/v1/audit-eventsWho did what in the organization — sign-ins, members and licenses, API keys, saved views, exports, widgets, webhooks, plan changes — and every time CapitolMath staff opened or changed it. For a SIEM or other collector. Ids only: names are shown only to the organization's own Owners and Admins in the app. The key must have been created by an Owner or Admin with Can read the audit log, on a plan that includes it (Developer and above); otherwise 403 forbidden. Entries are append-only and chained: each carries the hash of the one before (prev_hash) and its own, so a missing or altered entry shows. Follow the log with after: keep the last seq you stored and ask for what came after it; every entry arrives exactly once. How far back it reaches is the plan's retention (retention_days). anchor gives the most recent daily public anchor that includes the log, with its Merkle proof.
https://staging.capitolmath.ai/v1/audit-events?after=1200&limit=100Parameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| after | string | 1200 (oldest first, after this seq) |
| before | string | 1300 (newest first, before this seq) |
| limit | integer | 100 (up to 1000) |
| action | string | member.role_changed |
| group | string | auth | access | egress | configuration | billing | organization | staff |
| actor_type | string | user | api_key | system | staff |
| outcome | string | success | failure | denied |
| from | string | 2026-10-01T00:00:00Z |
| to | string | 2026-10-15T00:00:00Z |
Response
{
"data": [
{
"seq": "1201",
"occurred_at": "2026-10-14T15:02:11.000Z",
"action": "member.role_changed",
"outcome": "success",
"actor_type": "user",
"actor_id": "user_…",
"resource_type": "member",
"resource_id": "user_…",
"changes": [{ "column": "role", "prior": "Member", "new": "Admin" }],
"prev_hash": "4f1c…",
"hash": "9a7e…"
}
],
"next": "1201",
"order": "asc",
"retention_days": 365,
"anchor": { "day": "2026-10-14", "seq": "1198", "root": "c02d…", "verify": "…" }
}List your watched bills
/v1/watchesThe bills the key's owner watches, in the key's organization, newest first. Watch is how a member follows a bill: its stage changes and hearings come to them by email, under their own notification settings. A watch is personal: a key reads and changes only its creator's watches. No extra key permission is needed: any key whose creator may use the REST API can use it.
https://staging.capitolmath.ai/v1/watches?page=1Parameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| page | number | 1 |
| page-size | number | 25 |
Response
{
"pagination": { "page": 1, "page_size": 25, "returned_count": 1, "total_count": 1, "total_pages": 1, "next_url": null, "prev_url": null },
"data": [
{
"location": "CA",
"session": 2026,
"bill_number": "SB 12",
"bill_name": "Housing: accessory dwelling units",
"detail_url": "https://staging.capitolmath.ai/v1/bill-detail/CA/2026/SB%2012",
"watching": true,
"watched_at": "2026-10-28T16:20:00.000Z"
}
]
}Watch a bill
/v1/watchesWatch a bill for the key's owner, named as /v1/bill-detail names it. On a bill the organization tracks, they are also listed among the watchers of each tracked item for it they can see. Watching a bill already watched changes nothing (added is false). Watching never counts against a plan's tracked bills.
https://staging.capitolmath.ai/v1/watches?location=CA&session=2026&bill-number=SB%2012Parameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| location * | string | CA |
| session * | number | 2026 |
| bill-number * | string | SB 12 |
Response
{
"data": { "location": "CA", "session": 2026, "bill_number": "SB 12", "bill_name": "Housing: accessory dwelling units", "detail_url": "https://staging.capitolmath.ai/v1/bill-detail/CA/2026/SB%2012", "watching": true, "watched_at": "2026-11-02T15:04:05.000Z", "added": true }
}Stop watching a bill
/v1/watchesStop watching a bill for the key's owner, and take them off the watchers of the tracked items for it. A member who owns a tracked item for the bill always follows it: that answers 409 conflict until the item is given to someone else.
https://staging.capitolmath.ai/v1/watches?location=CA&session=2026&bill-number=SB%2012Parameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| location * | string | CA |
| session * | number | 2026 |
| bill-number * | string | SB 12 |
Response
{
"data": { "location": "CA", "session": 2026, "bill_number": "SB 12", "bill_name": "Housing: accessory dwelling units", "detail_url": "https://staging.capitolmath.ai/v1/bill-detail/CA/2026/SB%2012", "watching": false, "watched_at": null, "removed": true }
}Search tracked items
/v1/tracked-itemsYour organization's tracked items, as the member who created the key sees them on Tracking › Tracked items: the same items, the same filters and the same order, with Read All when they hold it. A client member's key sees only their client's items, and only what clients are shown. Their permissions are checked on every call, so turning off their Read on Tracked items stops the key on the next request. Without `status`, only items in an open status are listed, as the page starts; `status=all` lists every status. Status, tag, client and member ids come from the items themselves. A filter that can't apply (an unknown status or tag, a client or field the key's owner can't see) is a 400 rather than ignored. `custom_fields` includes inactive fields, marked `inactive: true`: hidden in the app and closed to changes, but their values are kept and readable here. Reading never marks an item read.
https://staging.capitolmath.ai/v1/tracked-items?owner=me&watcher=me&position=opposeParameters
(*) Required| Parameter | Type | Example |
|---|---|---|
| page | number | 1 |
| page-size | number | 25 |
| q | string | housing |
| search-notes | boolean | true |
| status | string | open, all, or status ids |
| owner | string | me, or member ids |
| watcher | string | me, or member ids |
| position | string | oppose or [support,support_if_amended] |
| tag | string | tag ids |
| client | string | client ids, or none for your own work |
| legislation-view | string | Datacenter new |
| location | string | CA or [CA,US] |
| outcome | string | passed |
| furthest-stage | string | in_committee |
| effective | string | d90 |
| stage-moved | boolean | true |
| outcome-reached | boolean | true |
| open-tasks | boolean | true |
| conflicts | boolean | true |
| unread | string | stage,vote |
| custom-field | string | fld_fiscal:1000000.. |
| sort | string | priority or custom-field:fld_fiscal |
| order | string | desc |
| saved-view | string | My open items |
Response
{
"pagination": { "page": 1, "page_size": 25, "returned_count": 1, "total_count": 1, "total_pages": 1, "next_url": null, "prev_url": null },
"data": [
{
"id": "ti_8f2c41d07a",
"bill": { "location": "CA", "session": 2026, "bill_number": "SB 12", "bill_name": "Housing: accessory dwelling units", "detail_url": "https://staging.capitolmath.ai/v1/bill-detail/CA/2026/SB%2012" },
"client": { "id": "cl_coastal", "name": "Coastal Housing" },
"position": "oppose_unless_amended",
"position_label": "Oppose unless amended",
"public_position": false,
"our_ask": null,
"priority": "high",
"status": { "id": "st_engaged", "name": "Engaged", "closed": false },
"owner": { "id": "user_dana", "name": "Dana Ortiz" },
"watchers": [{ "id": "user_sam", "name": "Sam Lee" }],
"tags": [{ "id": "tag_zoning", "name": "Zoning" }],
"custom_fields": [
{ "id": "fld_fiscal", "name": "Fiscal impact", "type": "money", "value": 2500000, "value_label": null, "inactive": false },
{ "id": "fld_program", "name": "Program", "type": "choice", "value": "opt_adu", "value_label": "ADU grants", "inactive": true }
],
"open_tasks": 2,
"conflict": false,
"unread": true,
"unread_kinds": ["stage"],
"read_only": false,
"tracked_at": "2026-10-28T16:20:00.000Z",
"updated_at": "2026-11-03T21:05:00.000Z"
}
]
}Get a tracked item
/v1/tracked-items/{id}One tracked item as the member who created the key sees it, whatever its status. A 404 when it isn't tracked here or isn't shared with them: the two look the same.
https://staging.capitolmath.ai/v1/tracked-items/ti_8f2c41d07aParameters
(*) Required| Parameter | Type | Example |
|---|
Response
{
"data": {
"id": "ti_8f2c41d07a",
"bill": { "location": "CA", "session": 2026, "bill_number": "SB 12", "bill_name": "Housing: accessory dwelling units", "detail_url": "https://staging.capitolmath.ai/v1/bill-detail/CA/2026/SB%2012" },
"client": { "id": "cl_coastal", "name": "Coastal Housing" },
"position": "oppose_unless_amended",
"position_label": "Oppose unless amended",
"public_position": false,
"our_ask": null,
"priority": "high",
"status": { "id": "st_engaged", "name": "Engaged", "closed": false },
"owner": { "id": "user_dana", "name": "Dana Ortiz" },
"watchers": [{ "id": "user_sam", "name": "Sam Lee" }],
"tags": [{ "id": "tag_zoning", "name": "Zoning" }],
"custom_fields": [
{ "id": "fld_fiscal", "name": "Fiscal impact", "type": "money", "value": 2500000, "value_label": null, "inactive": false },
{ "id": "fld_program", "name": "Program", "type": "choice", "value": "opt_adu", "value_label": "ADU grants", "inactive": true }
],
"open_tasks": 2,
"conflict": false,
"unread": true,
"unread_kinds": ["stage"],
"read_only": false,
"tracked_at": "2026-10-28T16:20:00.000Z",
"updated_at": "2026-11-03T21:05:00.000Z"
}
}Track a bill
/v1/tracked-itemsTrack a bill as the key's creator, as Track does in the app. Needs a key with **Change tracked items** (`tracking:write`) and a creator who may track bills. Send JSON: `location`, `session` and `bill_number` name the bill as /v1/bill-detail does; `client` (an id) tracks it for a client, left out for your own work; `position`, `priority`, `status` (an id), `owner` (a member id) and `tags` (ids) are optional and take the app's defaults when left out. A bill already tracked for that client, or for your own work, is a 409. Audited, naming the key and its creator. A key without **Read the API** is answered with the new item's `id` only.
https://staging.capitolmath.ai/v1/tracked-itemsParameters
(*) Required| Parameter | Type | Example |
|---|
Response
{
"data": { "id": "ti_8f2c41d07a", "bill": { "location": "CA", "session": 2026, "bill_number": "SB 12", "…": "…" }, "client": null, "position": "none", "status": { "id": "st_monitoring", "name": "Monitoring", "closed": false }, "owner": { "id": "user_dana", "name": "Dana Ortiz" }, "…": "…" },
"created": true
}Change a tracked item
/v1/tracked-items/{id}Change a tracked item's fields as the key's creator, as the tracked item panel does. Needs a key with **Change tracked items** (`tracking:write`). Send JSON with any of `position`, `priority`, `status` (an id), `owner` (a member id), `public_position`, `our_ask`, `tags` (the whole set, as ids), `custom_fields` (`{ "<field id>": value }`, null clearing one) and an optional `reason`. Each field is checked against the creator's permissions as they stand: reassigning needs being the item's owner or Manage All; a field your organization has turned off, or an inactive custom field, is a 409. Each change is kept in the item's history, runs your automation rules as in the app, and is audited, naming the key and its creator. `data` is the item as the key's creator now sees it, or null when the change took it out of their sight; a key without **Read the API** gets `{ id, changed }` instead.
https://staging.capitolmath.ai/v1/tracked-items/ti_8f2c41d07aParameters
(*) Required| Parameter | Type | Example |
|---|
Response
{
"data": { "id": "ti_8f2c41d07a", "position": "oppose", "status": { "id": "st_engaged", "name": "Engaged", "closed": false }, "…": "…" },
"changed": ["position", "status"]
}Bulk datasets
Everything as files, rebuilt nightly: bills, votes, bill texts (the latest version of each bill), legislators, committees, calendar events and effective dates (each enacted law's dates and how they were worked out, where effective dates are live). One file per jurisdiction — or ALL for every jurisdiction together — and, for bills, votes, bill texts, events and effective dates, per year; each in CSV (gzip) and Parquet. The columns are the endpoints', so files and API responses join on the same keys: votes and effective dates join to bills on location, session and bill_number (with session_key where a state sat more than once in a year), and votes to legislators on legislator_id.
GET /v1/datasetslists every file with its size, row count and SHA-256 — skip what you already have.GET /v1/datasets/{dataset}/download?location=TX&year=2026&format=parquetreturns a link that works for 15 minutes; fetch the file straight from storage.- The last seven nightly builds stay available (
version=YYYY-MM-DD). - Downloads are included in the Pro and Scale plans. Signed in with a Full license, you can also download from the Bulk datasets page.
MCP server
The same dataset is exposed as a Model Context Protocol server, so assistants like Claude can query legislation as native tools — with enum-constrained inputs and token-frugal, model-shaped responses. It authenticates with the same vd_live_… key.
search_recordsSearch any kind of record — bills, officials, committees, events, your tracked items — with that kind's filters, or run your saved views.
get_recordOne record of any kind in full, by the id a search returned.
find_model_legislationNear-identical text in other states, and which instance came first.
get_whip_countForecast of how a chamber would vote on a bill that has not voted yet.
get_legislator_votesA legislator's roll-call votes, joined to bills.
compare_legislatorsIdeology-score table for two to ten legislators at once.
get_executive_signing_historyEvery bill a governor or the President signed, vetoed, or let become law unsigned.
Two tools reach every kind of record. search_records takes a kind and that kind's filters, or a saved_view to run your saved views by name; get_record takes a kind and the id a search returned. A filter that doesn't apply to the kind is refused with the ones that do. The other five are analysis tools, which compute rather than look up.
| Kind | Filters | Saved views |
|---|---|---|
bill | location (required), session, outcome, topic, origin_chamber, is_resolution, text_contains, sponsor_name_contains, furthest_stage, currently_with, committee, committee_action, economic_lean_min, economic_lean_max, social_lean_min, social_lean_max, last_updated_after, last_updated_before, last_updated_within_days, takes_effect_window, takes_effect_from, takes_effect_to, tracked, effective_status | bill views, tracked_item views |
official | location (required), name_contains, branch, party, district, committee_contains, leadership_title_contains, first_chamber_year, is_vacant, changed_since | official views |
committee | location (required), name_contains, chamber, origin_chamber, level, parent_id, reviews_bills, is_active, session, member_id, changed_since | None |
event | location, starts_after, starts_before, event_kind, calendar, status, committee, bill_number, session, text_contains, has_bills, is_projected, changed_since, order | event views, bill views |
tracked_item | location, text_contains, search_notes, status, owner, watcher, position, tag, client, legislation_view, outcome, furthest_stage, takes_effect, stage_moved, outcome_reached, open_tasks, conflicts, unread, custom_field, sort, order | tracked_item views |
Results chain by id rather than by link: every row carries the id that get_record takes — a bill's is LOCATION/YEAR/NUMBER, such as TX/2025/HB 11 — and a bill row also carries {location, year, number}, which the bill analysis tools take. A committee's bill counts come with the search_records arguments that list each one, since a model cannot follow a URL. Pass location: "US" anywhere a jurisdiction is taken to reach Congress — a federal seat is its own jurisdiction, not part of the state that elected the member.
The executive is an official too. search_records for officials with branch: "executive" returns the governor or President, and get_record on that id returns their executive profile — terms and a signing record, with the scores derived from it — rather than a legislator's, since they cast no votes; get_executive_signing_history lists the bills. Check signing_record.tracked before reporting a zero — executive actions are parsed for five jurisdictions today, and elsewhere the counts are zero by construction rather than by fact.
It also serves discovery resources (capitolmath://meta/topics, locations, vocab) and workflow prompts (analyze_bill, legislator_profile). Point a stdio MCP client at it:
{
"mcpServers": {
"capitolmath": {
"command": "node",
"args": ["/path/to/capitolmath-mcp/dist/index.js"],
"env": {
"CAPITOLMATH_API_KEY": "vd_live_…",
"CAPITOLMATH_BASE_URL": "https://staging.capitolmath.ai"
}
}
}
}The server can also run over Streamable HTTP (MCP_TRANSPORT=http, served at /mcp) for a self-hosted deployment. It uses the key in its own environment for every caller, so run it only where everyone who can reach it may use that key.
Dataset
CapitolMath aggregates public records from official state sources across all 50 states — bills, roll-call votes, legislators, and lifecycle events — then normalizes and enriches them with derived metrics.
What's included
- Bills — number, title, session year, sponsors, outcome & furthest stage, a plain-language summary, the full legislative text (via bill-detail), and topic labels.
- Votes — floor-vote outcomes and, where the source publishes them, per-member roll calls.
- Legislators — chamber, party, district, and vote-derived ideology scores.
- Derived metrics — bipartisan index, popularity index, economic & social scores, and legislative velocity.
Coverage & history
All 50 states are covered, with sessions currently spanning 2022–2026. Historical depth varies by state and by what each state publishes — most cover the current and prior session, and several go back further. Data is refreshed on a rolling schedule (not real-time), so expect some lag behind the official source.
Known limitations
- Roll calls vary. Some states publish only aggregate floor outcomes, not per-member votes — so
last_roll_callcan be empty even when a bill passed. - Derived scores are directional. Topic labels and economic/social scores are model-derived from bill text; treat them as signal, not official classification.
- Bipartisan index needs parties. Nonpartisan chambers (e.g. Nebraska) omit it.
- Legislator scores need votes.
economic_score/social_scoreare computed from a member's scored floor votes; members without qualifying votes returnnull. - Historical depth is uneven across states, reflecting differences in source availability.