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 location parameter or path segment, including US — is refused with forbidden. Where location is 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 parameter
  • unauthorized — 401: missing or invalid API key
  • forbidden — 403: the organization is suspended or inactive, or the request is outside the state a State plan covers (the response names it in covered_location)
  • not_found — 404
  • conflict — 409: stopping a watch on a bill the key's owner owns a tracked item for
  • rate_limited — 429
  • quota_exceeded — 429
  • internal — 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.

GET

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.

Endpoint
https://staging.capitolmath.ai/v1/bill-detail/TX/2026/HB%2018

Parameters

(*) Required
ParameterTypeExample
location *stringTX
year *number2026
number *stringHB 18
include-bill-textstringlatest 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
  }
}
GET

Accuracy scoreboard (public)

/v1/accuracy

Public, 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).

Endpoint
https://staging.capitolmath.ai/v1/accuracy?state=NY&lead=7d

Parameters

(*) Required
ParameterTypeExample
publicationstring2027-04-12
statestringNY
chamberstringupper or lower
sessionnumber2027
leadstring7d (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 }
  }
}
GET

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.

Endpoint
https://staging.capitolmath.ai/v1/whip-count/CA/2026/AB%201900?chamber=lower

Parameters

(*) Required
ParameterTypeExample
location *stringCA
year *number2026
number *stringAB 1900
chamberstringupper 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 } ]
    }
  }
}
GET

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.

Endpoint
https://staging.capitolmath.ai/v1/committee-detail/cmte-US-hsag00

Parameters

(*) Required
ParameterTypeExample
id *stringcmte-US-hsag00
sessionnumber2025

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"
  }
}
GET

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.

Endpoint
https://staging.capitolmath.ai/v1/event-detail/cal-US-336587

Parameters

(*) Required
ParameterTypeExample
id *stringcal-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"
  }
}
GET

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.

Endpoint
https://staging.capitolmath.ai/v1/legislator-detail/cmqtt3pgl0000sf3sq1u07aez

Parameters

(*) Required
ParameterTypeExample
id *stringcmqtt3pgl0000sf3sq1u07aez

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"
  }
}
GET

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.

Endpoint
https://staging.capitolmath.ai/v1/executive-detail/cmu30lklq005qsf4lh545bwbl

Parameters

(*) Required
ParameterTypeExample
id *stringthe 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
  }
}
GET

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.

Endpoint
https://staging.capitolmath.ai/v1/executive-signing-history/cmu30lklq005qsf4lh545bwbl?action=vetoed

Parameters

(*) Required
ParameterTypeExample
id *stringthe person's id from /official-search (or an executive term id); resolves to their current term
pagenumber1
page-sizenumber25
actionstringsigned, vetoed, unsigned
sessionstring2025, FL:2026F, or [2025,FL:2026F]
topicstringhealthcare
is-resolutionstringy or n
text-likestringappropriations
orderstringasc 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"
    }
  ]
}
GET

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.

Endpoint
https://staging.capitolmath.ai/v1/legislator-vote-history/cmqtt3pgl0000sf3sq1u07aez?vote=yes

Parameters

(*) Required
ParameterTypeExample
id *stringcmqtt3pgl0000sf3sq1u07aez
pagenumber1
page-sizenumber25
votestringyes
vote-typestringfloor, committee or all (default floor)
sessionstring2025, FL:2026F, or [2025,FL:2026F]
outcomestringpassed (one value)
topicstringcrime (one value)
origin-chamberstringupper or lower
is-resolutionstringy
text-likestringeminent domain
sponsor-name-likestringKing

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"
    }
  ]
}
GET

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.

Endpoint
https://staging.capitolmath.ai/v1/legislator-amendments/cmqtt3pgl0000sf3sq1u07aez?outcome=adopted

Parameters

(*) Required
ParameterTypeExample
id *stringcmqtt3pgl0000sf3sq1u07aez
pagenumber1
page-sizenumber25
sessionnumber2026 (one value)
outcomestringadopted (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

Get a legislator scorecard

/v1/scorecard

One 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.

Endpoint
https://staging.capitolmath.ai/v1/scorecard?name=California%202026%20Legislator%20Scorecard

Parameters

(*) Required
ParameterTypeExample
name *stringCalifornia 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": "…" }
  }
}
GET

List bulk datasets

/v1/datasets

Every 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.

Endpoint
https://staging.capitolmath.ai/v1/datasets?dataset=bills

Parameters

(*) Required
ParameterTypeExample
datasetstringbills (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": "…" }
      ]
    }
  ]
}
GET

Download a bulk dataset file

/v1/datasets/{dataset}/download

A 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.

Endpoint
https://staging.capitolmath.ai/v1/datasets/bills/download?location=TX&year=2026&format=parquet

Parameters

(*) Required
ParameterTypeExample
location *stringTX, or ALL
yearnumber2026 (required for bills, votes, bill_texts, events, effective_dates)
formatstringcsv (default) or parquet
versionstring2026-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"
  }
}
GET

Read the organization's audit log

/v1/audit-events

Who 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.

Endpoint
https://staging.capitolmath.ai/v1/audit-events?after=1200&limit=100

Parameters

(*) Required
ParameterTypeExample
afterstring1200 (oldest first, after this seq)
beforestring1300 (newest first, before this seq)
limitinteger100 (up to 1000)
actionstringmember.role_changed
groupstringauth | access | egress | configuration | billing | organization | staff
actor_typestringuser | api_key | system | staff
outcomestringsuccess | failure | denied
fromstring2026-10-01T00:00:00Z
tostring2026-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": "…" }
}
GET

List your watched bills

/v1/watches

The 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.

Endpoint
https://staging.capitolmath.ai/v1/watches?page=1

Parameters

(*) Required
ParameterTypeExample
pagenumber1
page-sizenumber25

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"
    }
  ]
}
POST

Watch a bill

/v1/watches

Watch 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.

Endpoint
https://staging.capitolmath.ai/v1/watches?location=CA&session=2026&bill-number=SB%2012

Parameters

(*) Required
ParameterTypeExample
location *stringCA
session *number2026
bill-number *stringSB 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 }
}
DELETE

Stop watching a bill

/v1/watches

Stop 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.

Endpoint
https://staging.capitolmath.ai/v1/watches?location=CA&session=2026&bill-number=SB%2012

Parameters

(*) Required
ParameterTypeExample
location *stringCA
session *number2026
bill-number *stringSB 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 }
}
GET

Search tracked items

/v1/tracked-items

Your 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.

Endpoint
https://staging.capitolmath.ai/v1/tracked-items?owner=me&watcher=me&position=oppose

Parameters

(*) Required
ParameterTypeExample
pagenumber1
page-sizenumber25
qstringhousing
search-notesbooleantrue
statusstringopen, all, or status ids
ownerstringme, or member ids
watcherstringme, or member ids
positionstringoppose or [support,support_if_amended]
tagstringtag ids
clientstringclient ids, or none for your own work
legislation-viewstringDatacenter new
locationstringCA or [CA,US]
outcomestringpassed
furthest-stagestringin_committee
effectivestringd90
stage-movedbooleantrue
outcome-reachedbooleantrue
open-tasksbooleantrue
conflictsbooleantrue
unreadstringstage,vote
custom-fieldstringfld_fiscal:1000000..
sortstringpriority or custom-field:fld_fiscal
orderstringdesc
saved-viewstringMy 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

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.

Endpoint
https://staging.capitolmath.ai/v1/tracked-items/ti_8f2c41d07a

Parameters

(*) Required
ParameterTypeExample

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"
    }
}
POST

Track a bill

/v1/tracked-items

Track 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.

Endpoint
https://staging.capitolmath.ai/v1/tracked-items

Parameters

(*) Required
ParameterTypeExample

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
}
PATCH

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.

Endpoint
https://staging.capitolmath.ai/v1/tracked-items/ti_8f2c41d07a

Parameters

(*) Required
ParameterTypeExample

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/datasets lists 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=parquet returns 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_records

Search any kind of record — bills, officials, committees, events, your tracked items — with that kind's filters, or run your saved views.

get_record

One record of any kind in full, by the id a search returned.

find_model_legislation

Near-identical text in other states, and which instance came first.

get_whip_count

Forecast of how a chamber would vote on a bill that has not voted yet.

get_legislator_votes

A legislator's roll-call votes, joined to bills.

compare_legislators

Ideology-score table for two to ten legislators at once.

get_executive_signing_history

Every 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.

The kinds search_records and get_record serve, with each kind's filters
KindFiltersSaved views
billlocation (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_statusbill views, tracked_item views
officiallocation (required), name_contains, branch, party, district, committee_contains, leadership_title_contains, first_chamber_year, is_vacant, changed_sinceofficial views
committeelocation (required), name_contains, chamber, origin_chamber, level, parent_id, reviews_bills, is_active, session, member_id, changed_sinceNone
eventlocation, starts_after, starts_before, event_kind, calendar, status, committee, bill_number, session, text_contains, has_bills, is_projected, changed_since, orderevent views, bill views
tracked_itemlocation, 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, ordertracked_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.

50
States
491,640
Bills
26,255,120
Recorded votes
9,804
Legislators

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_call can 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_score are computed from a member's scored floor votes; members without qualifying votes return null.
  • Historical depth is uneven across states, reflecting differences in source availability.