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

idMeaning
1Introduced
2In Committee
3Passed One Chamber
4Passed Both Chambers
5Signed into Law
6Vetoed
7Failed / Dead
8Chaptered (law)

Aggregate buckets in this API: passed = {4, 5, 8}, failed = {6, 7}, alive = {1, 2, 3}.

Meta

Method & pathNotes
GET /v1/healthLiveness. Public, no key.
GET /v1/statusPer-state freshness snapshot. Public, cached 60s.
GET /v1/coveragePer-state depth snapshot with vs_reference ratios. Public.
GET /v1/_meta/usageThis key's tier, monthly cap, and usage for the period.
json
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 & pathParameters
GET /v1/states
GET /v1/sessionsstate, limit
json
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 & pathParameters
GET /v1/billsstate, 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}/changesinclude_whitespace=1
GET /v1/bills/{bill_id}/difffrom, 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.

json
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-resourceFields
/sponsorsbill_id, people_id, name, first_name, last_name, party_id, party_name, district, sponsor_order, sponsor_type_id, sponsor_type_name
/historybill_id, history_step, history_date, history_action, history_body_id, body_name, history_major
/textstext_id, bill_id, bill_text_type_id, text_type, text_date, size_bytes, word_count, state_url
/votesroll_call_id, bill_id, roll_call_body_id, body, vote_date, description, yea, nay, nv, absent, total, passed, state_url
/subjectsbill_id, subject_id, subject_name
/amendmentsamendment_id, bill_id, adopted, amendment_body_id, body, amendment_date, amendment_title, amendment_desc, amendment_mime_id, amendment_size, state_url
/referralsbill_id, committee_id, committee_name, referral_date, referral_step
/calendarbill_id, event_hash, event_type_id, event_type_name, event_date, event_time, event_location, event_desc
/supplementssupplement_id, bill_id, supplement_type_id, supplement_type, supplement_date, title, description, state_url, supplement_mime_id, supplement_size
/sastbill_id, sast_type_id, sast_type, sast_bill_id, sast_bill_number
/citationsid, bill_id, citation_type, citation_number, catchline, bill_page, state_url, created
/analysesid, 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.

bash
curl "https://delilah-api.jsv21b.workers.dev/v1/bills/1234567/diff?from=111&to=222" \
  -H "X-API-Key: dk_live_…"

Votes

Method & pathNotes
GET /v1/votes/{roll_call_id}Roll-call summary and tally.
GET /v1/votes/{roll_call_id}/detailsHow each member voted.
json
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 & pathParameters
GET /v1/legislatorsstate, q, party, chamber, district, limit, offset
GET /v1/legislators/{people_id}
GET /v1/legislators/{people_id}/voteslimit, offset
GET /v1/legislators/{people_id}/billslimit, offset
json
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 & pathParameters
GET /v1/committeesstate, chamber, q, limit
GET /v1/committees/{committee_id}
GET /v1/committees/{committee_id}/members
GET /v1/committees/{committee_id}/hearings
GET /v1/hearingsstate, since, limit
json
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 & pathParameters
GET /v1/topics
GET /v1/topics/{slug}
GET /v1/subjectsstate, q, limit
json
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 & pathParameters
GET /v1/candidatesstate, 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 & pathParameters
GET /v1/changessince, state, reason, limit
GET /v1/streamstates (CSV), since, reason
GET /v1/webhooks
POST /v1/webhooksbody: webhook_url, events, filters
DELETE /v1/webhooks/{id}
POST /v1/webhooks/{id}/testFires a synthetic delivery.
POST /v1/push-mirrorbody: webhook_url, events, filters
POST /v1/push-mirror/testbody: 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.

bash
# 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.

bash
curl -N "https://delilah-api.jsv21b.workers.dev/v1/stream?states=FL,CA" \
  -H "X-API-Key: dk_live_…"