API Reference
46 read-only endpoints over US legislative data, grouped by resource. Every path below is relative to https://delilah-api.jsv21b.workers.dev and — except for the three public ones — requires an X-API-Key header.
The machine-readable OpenAPI 3 spec is public at https://delilah-api.jsv21b.workers.dev/openapi.json, and an interactive reference rendered from that same spec lives at https://delilah-api.jsv21b.workers.dev/docs. In the JSON samples on this page, field names are exactly what the API returns; values written as <type> are placeholders.
Per-resource references
This page is the map. Each resource below has its own reference with every parameter, example request, and response shape spelled out endpoint by endpoint.
- Bills — search, one bill, and the fourteen sub-resources: sponsors, votes, history, texts, amendments, subjects, calendar, citations, analyses, cross-references, referrals, supplements, and text diffs.
- Legislators — roster search, one member, their voting history, and their sponsorships.
- Votes — roll-call summaries and individual member votes.
- Committees & hearings — committees, members, and hearings both per-committee and statewide.
- Sessions & states — the two reference lists everything else is keyed against.
- Discovery — coverage, the change feed, topics, subjects, the SSE stream, and candidates.
Response envelopes
Single records come back as { data: {…} }, collections as { data: [ … ] }. Endpoints that page add a pagination object (Pagination); the change feed and vote details add a summary. Errors use a single envelope described in Errors.
Status ID ladder
status_id appears on every bill object and is accepted as a filter on /v1/bills. It follows this API’s own ladder below. If you are moving from another data source, map explicitly rather than 1:1 — the same integer often means something different elsewhere (most notably, here 5 is Signed into Law).
| id | Meaning |
|---|---|
| 1 | Introduced |
| 2 | In Committee |
| 3 | Passed One Chamber |
| 4 | Passed Both Chambers |
| 5 | Signed into Law |
| 6 | Vetoed |
| 7 | Failed / Dead |
| 8 | Chaptered (law) |
Aggregate buckets in this API: passed = {4, 5, 8}, failed = {6, 7}, alive = {1, 2, 3}.
Meta
| Method & path | Notes |
|---|---|
GET /v1/health | Liveness. Public, no key. |
GET /v1/status | Per-state freshness snapshot. Public, cached 60s. |
GET /v1/coverage | Per-state depth snapshot with vs_reference ratios. Public. |
GET /v1/_meta/usage | This key's tier, monthly cap, and usage for the period. |
GET /v1/health
{
"ok": true,
"service": "delilah-api",
"version": "v1",
"ts": "2026-08-12T20:31:54.874Z"
}/v1/coverage reports generated_at, computed_at, states_total, states_in_session, a totals object, and a per_state array carrying bills, pct_with_votes, pct_with_texts, pct_with_sponsors, the raw *_total counts, has_live_session, and vs_reference. A live sample is on the Quickstart.
Reference
| Method & path | Parameters |
|---|---|
GET /v1/states | — |
GET /v1/sessions | state, limit |
GET /v1/sessions?state=FL
{
"data": [
{
"session_id": "<int>",
"state_id": "<int>",
"state_abbr": "<string>",
"year_start": "<int>",
"year_end": "<int>",
"special": "<int>",
"session_tag": "<string>",
"session_title": "<string>",
"session_name": "<string>",
"sine_die": "<int>",
"session_start_date": "<date>",
"actual_sine_die_date": "<date>",
"expected_sine_die_date": "<date>",
"prefile": "<int>",
"prior": "<int>",
"source_url": "<string>"
}
]
}/v1/states returns the 52 jurisdictions with state_id, state_abbr, state_name, and capitol. Use US for Congress and DC for the District.
Bills
| Method & path | Parameters |
|---|---|
GET /v1/bills | state, session_id, bill_number, q, topic, subject, sponsor_last_name, status_id, title_contains, updated_since, limit, offset |
GET /v1/bills/{bill_id} | — |
GET /v1/bills/{bill_id}/sponsors | — |
GET /v1/bills/{bill_id}/history | — |
GET /v1/bills/{bill_id}/texts | — |
GET /v1/bills/{bill_id}/votes | — |
GET /v1/bills/{bill_id}/subjects | — |
GET /v1/bills/{bill_id}/amendments | — |
GET /v1/bills/{bill_id}/referrals | — |
GET /v1/bills/{bill_id}/calendar | — |
GET /v1/bills/{bill_id}/supplements | — |
GET /v1/bills/{bill_id}/sast | — |
GET /v1/bills/{bill_id}/citations | — |
GET /v1/bills/{bill_id}/analyses | — |
GET /v1/bills/{bill_id}/changes | include_whitespace=1 |
GET /v1/bills/{bill_id}/diff | from, to (both required, text_id values) |
bill_number accepts either the compact form (HB1) or the padded form (HB0001) — both normalize to the same row. q searches title, description, and subject names; topic takes a canonical slug (see Topics); updated_since takes an ISO-8601 date or timestamp.
GET /v1/bills?state=FL&limit=1
{
"data": [
{
"bill_id": "<int>",
"state_id": "<int>",
"state_abbr": "<string>",
"session_id": "<int>",
"bill_number": "<string>",
"bill_type_id": "<int>",
"bill_type_name": "<string>",
"body_id": "<int>",
"current_body_id": "<int>",
"status_id": "<int>",
"status_date": "<date>",
"title": "<string>",
"description": "<string>",
"state_url": "<string>",
"updated": "<timestamp>",
"created": "<timestamp>",
"change_hash": "<string>",
"content_hash": "<string>",
"summary": "<string|null>",
"last_action": "<string|null>",
"completed": "<int|null>",
"pending_committee_id": "<int|null>"
}
],
"pagination": { "limit": 1, "offset": 0, "returned": 1 }
}Sub-resource rows, by path:
| Sub-resource | Fields |
|---|---|
/sponsors | bill_id, people_id, name, first_name, last_name, party_id, party_name, district, sponsor_order, sponsor_type_id, sponsor_type_name |
/history | bill_id, history_step, history_date, history_action, history_body_id, body_name, history_major |
/texts | text_id, bill_id, bill_text_type_id, text_type, text_date, size_bytes, word_count, state_url |
/votes | roll_call_id, bill_id, roll_call_body_id, body, vote_date, description, yea, nay, nv, absent, total, passed, state_url |
/subjects | bill_id, subject_id, subject_name |
/amendments | amendment_id, bill_id, adopted, amendment_body_id, body, amendment_date, amendment_title, amendment_desc, amendment_mime_id, amendment_size, state_url |
/referrals | bill_id, committee_id, committee_name, referral_date, referral_step |
/calendar | bill_id, event_hash, event_type_id, event_type_name, event_date, event_time, event_location, event_desc |
/supplements | supplement_id, bill_id, supplement_type_id, supplement_type, supplement_date, title, description, state_url, supplement_mime_id, supplement_size |
/sast | bill_id, sast_type_id, sast_type, sast_bill_id, sast_bill_number |
/citations | id, bill_id, citation_type, citation_number, catchline, bill_page, state_url, created |
/analyses | id, bill_id, analysis_type, analysis_title, author, analysis_date, state_url, created |
/citations and /analyses are populated for the states that publish statute references and staff analyses; elsewhere they return an empty data array. /changes lists every computed text diff for a bill as a timeline, and /diff returns the unified diff between two specific text_id versions.
curl "https://delilah-api.jsv21b.workers.dev/v1/bills/1234567/diff?from=111&to=222" \ -H "X-API-Key: dk_live_…"
Votes
| Method & path | Notes |
|---|---|
GET /v1/votes/{roll_call_id} | Roll-call summary and tally. |
GET /v1/votes/{roll_call_id}/details | How each member voted. |
GET /v1/votes/{roll_call_id}/details
{
"data": [
{
"people_id": "<int>",
"name": "<string>",
"party_name": "<string>",
"district": "<string>",
"vote": "<Yea|Nay|NV|Absent>"
}
],
"summary": { "roll_call_id": "<int>", "total": "<int>" }
}Legislators
| Method & path | Parameters |
|---|---|
GET /v1/legislators | state, q, party, chamber, district, limit, offset |
GET /v1/legislators/{people_id} | — |
GET /v1/legislators/{people_id}/votes | limit, offset |
GET /v1/legislators/{people_id}/bills | limit, offset |
GET /v1/legislators/{people_id}
{
"data": {
"people_id": "<int>",
"state_id": "<int>",
"state_abbr": "<string>",
"role_id": "<int>",
"chamber": "<string>",
"party_id": "<int>",
"party_name": "<string>",
"name": "<string>",
"first_name": "<string>",
"middle_name": "<string>",
"last_name": "<string>",
"suffix": "<string>",
"nickname": "<string>",
"district": "<string>",
"ballotpedia": "<string>",
"followthemoney_eid": "<string>",
"votesmart_id": "<string>",
"opensecrets_id": "<string>",
"updated": "<timestamp>"
}
}/votes returns one row per roll call the member participated in — the vote itself (roll_call_id, vote_id, vote_name, roll_call_date, roll_call_desc, body_id, the yea/nay/nv/absent/total tally and passed) joined to the bill (bill_id, bill_number, title, state_abbr). /bills returns their sponsorships: sponsor_type_id, sponsor_order, bill_id, bill_number, title, description, status, status_date, state_abbr.
Committees & hearings
| Method & path | Parameters |
|---|---|
GET /v1/committees | state, chamber, q, limit |
GET /v1/committees/{committee_id} | — |
GET /v1/committees/{committee_id}/members | — |
GET /v1/committees/{committee_id}/hearings | — |
GET /v1/hearings | state, since, limit |
GET /v1/committees?state=FL
{
"data": [
{
"committee_id": "<int>",
"state_abbr": "<string>",
"chamber": "<string>",
"committee_name": "<string>",
"committee_type": "<string>",
"description": "<string>",
"chair_name": "<string>",
"vice_chair_name": "<string>",
"members_count": "<int>",
"website_url": "<string>",
"meeting_schedule": "<string>"
}
]
}Members carry committee_id, people_id, name, party_id, party_name, and member_role. Hearing rows carry hearing_id, state_code, chamber, committee_name, hearing_title, hearing_date, hearing_time, hearing_datetime, status, is_live, hearing_location, room_number, video_url, audio_url, transcript_url, transcript_text, participants, bills_discussed, topics, and source_url — video and transcript fields are present for the states that publish them.
Topics & subjects
| Method & path | Parameters |
|---|---|
GET /v1/topics | — |
GET /v1/topics/{slug} | — |
GET /v1/subjects | state, q, limit |
GET /v1/topics/{slug}
{
"slug": "<string>",
"display_name": "<string>",
"description": "<string>",
"emoji": "<string>",
"sort_order": "<int>",
"bill_count": "<int>",
"raw_subjects_mapped": "<int>",
"sample_subjects": ["<string>"]
}/v1/topics lists the canonical taxonomy with a bill_count per topic; /v1/subjects is the raw per-state subject firehose underneath it. See Topics.
Candidates
| Method & path | Parameters |
|---|---|
GET /v1/candidates | state, office, cycle, fec_id, bioguide, limit, offset |
GET /v1/candidates/{id} | — |
List rows carry id, name, party, photo_url, people_id, fec_id, plus the FEC side — office, state, district, cycle, incumbent_challenge, bioguide_id. The detail endpoint adds bio, website, socials, bp_photo_url, bp_url, and a candidacies array (contest_id, party, is_incumbent, withdrew, votes, vote_pct, is_winner, header, election_date, office_name, office_level, chamber, district, election_year, race_state).
people_id is the crosswalk: it keys straight into /v1/legislators/{people_id} and its drill-downs. The link is deterministic — no name matching. Challengers carry FEC and Ballotpedia data only, so the state, office, and cycle filters match only candidates with an FEC record.
Realtime
| Method & path | Parameters |
|---|---|
GET /v1/changes | since, state, reason, limit |
GET /v1/stream | states (CSV), since, reason |
GET /v1/webhooks | — |
POST /v1/webhooks | body: webhook_url, events, filters |
DELETE /v1/webhooks/{id} | — |
POST /v1/webhooks/{id}/test | Fires a synthetic delivery. |
POST /v1/push-mirror | body: webhook_url, events, filters |
POST /v1/push-mirror/test | body: webhook_url |
Change rows carry bill_id, bill_number, state_abbr, title, reason_id, reason_name, and changed_at. Reasons include NewBill, StatusChange, Text, Vote, and Amendment among others. Without since, the feed returns the last hour.
# subscribe to a webhook
curl -X POST https://delilah-api.jsv21b.workers.dev/v1/webhooks \
-H "X-API-Key: dk_live_…" \
-H "Content-Type: application/json" \
-d '{
"webhook_url": "https://example.com/delilah-hook",
"events": ["status_change", "vote_recorded"],
"filters": { "states": ["FL", "CA"] }
}'Valid events: bill_introduced, status_change, vote_recorded, text_updated, passed, vetoed, signed, amendment_added. filters accepts states and subjects arrays. The webhook_url must be HTTPS and must not point at an internal or private host. A 201 response returns the subscription with its secret once — store it, since it is what you verify HMAC signatures with, and it is never returned again. A key may hold 25 active subscriptions.
/v1/stream is Server-Sent Events. Each connection emits bill_change events, lives about 85 seconds, then sends a rotate event; a standard EventSource client reconnects on its own and resumes from Last-Event-ID. /v1/push-mirrorexists for teams migrating off a provider push feed: deliveries arrive in that provider's wire format, so an existing handler keeps working unchanged.
curl -N "https://delilah-api.jsv21b.workers.dev/v1/stream?states=FL,CA" \ -H "X-API-Key: dk_live_…"

