{"openapi":"3.1.0","info":{"title":"BallotData API","version":"1.5.0","summary":"U.S. ballots, endorsers and endorsements, and a live stream of endorsement changes.","description":"Ballot races (elections, contests and their choices), endorsers and endorsements across the U.S., who holds office now, and a stream of endorsement changes.\n\nEverything is JSON over HTTPS. Send your API key on every request as `Authorization: Bearer ebk_…`. Lists are paged: `{ \"data\": [...], \"next_cursor\": \"…\" }`; pass `cursor` for the next page. Errors are `{ \"error\": { \"code\", \"message\", \"request_id\" } }`.\n\nAddress and geography lookups (the ballot at an address, districts and boundaries, representatives by location) have been retired: choose races by state, election or contest.","contact":{"name":"BallotData","email":"sales@ballotdata.ai","url":"https://ballotdata.ai"},"termsOfService":"https://ballotdata.ai/legal/terms","license":{"name":"Proprietary: BallotData terms of service","url":"https://ballotdata.ai/legal/terms"}},"externalDocs":{"description":"Guides","url":"https://api.ballotdata.ai/docs"},"servers":[{"url":"https://api.ballotdata.ai","description":"Production"}],"security":[{"bearer":[]},{"apiKey":[]}],"tags":[{"name":"Elections","description":"States, elections and coverage: what there is to ask about."},{"name":"Contests","description":"The races on a ballot and their choices: candidates, or Yes/No for a measure. Contests carry their voting rules: vote for one, vote for N (votes_allowed), ranked choice (ranks_allowed), or yes/no, and their election results once a source has them: official counts from election offices, certified returns compiled by researchers for past elections, or a party’s announcement of the winner, always with the source’s URL."},{"name":"Endorsers","description":"Who endorses: unions, newspapers, parties, clubs, advocacy groups and officials, by category and home state."},{"name":"Endorsements","description":"Who each endorser backs in each race, and every change to those endorsements."},{"name":"Candidates","description":"The people on the ballot, every endorsement each one received, and where they stand on common issues."},{"name":"Officeholders","description":"Who holds each seat now, from Open States."},{"name":"Stream","description":"Every endorsement added, changed or removed, as Server-Sent Events or by polling. Needs the stream:read scope and a plan that includes the stream.","externalDocs":{"description":"Stream API reference","url":"https://api.ballotdata.ai/docs/stream/reference"}},{"name":"Subscriptions","description":"Saved filters for the stream and events. Reading needs stream:read; changing needs subscriptions:write.","externalDocs":{"description":"Stream API reference","url":"https://api.ballotdata.ai/docs/stream/reference"}}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","bearerFormat":"ebk_<prefix>_<secret>","description":"An API key from the console, as Authorization: Bearer ebk_…"},"apiKey":{"type":"apiKey","in":"header","name":"X-API-Key","description":"The same key in an X-API-Key header, for clients that can’t set Authorization."}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per minute on your plan.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left this minute.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the minute resets.","schema":{"type":"integer"}},"X-Daily-Limit":{"description":"Requests allowed per UTC day, on plans with a daily cap.","schema":{"type":"integer"}},"X-Daily-Remaining":{"description":"Requests left today.","schema":{"type":"integer"}},"X-Request-Id":{"description":"This request’s id. Quote it when you write to us.","schema":{"type":"string","format":"uuid"}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id"],"properties":{"code":{"type":"string","description":"Machine-readable error code.","enum":["missing_api_key","invalid_api_key","revoked_api_key","expired_api_key","ip_not_allowed","account_pending","account_suspended","account_closed","insufficient_scope","no_access","outside_purchase","invalid_parameter","invalid_cursor","invalid_filter","invalid_json","invalid_subscription","body_too_large","not_found","subscription_not_found","conflict","too_many_requests","too_many_failures","rate_limit_exceeded","daily_limit_exceeded","too_many_streams","cursor_expired","warming_up","server_busy","not_available","internal_error"]},"message":{"type":"string","description":"What went wrong, in plain English."},"request_id":{"type":"string","description":"Quote this when you write to us. Also sent as the X-Request-Id header.","format":"uuid"}}}}},"State":{"type":"object","required":["code","fips","name"],"properties":{"code":{"type":"string","pattern":"^[A-Z]{2}$","description":"USPS state code.","examples":["CA"]},"fips":{"type":"string","description":"Two-digit FIPS code.","examples":["06"]},"name":{"type":"string","description":"Full name."}}},"Party":{"type":"object","required":["slug","name","abbreviation"],"properties":{"slug":{"type":"string","description":"Party slug.","examples":["democratic"]},"name":{"type":"string","description":"Party name."},"abbreviation":{"type":["string","null"],"description":"Short form as printed, e.g. DEM."}}},"Category":{"type":"object","description":"The kind of endorser.","required":["slug","name"],"properties":{"slug":{"type":"string","description":"Category slug, used in categories= filters.","examples":["labor"]},"name":{"type":"string","description":"Display name."}}},"EndorserCategory":{"type":"object","required":["slug","name","singular_name","endorser_count"],"properties":{"slug":{"type":"string","description":"Category slug, used in categories= filters.","examples":["labor"]},"name":{"type":"string","description":"Display name (plural)."},"singular_name":{"type":"string","description":"One of them: “Labor union”."},"endorser_count":{"type":"integer","description":"Endorsers in this category."}}},"EndorserRef":{"type":"object","description":"Who made an endorsement.","required":["id","slug","name","category","state"],"properties":{"id":{"type":"integer","description":"Endorser id."},"slug":{"type":"string","description":"Endorser slug, used in endorsers= filters."},"name":{"type":"string","description":"Endorser name."},"category":{"$ref":"#/components/schemas/Category"},"state":{"type":["string","null"],"pattern":"^[A-Z]{2}$","description":"Home state; null for national endorsers.","examples":["CA"]}}},"Endorser":{"type":"object","required":["id","slug","name","category","description","website_url","logo_url","state","party","endorsement_count"],"properties":{"id":{"type":"integer","description":"Endorser id."},"slug":{"type":"string","description":"Endorser slug, used in endorsers= filters and GET /v1/endorsers/{slug}."},"name":{"type":"string","description":"Endorser name."},"category":{"$ref":"#/components/schemas/Category"},"description":{"type":["string","null"],"description":"About the endorser."},"website_url":{"type":["string","null"],"description":"Endorser’s website.","format":"uri"},"logo_url":{"type":["string","null"],"description":"Logo image.","format":"uri"},"state":{"type":["string","null"],"pattern":"^[A-Z]{2}$","description":"Home state; null for national endorsers.","examples":["CA"]},"party":{"description":"The party, for a party or party club.","oneOf":[{"$ref":"#/components/schemas/Party"},{"type":"null"}]},"endorsement_count":{"type":"integer","description":"Contests this endorser takes a position in, across every election you can read. A ranked-choice order or a vote-for-N slate counts once."}}},"EndorserDetail":{"type":"object","required":["id","slug","name","category","description","website_url","logo_url","state","party","endorsement_count","elections"],"properties":{"id":{"type":"integer","description":"Endorser id."},"slug":{"type":"string","description":"Endorser slug."},"name":{"type":"string","description":"Endorser name."},"category":{"$ref":"#/components/schemas/Category"},"description":{"type":["string","null"],"description":"About the endorser."},"website_url":{"type":["string","null"],"description":"Endorser’s website.","format":"uri"},"logo_url":{"type":["string","null"],"description":"Logo image.","format":"uri"},"state":{"type":["string","null"],"pattern":"^[A-Z]{2}$","description":"Home state; null for national endorsers.","examples":["CA"]},"party":{"description":"The party, for a party or party club.","oneOf":[{"$ref":"#/components/schemas/Party"},{"type":"null"}]},"endorsement_count":{"type":"integer","description":"Contests this endorser takes a position in, across every election you can read. A ranked-choice order or a vote-for-N slate counts once."},"elections":{"type":"array","description":"Elections the endorser has endorsed in, newest first.","items":{"type":"object","required":["election","name","date","contests"],"properties":{"election":{"type":"string","description":"Election slug."},"name":{"type":"string","description":"Election name."},"date":{"type":"string","description":"Election day.","format":"date"},"contests":{"type":"integer","description":"Contests this endorser took a position in."}}}}}},"Election":{"type":"object","required":["id","slug","name","short_name","date","state","type","primary_system","info_url","details"],"properties":{"id":{"type":"integer","description":"Election id."},"slug":{"type":"string","description":"Election slug, used in elections= filters: <state>-<date>-<type>.","examples":["ca-2026-11-03-general"]},"name":{"type":"string","description":"Election name."},"short_name":{"type":["string","null"],"description":"Shorter name."},"date":{"type":"string","description":"Election day.","format":"date"},"state":{"type":["string","null"],"pattern":"^[A-Z]{2}$","description":"State; null for a multi-state election.","examples":["CA"]},"type":{"type":"string","description":"Kind of election.","enum":["general","primary","special","special_primary","runoff","consolidated"]},"primary_system":{"type":["string","null"],"description":"How the state runs primaries.","enum":["none","closed","semi_closed","open","top_two","nonpartisan",null]},"info_url":{"type":["string","null"],"description":"Official election page.","format":"uri"},"details":{"type":"object","description":"Voting rules for the election. Only keys that apply are present.","required":[],"properties":{"all_mail":{"type":"boolean","description":"Every voter is mailed a ballot."},"ranked_choice":{"type":"boolean","description":"Some contests in this election use ranked choice voting."},"fusion_voting":{"type":"boolean","description":"Candidates can appear on more than one party’s line (New York, Connecticut)."},"partisan_primary":{"type":"boolean","description":"A party primary."},"primary":{"type":"string","description":"Which primary, where a state holds several."},"consolidated_with":{"type":"string","description":"Elections held on the same ballot."},"runoff_date":{"type":"string","description":"Date of a runoff, if one is needed.","format":"date"},"runoff_election_date":{"type":"string","description":"Date of the runoff election.","format":"date"},"top_two_primary_date":{"type":"string","description":"Date of the top-two primary that chose these candidates.","format":"date"},"louisiana_open_primary":{"type":"boolean","description":"Louisiana’s open (jungle) primary."},"note":{"type":"string","description":"Anything else voters should know."},"historical":{"type":"boolean","description":"A past election kept for its results and history (the November 5, 2024 general, for example). Never shown to voters on endorsedby.org."}}}}},"ElectionSummary":{"allOf":[{"$ref":"#/components/schemas/Election"},{"type":"object","required":["contest_count","endorsement_count","result_count"],"properties":{"contest_count":{"type":"integer","description":"Contests on the ballot."},"endorsement_count":{"type":"integer","description":"Endorsements in those contests."},"result_count":{"type":"integer","description":"Contests on the ballot with a result."}}}]},"District":{"type":"object","description":"The district a contest is voted in.","required":["id","ocd_id","name","level","type","state"],"properties":{"id":{"type":"integer","description":"District id. Matches an officeholder’s district_id."},"ocd_id":{"type":["string","null"],"description":"Open Civic Data division id, used in districts= filters."},"name":{"type":"string","description":"District name."},"level":{"type":"string","description":"Level of government.","enum":["federal","state","county","city","school","special"]},"type":{"type":"string","description":"Kind of district: statewide, congressional, state_upper, state_lower, county, council, school, …"},"state":{"type":["string","null"],"pattern":"^[A-Z]{2}$","description":"USPS state code.","examples":["CA"]}}},"Candidate":{"type":"object","required":["id","full_name","website_url","photo_url"],"properties":{"id":{"type":"integer","description":"Candidate id."},"full_name":{"type":"string","description":"Full name."},"website_url":{"type":["string","null"],"description":"Campaign website.","format":"uri"},"photo_url":{"type":["string","null"],"description":"Photo.","format":"uri"}}},"Choice":{"type":"object","description":"A line a voter can mark: a candidate, or a Yes/No answer.","required":["id","label","measure_answer","option","ballot_designation","withdrawn","incumbent","vote_meaning","candidate","party","party_label","result"],"properties":{"id":{"type":"integer","description":"Choice id: the choice_id endorsements refer to."},"label":{"type":"string","description":"As printed: the candidate’s ballot name, or Yes / No."},"measure_answer":{"type":["string","null"],"description":"yes or no for a measure or retention question; null for a candidate.","enum":["yes","no",null]},"option":{"type":["string","null"],"description":"A special line: none_of_these (Nevada).","enum":["none_of_these",null]},"ballot_designation":{"type":["string","null"],"description":"Occupation line printed under the name, e.g. “Transit Planner”."},"withdrawn":{"type":"boolean","description":"The candidate withdrew but is still printed on the ballot."},"incumbent":{"type":["boolean","null"],"description":"The candidate holds this seat now: true or false where we know, null where we don’t yet (and for a measure answer)."},"vote_meaning":{"type":["string","null"],"description":"For a measure answer: what a Yes (or No) vote means, as the election office words it."},"candidate":{"description":"The candidate; null for a measure answer.","oneOf":[{"$ref":"#/components/schemas/Candidate"},{"type":"null"}]},"party":{"description":"The party (or party preference) printed beside the name.","oneOf":[{"$ref":"#/components/schemas/Party"},{"type":"null"}]},"party_label":{"type":["string","null"],"description":"The party text exactly as printed, where it differs (fusion lines, top-two preferences)."},"result":{"description":"This choice’s votes, once the contest has a result; null before then.","oneOf":[{"$ref":"#/components/schemas/ChoiceResult"},{"type":"null"}]}}},"ChoiceResult":{"type":"object","description":"One choice’s line in the result.","required":["votes","vote_pct","winner"],"properties":{"votes":{"type":["integer","null"],"description":"Votes for this choice. null only on a party-reported result, which names a winner without counts."},"vote_pct":{"type":["number","null"],"description":"Share of the contest’s votes, 0 to 100, as the source gives it (three decimals)."},"winner":{"type":["boolean","null"],"description":"true for a winner (for vote-for-N, each of the N), false for a choice that lost; null when not decided yet or not stated, such as a race headed to a runoff."}}},"OtherResultLine":{"type":"object","description":"A line the source reports that isn’t a choice on the printed ballot.","required":["label","votes","write_in"],"properties":{"label":{"type":["string","null"],"description":"As the source reports it: a write-in’s name, “Write-in”, or another line not on the printed ballot."},"votes":{"type":["integer","null"],"description":"Votes for it."},"write_in":{"type":"boolean","description":"A write-in candidate."}}},"ContestResult":{"type":"object","description":"A contest’s result. Each choice’s votes are on the choice (choices[].result).","required":["status","source_kind","party_reported","total_votes","precincts_reporting","precincts_total","source","official_url","more_sources","write_in_votes","blank_votes","over_votes","under_votes","other_choices","rcv_rounds","notes","fetched_at"],"properties":{"status":{"type":"string","description":"unofficial (election-night and canvass counts, still changing) or certified (the canvass is certified).","enum":["unofficial","certified"]},"source_kind":{"type":"string","description":"Where the result comes from: official (a state, county, city or federal election office), compiled (certified official returns compiled by a research group, such as the MIT Election Data and Science Lab’s precinct returns, summed to the contest) or party (a state or national party’s own announcement of the winner, with no vote counts; replaced as soon as an official result appears).","enum":["official","compiled","party"]},"party_reported":{"type":"boolean","description":"true when the result is a party’s announcement (source_kind party), not an official count."},"total_votes":{"type":["integer","null"],"description":"Votes cast in the contest: the printed choices plus write-ins. null on a party-reported result."},"precincts_reporting":{"type":["integer","null"],"description":"Precincts counted so far, where the source says."},"precincts_total":{"type":["integer","null"],"description":"Precincts in the contest, where the source says."},"source":{"type":"object","description":"The source of the result.","required":["name","url","contest_name","updated_at"],"properties":{"name":{"type":"string","description":"Who published the result.","examples":["California Secretary of State"]},"url":{"type":"string","description":"Where it was published: the contest’s own results page where the source has one. Every result has one.","format":"uri"},"contest_name":{"type":["string","null"],"description":"The contest’s name in the source, which may differ from the printed title."},"updated_at":{"type":["string","null"],"description":"When the source last updated it, if it says.","format":"date-time"}}},"official_url":{"type":["string","null"],"description":"For a compiled result, the state’s own certified results, where we have them.","format":"uri"},"more_sources":{"type":"array","description":"Further announcements of the same result (party-reported results that more than one party published).","items":{"type":"object","required":["name","url"],"properties":{"name":{"type":["string","null"],"description":"Who published it."},"url":{"type":"string","description":"Where.","format":"uri"}}}},"write_in_votes":{"type":["integer","null"],"description":"Votes for write-in candidates, where the source counts them."},"blank_votes":{"type":["integer","null"],"description":"Blank votes, where the source counts them."},"over_votes":{"type":["integer","null"],"description":"Overvotes, where the source counts them."},"under_votes":{"type":["integer","null"],"description":"Undervotes, where the source counts them."},"other_choices":{"type":"array","description":"Lines the source reports that aren’t on the printed ballot, such as named write-ins.","items":{"$ref":"#/components/schemas/OtherResultLine"}},"rcv_rounds":{"type":["array","null"],"items":{"type":"object"},"description":"Ranked choice: the rounds of the official tabulation, as the source reports them; null for other contests or where we don’t have them."},"notes":{"type":["string","null"],"description":"Anything else about the result, such as how a ranked-choice winner was decided."},"fetched_at":{"type":"string","description":"When we last read it from the source.","format":"date-time"}}},"ContestResultBrief":{"type":"object","description":"A contest’s result in brief: GET /v1/contests/{id} has all of it.","required":["status","source_kind","party_reported","total_votes","source_url"],"properties":{"status":{"type":"string","description":"unofficial (election-night and canvass counts, still changing) or certified (the canvass is certified).","enum":["unofficial","certified"]},"source_kind":{"type":"string","description":"Where the result comes from: official (a state, county, city or federal election office), compiled (certified official returns compiled by a research group, such as the MIT Election Data and Science Lab’s precinct returns, summed to the contest) or party (a state or national party’s own announcement of the winner, with no vote counts; replaced as soon as an official result appears).","enum":["official","compiled","party"]},"party_reported":{"type":"boolean","description":"true when the result is a party’s announcement (source_kind party), not an official count."},"total_votes":{"type":["integer","null"],"description":"Votes cast in the contest. null on a party-reported result."},"source_url":{"type":"string","description":"Where the result was published.","format":"uri"}}},"ContestBase":{"type":"object","required":["id","title","short_title","kind","vote_method","votes_allowed","ranks_allowed","measure_code","description","ballot_text","full_text_url","unopposed","ballot_order","party","details","source_url","district","result","election"],"properties":{"id":{"type":"integer","description":"Contest id."},"title":{"type":"string","description":"Title as printed on the ballot."},"short_title":{"type":["string","null"],"description":"Shorter title."},"kind":{"type":"string","description":"candidate (an office), measure (a proposition) or retention (keep a judge?).","enum":["candidate","measure","retention"]},"vote_method":{"type":"string","description":"How votes are cast: plurality (vote for one), multi_seat (vote for up to votes_allowed), ranked_choice (rank up to ranks_allowed), approval, or yes_no.","enum":["plurality","multi_seat","ranked_choice","approval","yes_no"]},"votes_allowed":{"type":["integer","null"],"description":"How many choices a voter may mark (vote for N)."},"ranks_allowed":{"type":["integer","null"],"description":"Ranked choice: how many choices a voter may rank."},"measure_code":{"type":["string","null"],"description":"The measure’s letter or number: “K”, “Prop 1”."},"description":{"type":["string","null"],"description":"A short description or question. For a measure with ballot_text, often a summary."},"ballot_text":{"type":["string","null"],"description":"A measure’s wording exactly as printed on the ballot; null where we don’t have it yet."},"full_text_url":{"type":["string","null"],"description":"The official full text of a measure.","format":"uri"},"unopposed":{"type":"boolean","description":"A candidate contest with no more candidates still running than seats (the same rule endorsedby.org uses). Always false for measures."},"ballot_order":{"type":["integer","null"],"description":"Position on the printed ballot."},"party":{"description":"For a party primary, the party.","oneOf":[{"$ref":"#/components/schemas/Party"},{"type":"null"}]},"details":{"type":"object","description":"Facts about the contest. Only keys that apply are present.","required":[],"properties":{"uncontested":{"type":"boolean","description":"Only one candidate is running."},"nonpartisan":{"type":"boolean","description":"Candidates appear without a party."},"measure_type":{"type":"string","description":"bond, charter_amendment, initiative, referendum, tax, …"},"vote_required":{"type":"string","description":"Share of the vote needed to pass, e.g. \"2/3\" or \"55%\"."},"majority_required":{"type":"boolean","description":"A candidate needs a majority to win outright (else a runoff)."},"amount":{"type":"number","description":"Dollar amount of a bond or tax measure."},"term":{"type":"string","description":"Length of the term, e.g. \"4 years\"."},"short_term":{"type":"boolean","description":"The seat is for the remainder of an unexpired term."},"justice":{"type":"string","description":"The judge or justice a retention question is about."},"runoff_date":{"type":"string","description":"Date of a runoff, if one is needed.","format":"date"},"stage":{"type":"string","description":"Stage of a multi-round contest."},"elected_at_large":{"type":"boolean","description":"Elected by the whole jurisdiction, not by district."},"none_of_these":{"type":"boolean","description":"The ballot offers “None of these candidates” (Nevada)."},"primary_nominating":{"type":"boolean","description":"The primary nominates a party’s candidate."},"winners":{"type":"integer","description":"Number of winners, where it differs from votes_allowed."},"ref_type":{"type":"string","description":"Kind of referendum."},"party_lines_unverified":{"type":"boolean","description":"The candidates are known, but their party lines haven’t been checked against a printed ballot yet."},"question_wording":{"type":"string","description":"notice where description summarizes an official notice (Ohio) instead of quoting the ballot."}}},"source_url":{"type":["string","null"],"description":"The official source the contest was loaded from.","format":"uri"},"district":{"$ref":"#/components/schemas/District"},"result":{"description":"The election result, once a source has one; null before then.","oneOf":[{"$ref":"#/components/schemas/ContestResult"},{"type":"null"}]},"election":{"$ref":"#/components/schemas/Election"}}},"ContestBaseNoElection":{"type":"object","description":"A contest, in an export: its election is given once at the top.","required":["id","title","short_title","kind","vote_method","votes_allowed","ranks_allowed","measure_code","description","ballot_text","full_text_url","unopposed","ballot_order","party","details","source_url","district","result"],"properties":{"id":{"type":"integer","description":"Contest id."},"title":{"type":"string","description":"Title as printed on the ballot."},"short_title":{"type":["string","null"],"description":"Shorter title."},"kind":{"type":"string","description":"candidate (an office), measure (a proposition) or retention (keep a judge?).","enum":["candidate","measure","retention"]},"vote_method":{"type":"string","description":"How votes are cast: plurality (vote for one), multi_seat (vote for up to votes_allowed), ranked_choice (rank up to ranks_allowed), approval, or yes_no.","enum":["plurality","multi_seat","ranked_choice","approval","yes_no"]},"votes_allowed":{"type":["integer","null"],"description":"How many choices a voter may mark (vote for N)."},"ranks_allowed":{"type":["integer","null"],"description":"Ranked choice: how many choices a voter may rank."},"measure_code":{"type":["string","null"],"description":"The measure’s letter or number: “K”, “Prop 1”."},"description":{"type":["string","null"],"description":"A short description or question. For a measure with ballot_text, often a summary."},"ballot_text":{"type":["string","null"],"description":"A measure’s wording exactly as printed on the ballot; null where we don’t have it yet."},"full_text_url":{"type":["string","null"],"description":"The official full text of a measure.","format":"uri"},"unopposed":{"type":"boolean","description":"A candidate contest with no more candidates still running than seats (the same rule endorsedby.org uses). Always false for measures."},"ballot_order":{"type":["integer","null"],"description":"Position on the printed ballot."},"party":{"description":"For a party primary, the party.","oneOf":[{"$ref":"#/components/schemas/Party"},{"type":"null"}]},"details":{"type":"object","description":"Facts about the contest. Only keys that apply are present.","required":[],"properties":{"uncontested":{"type":"boolean","description":"Only one candidate is running."},"nonpartisan":{"type":"boolean","description":"Candidates appear without a party."},"measure_type":{"type":"string","description":"bond, charter_amendment, initiative, referendum, tax, …"},"vote_required":{"type":"string","description":"Share of the vote needed to pass, e.g. \"2/3\" or \"55%\"."},"majority_required":{"type":"boolean","description":"A candidate needs a majority to win outright (else a runoff)."},"amount":{"type":"number","description":"Dollar amount of a bond or tax measure."},"term":{"type":"string","description":"Length of the term, e.g. \"4 years\"."},"short_term":{"type":"boolean","description":"The seat is for the remainder of an unexpired term."},"justice":{"type":"string","description":"The judge or justice a retention question is about."},"runoff_date":{"type":"string","description":"Date of a runoff, if one is needed.","format":"date"},"stage":{"type":"string","description":"Stage of a multi-round contest."},"elected_at_large":{"type":"boolean","description":"Elected by the whole jurisdiction, not by district."},"none_of_these":{"type":"boolean","description":"The ballot offers “None of these candidates” (Nevada)."},"primary_nominating":{"type":"boolean","description":"The primary nominates a party’s candidate."},"winners":{"type":"integer","description":"Number of winners, where it differs from votes_allowed."},"ref_type":{"type":"string","description":"Kind of referendum."},"party_lines_unverified":{"type":"boolean","description":"The candidates are known, but their party lines haven’t been checked against a printed ballot yet."},"question_wording":{"type":"string","description":"notice where description summarizes an official notice (Ohio) instead of quoting the ballot."}}},"source_url":{"type":["string","null"],"description":"The official source the contest was loaded from.","format":"uri"},"district":{"$ref":"#/components/schemas/District"},"result":{"description":"The election result, once a source has one; null before then.","oneOf":[{"$ref":"#/components/schemas/ContestResult"},{"type":"null"}]}}},"Contest":{"allOf":[{"$ref":"#/components/schemas/ContestBase"},{"type":"object","required":["choices"],"properties":{"choices":{"type":"array","description":"Every choice, in ballot order.","items":{"$ref":"#/components/schemas/Choice"}}}}]},"Pick":{"type":"object","required":["endorsement_id","choice_id","label","measure_answer","rank","statement","source_url","published_on","added_by"],"properties":{"endorsement_id":{"type":"integer","description":"The endorsement’s id (GET /v1/endorsements/{id})."},"choice_id":{"type":"integer","description":"The Choice endorsed."},"label":{"type":"string","description":"That choice’s label."},"measure_answer":{"type":["string","null"],"description":"yes or no for a measure.","enum":["yes","no",null]},"rank":{"type":["integer","null"],"description":"Ranked choice: 1 for first choice, 2 for second, …; null otherwise."},"statement":{"type":["string","null"],"description":"What the endorser said about it."},"source_url":{"type":["string","null"],"description":"Where the endorsement was published.","format":"uri"},"published_on":{"type":["string","null"],"description":"When it was published.","format":"date"},"added_by":{"type":"string","description":"How the endorsement got here: automatic (added by our automatic watch of the endorser’s own page), staff (entered by hand by BallotData staff) or data_load (loaded in a researched data pass).","enum":["automatic","staff","data_load"]}}},"Position":{"type":"object","description":"One endorser’s position in a contest.","required":["endorser","picks"],"properties":{"endorser":{"$ref":"#/components/schemas/EndorserRef"},"picks":{"type":"array","description":"One pick for vote-for-one, up to N for vote-for-N, the ranking for ranked choice, Yes or No for a measure.","items":{"$ref":"#/components/schemas/Pick"}}}},"ContestDetail":{"allOf":[{"$ref":"#/components/schemas/Contest"},{"type":"object","required":["endorsements"],"properties":{"endorsements":{"type":"array","description":"Every (matching) endorser’s position, by endorser name.","items":{"$ref":"#/components/schemas/Position"}}}}]},"Endorsement":{"type":"object","description":"The basics of an endorsement.","required":["id","rank","published_on","added_by","endorser","contest","election","choice"],"properties":{"id":{"type":"integer","description":"Endorsement id: GET /v1/endorsements/{id} has the full record."},"rank":{"type":["integer","null"],"description":"Ranked choice: the rank the endorser gave; null otherwise."},"published_on":{"type":["string","null"],"description":"When it was published.","format":"date"},"added_by":{"type":"string","description":"How the endorsement got here: automatic (added by our automatic watch of the endorser’s own page), staff (entered by hand by BallotData staff) or data_load (loaded in a researched data pass).","enum":["automatic","staff","data_load"]},"endorser":{"type":"object","description":"Who made the endorsement.","required":["id","slug","name"],"properties":{"id":{"type":"integer","description":"Endorser id."},"slug":{"type":"string","description":"Endorser slug."},"name":{"type":"string","description":"Endorser name."}}},"contest":{"type":"object","description":"The contest.","required":["id","title"],"properties":{"id":{"type":"integer","description":"Contest id."},"title":{"type":"string","description":"Title as printed on the ballot."}}},"election":{"type":"object","description":"The contest’s election.","required":["id","slug","date"],"properties":{"id":{"type":"integer","description":"Election id."},"slug":{"type":"string","description":"Election slug."},"date":{"type":"string","description":"Election day.","format":"date"}}},"choice":{"type":"object","description":"The choice endorsed.","required":["id","label","measure_answer"],"properties":{"id":{"type":"integer","description":"Choice id."},"label":{"type":"string","description":"As printed: the candidate’s ballot name, or Yes / No."},"measure_answer":{"type":["string","null"],"description":"yes or no for a measure; null for a candidate.","enum":["yes","no",null]}}}}},"EndorsementDetail":{"type":"object","description":"An endorsement in full.","required":["id","rank","statement","source_url","published_on","retrieved_at","added_by","endorser","contest","choice"],"properties":{"id":{"type":"integer","description":"Endorsement id."},"rank":{"type":["integer","null"],"description":"Ranked choice: the rank the endorser gave; null otherwise."},"statement":{"type":["string","null"],"description":"What the endorser said."},"source_url":{"type":["string","null"],"description":"Where it was published.","format":"uri"},"published_on":{"type":["string","null"],"description":"When it was published.","format":"date"},"retrieved_at":{"type":"string","description":"When we collected it.","format":"date-time"},"added_by":{"type":"string","description":"How the endorsement got here: automatic (added by our automatic watch of the endorser’s own page), staff (entered by hand by BallotData staff) or data_load (loaded in a researched data pass).","enum":["automatic","staff","data_load"]},"endorser":{"type":"object","description":"Who made the endorsement.","required":["id","slug","name","category","description","website_url","logo_url","state","party"],"properties":{"id":{"type":"integer","description":"Endorser id."},"slug":{"type":"string","description":"Endorser slug."},"name":{"type":"string","description":"Endorser name."},"category":{"$ref":"#/components/schemas/Category"},"description":{"type":["string","null"],"description":"About the endorser."},"website_url":{"type":["string","null"],"description":"Endorser’s website.","format":"uri"},"logo_url":{"type":["string","null"],"description":"Logo image.","format":"uri"},"state":{"type":["string","null"],"pattern":"^[A-Z]{2}$","description":"Home state; null for national endorsers.","examples":["CA"]},"party":{"description":"The party, for a party or party club.","oneOf":[{"$ref":"#/components/schemas/Party"},{"type":"null"}]}}},"contest":{"$ref":"#/components/schemas/ContestBase"},"choice":{"$ref":"#/components/schemas/Choice"}}},"HistoryValues":{"type":"object","description":"An endorsement’s values at one point in time.","required":["choice_id","choice_label","rank","statement","source_url","published_on"],"properties":{"choice_id":{"type":"integer","description":"The choice endorsed."},"choice_label":{"type":["string","null"],"description":"That choice’s label as printed; null if the choice has since been removed from the ballot."},"rank":{"type":["integer","null"],"description":"Ranked choice: the rank; null otherwise."},"statement":{"type":["string","null"],"description":"What the endorser said."},"source_url":{"type":["string","null"],"description":"Where it was published.","format":"uri"},"published_on":{"type":["string","null"],"description":"When it was published.","format":"date"}}},"HistoryEntry":{"type":"object","description":"One change to an endorsement.","required":["at","change","changed","before","after"],"properties":{"at":{"type":"string","description":"When the change was made. For endorsements collected before we kept history (October 27, 2026), created is when we collected it.","format":"date-time"},"change":{"type":"string","description":"What happened.","enum":["created","updated","deleted"]},"changed":{"type":"array","description":"For updated: the fields that changed. Empty otherwise.","items":{"type":"string","enum":["choice_id","rank","statement","source_url","published_on"]}},"before":{"description":"The values before the change; null for created.","oneOf":[{"$ref":"#/components/schemas/HistoryValues"},{"type":"null"}]},"after":{"description":"The values after the change; null for deleted.","oneOf":[{"$ref":"#/components/schemas/HistoryValues"},{"type":"null"}]}}},"CandidateContest":{"type":"object","description":"A contest a candidate is on.","required":["contest","district","election","choice","result","endorsement_count"],"properties":{"contest":{"type":"object","required":["id","title","kind","vote_method","votes_allowed","ranks_allowed"],"properties":{"id":{"type":"integer","description":"Contest id."},"title":{"type":"string","description":"Title as printed on the ballot."},"kind":{"type":"string","description":"candidate or retention.","enum":["candidate","measure","retention"]},"vote_method":{"type":"string","description":"How votes are cast.","enum":["plurality","multi_seat","ranked_choice","approval","yes_no"]},"votes_allowed":{"type":["integer","null"],"description":"Vote for N."},"ranks_allowed":{"type":["integer","null"],"description":"Ranked choice: how many choices a voter may rank."}}},"district":{"type":"object","required":["id","ocd_id","name","level","state"],"properties":{"id":{"type":"integer","description":"District id."},"ocd_id":{"type":["string","null"],"description":"OCD division id."},"name":{"type":"string","description":"District name."},"level":{"type":"string","description":"Level.","enum":["federal","state","county","city","school","special"]},"state":{"type":["string","null"],"pattern":"^[A-Z]{2}$","description":"USPS state code.","examples":["CA"]}}},"election":{"type":"object","required":["id","slug","name","date"],"properties":{"id":{"type":"integer","description":"Election id."},"slug":{"type":"string","description":"Election slug."},"name":{"type":"string","description":"Election name."},"date":{"type":"string","description":"Election day.","format":"date"}}},"choice":{"type":"object","required":["id","label","ballot_designation","withdrawn","party","party_label","result"],"properties":{"id":{"type":"integer","description":"Choice id."},"label":{"type":"string","description":"The name as printed."},"ballot_designation":{"type":["string","null"],"description":"Occupation line printed under the name."},"withdrawn":{"type":"boolean","description":"Withdrew but still printed."},"party":{"description":"The party printed beside the name.","oneOf":[{"$ref":"#/components/schemas/Party"},{"type":"null"}]},"party_label":{"type":["string","null"],"description":"The party text exactly as printed, where it differs."},"result":{"description":"The candidate’s votes and whether they won, once the contest has a result.","oneOf":[{"$ref":"#/components/schemas/ChoiceResult"},{"type":"null"}]}}},"result":{"description":"The contest’s result, once a source has one.","oneOf":[{"$ref":"#/components/schemas/ContestResultBrief"},{"type":"null"}]},"endorsement_count":{"type":"integer","description":"Endorsers who endorsed this candidate in this contest."}}},"CandidateSummary":{"type":"object","required":["id","full_name","website_url","photo_url","incumbent","contests"],"properties":{"id":{"type":"integer","description":"Candidate id."},"full_name":{"type":"string","description":"Full name."},"website_url":{"type":["string","null"],"description":"Campaign website.","format":"uri"},"photo_url":{"type":["string","null"],"description":"Photo.","format":"uri"},"incumbent":{"type":["boolean","null"],"description":"Holds the seat now: true or false where we know, null where we don’t yet."},"contests":{"type":"array","description":"The contests they’re on that you can read, newest election first.","items":{"$ref":"#/components/schemas/CandidateContest"}}}},"CandidateEndorsement":{"type":"object","description":"One endorsement a candidate received.","required":["endorsement_id","endorser","rank","statement","source_url","published_on","added_by"],"properties":{"endorsement_id":{"type":"integer","description":"Endorsement id."},"endorser":{"$ref":"#/components/schemas/EndorserRef"},"rank":{"type":["integer","null"],"description":"Ranked choice: the rank the endorser gave; null otherwise."},"statement":{"type":["string","null"],"description":"What the endorser said."},"source_url":{"type":["string","null"],"description":"Where it was published.","format":"uri"},"published_on":{"type":["string","null"],"description":"When it was published.","format":"date"},"added_by":{"type":"string","description":"How the endorsement got here: automatic (added by our automatic watch of the endorser’s own page), staff (entered by hand by BallotData staff) or data_load (loaded in a researched data pass).","enum":["automatic","staff","data_load"]}}},"Issue":{"type":"object","description":"A policy issue candidates take positions on.","required":["slug","name","question","description"],"properties":{"slug":{"type":"string","description":"Issue slug, for issues= filters."},"name":{"type":"string","description":"Short name: “Abortion”."},"question":{"type":"string","description":"The yes/no question a position’s stance answers."},"description":{"type":"string","description":"What the issue covers."}}},"IssuePositionSource":{"type":"object","description":"A link a position was taken from.","required":["url","kind","publisher","title","published_on"],"properties":{"url":{"type":"string","description":"Where the position comes from.","format":"uri"},"kind":{"type":"string","description":"What kind of source: the candidate’s own site, an official roll-call vote record, an official statement, a published candidate questionnaire, a debate, or news reporting.","enum":["candidate_website","voting_record","official_statement","questionnaire","debate","news"]},"publisher":{"type":"string","description":"Who published it: “U.S. Senate”, “The Atlanta Journal-Constitution”."},"title":{"type":["string","null"],"description":"Its title."},"published_on":{"type":["string","null"],"description":"When it was published, where known.","format":"date"}}},"IssuePosition":{"type":"object","description":"Where a candidate stands on one issue, as they stated it or as their votes in Congress show it. Never inferred from party or endorsements.","required":["id","issue","stance","summary","quote","checked_on","sources"],"properties":{"id":{"type":"integer","description":"Position id."},"issue":{"type":"object","required":["slug","name"],"properties":{"slug":{"type":"string","description":"Issue slug."},"name":{"type":"string","description":"Issue name."}}},"stance":{"type":["string","null"],"enum":["supports","opposes","mixed",null],"description":"The answer to the issue’s question: supports (yes), opposes (no), mixed (yes to part, or yes with limits the summary spells out), or null for a stated position that doesn’t answer it either way."},"summary":{"type":"string","description":"Where they stand, in neutral words: what they said, or how they voted."},"quote":{"type":["string","null"],"description":"Their own words, verbatim, where a source has them."},"checked_on":{"type":"string","description":"When the sources were last read.","format":"date"},"sources":{"type":"array","description":"Every link the position was taken from; at least one.","items":{"$ref":"#/components/schemas/IssuePositionSource"}}}},"IssuePositionItem":{"allOf":[{"$ref":"#/components/schemas/IssuePosition"},{"type":"object","required":["candidate","contests"],"properties":{"candidate":{"type":"object","required":["id","full_name"],"properties":{"id":{"type":"integer","description":"Candidate id."},"full_name":{"type":"string","description":"Full name."}}},"contests":{"type":"array","description":"The candidate’s contests that you can read (and that match the filters), newest election first.","items":{"type":"object","required":["id","title","election","state"],"properties":{"id":{"type":"integer","description":"Contest id."},"title":{"type":"string","description":"Title as printed on the ballot."},"election":{"type":"object","required":["slug","date"],"properties":{"slug":{"type":"string","description":"Election slug."},"date":{"type":"string","description":"Election day.","format":"date"}}},"state":{"type":"string","pattern":"^[A-Z]{2}$","description":"USPS state code.","examples":["CA"]}}}}}}]},"CandidateDetail":{"type":"object","required":["id","full_name","website_url","photo_url","incumbent","contests","issue_positions"],"properties":{"id":{"type":"integer","description":"Candidate id."},"full_name":{"type":"string","description":"Full name."},"website_url":{"type":["string","null"],"description":"Campaign website.","format":"uri"},"photo_url":{"type":["string","null"],"description":"Photo.","format":"uri"},"incumbent":{"type":["boolean","null"],"description":"Holds the seat now: true or false where we know, null where we don’t yet."},"contests":{"type":"array","description":"The contests they’re on that you can read, newest election first, each with the endorsements received.","items":{"allOf":[{"$ref":"#/components/schemas/CandidateContest"},{"type":"object","required":["endorsements"],"properties":{"endorsements":{"type":"array","description":"Every endorsement they received in this contest, by endorser name.","items":{"$ref":"#/components/schemas/CandidateEndorsement"}}}}]}},"issue_positions":{"type":"array","description":"Where they stand on common issues (GET /v1/issues), in issue order, each with its sources. Empty where we have none yet.","items":{"$ref":"#/components/schemas/IssuePosition"}}}},"Change":{"type":"object","description":"One change to an endorsement.","required":["id","at","change","changed","endorsement_id","endorser","contest","election","before","after"],"properties":{"id":{"type":"integer","description":"The change’s id: increasing and unique. Pages are in this order."},"at":{"type":"string","description":"When the change was made.","format":"date-time"},"change":{"type":"string","description":"What happened.","enum":["created","updated","deleted"]},"changed":{"type":"array","description":"For updated: the fields that changed. Empty otherwise.","items":{"type":"string","enum":["choice_id","rank","statement","source_url","published_on"]}},"endorsement_id":{"type":"integer","description":"The endorsement changed."},"endorser":{"type":"object","required":["id","slug","name","category"],"properties":{"id":{"type":"integer","description":"Endorser id."},"slug":{"type":["string","null"],"description":"Endorser slug; null if the endorser has since been removed."},"name":{"type":["string","null"],"description":"Endorser name; null if the endorser has since been removed."},"category":{"description":"Category.","oneOf":[{"$ref":"#/components/schemas/Category"},{"type":"null"}]}}},"contest":{"type":"object","required":["id","title"],"properties":{"id":{"type":"integer","description":"Contest id."},"title":{"type":"string","description":"Title as printed on the ballot."}}},"election":{"type":"object","required":["id","slug","date"],"properties":{"id":{"type":"integer","description":"Election id."},"slug":{"type":"string","description":"Election slug."},"date":{"type":"string","description":"Election day.","format":"date"}}},"before":{"description":"The values before the change; null for created.","oneOf":[{"$ref":"#/components/schemas/HistoryValues"},{"type":"null"}]},"after":{"description":"The values after the change; null for deleted.","oneOf":[{"$ref":"#/components/schemas/HistoryValues"},{"type":"null"}]}}},"ElectionExport":{"type":"object","required":["election","contests"],"properties":{"election":{"$ref":"#/components/schemas/Election"},"contests":{"type":"array","description":"Every contest you can read in the election, in ballot order: races from federal down, then measures.","items":{"allOf":[{"$ref":"#/components/schemas/ContestBaseNoElection"},{"type":"object","required":["choices","endorsements"],"properties":{"choices":{"type":"array","description":"Every choice, in ballot order.","items":{"$ref":"#/components/schemas/Choice"}},"endorsements":{"type":"array","description":"Every endorser’s position, by endorser name.","items":{"$ref":"#/components/schemas/Position"}}}}]}}}},"Filter":{"type":"object","description":"Which endorsements: fields combine with AND, values within a field with OR, and a missing field matches everything. Each field takes at most 500 values.","required":[],"properties":{"categories":{"type":"array","description":"Endorser category slugs (GET /v1/endorser-categories).","items":{"type":"string"}},"endorsers":{"type":"array","description":"Endorser slugs.","items":{"type":"string"}},"endorser_states":{"type":"array","description":"Endorsers’ home states (USPS codes), or \"national\".","items":{"type":"string"}},"states":{"type":"array","description":"States the contests are voted in.","items":{"type":"string","pattern":"^[A-Z]{2}$","description":"USPS state code.","examples":["CA"]}},"elections":{"type":"array","description":"Election slugs.","items":{"type":"string"}},"contest_kinds":{"type":"array","description":"Contest kinds.","items":{"type":"string","enum":["candidate","measure","retention"]}},"levels":{"type":"array","description":"District levels.","items":{"type":"string","enum":["federal","state","county","city","school","special"]}},"types":{"type":"array","description":"Stream only: kinds of change.","items":{"type":"string","enum":["created","updated","deleted"]}}}},"Officeholder":{"type":"object","required":["person_id","name","given_name","family_name","office","office_title","level","state","district","district_ocd_id","district_id","party","party_abbreviation","term_start","term_end","email","phone","website_url","image_url","offices","social","source_urls","updated_at"],"properties":{"person_id":{"type":"string","description":"Open States person id; stays the same across terms and offices.","format":"uuid"},"name":{"type":"string","description":"Full name."},"given_name":{"type":["string","null"],"description":"Given name."},"family_name":{"type":["string","null"],"description":"Family name."},"office":{"type":"string","description":"The office.","enum":["us_senator","us_representative","governor","lieutenant_governor","attorney_general","secretary_of_state","chief_election_officer","treasurer","auditor","state_senator","state_representative","state_legislator","city_council"]},"office_title":{"type":"string","description":"Title the state uses: “Assemblymember”, “Delegate”."},"level":{"type":"string","description":"Level of government.","enum":["federal","state","city"]},"state":{"type":"string","pattern":"^[A-Z]{2}$","description":"USPS state code.","examples":["CA"]},"district":{"type":["string","null"],"description":"District as the legislature names it: \"12\", \"1A\", \"21st Middlesex\"."},"district_ocd_id":{"type":"string","description":"OCD division id of the seat."},"district_id":{"type":["integer","null"],"description":"The matching contest district id, where there is one."},"party":{"type":["string","null"],"description":"Party as published: “Democratic”, “Republican/Conservative”."},"party_abbreviation":{"type":["string","null"],"description":"Party abbreviation."},"term_start":{"type":["string","null"],"description":"Start of the current term.","format":"date"},"term_end":{"type":["string","null"],"description":"End of the current term.","format":"date"},"email":{"type":["string","null"],"description":"Office email.","format":"email"},"phone":{"type":["string","null"],"description":"Office phone (capitol office first)."},"website_url":{"type":["string","null"],"description":"Official page.","format":"uri"},"image_url":{"type":["string","null"],"description":"Official photo.","format":"uri"},"offices":{"type":"array","description":"Offices with their addresses and phones.","items":{"type":"object","required":[],"properties":{"classification":{"type":"string","description":"capitol, district, …"},"address":{"type":"string","description":"Mailing address."},"voice":{"type":"string","description":"Phone."},"fax":{"type":"string","description":"Fax."}}}},"social":{"type":"object","description":"Social accounts, where known.","required":[],"properties":{"twitter":{"type":"string","description":"Handle."},"facebook":{"type":"string","description":"Handle."},"instagram":{"type":"string","description":"Handle."},"youtube":{"type":"string","description":"Handle."}}},"source_urls":{"type":"array","description":"Where the data came from.","items":{"type":"string","format":"uri"}},"updated_at":{"type":"string","description":"Last refreshed.","format":"date-time"}}},"OfficeholderDetail":{"allOf":[{"$ref":"#/components/schemas/Officeholder"},{"type":"object","required":["seats"],"properties":{"seats":{"type":"array","description":"Every seat they hold (rarely more than one).","items":{"$ref":"#/components/schemas/Officeholder"}}}}]},"Coverage":{"type":"object","required":["state","name","election_count","contest_count","measure_count","endorser_count","endorsement_count","endorsed_contest_count","first_election","last_election","updated_at"],"properties":{"state":{"type":"string","pattern":"^[A-Z]{2}$","description":"USPS state code.","examples":["CA"]},"name":{"type":"string","description":"State name."},"election_count":{"type":"integer","description":"Elections."},"contest_count":{"type":"integer","description":"Contests."},"measure_count":{"type":"integer","description":"Of those, ballot measures."},"endorser_count":{"type":"integer","description":"Endorsers with endorsements here."},"endorsement_count":{"type":"integer","description":"Endorsements."},"endorsed_contest_count":{"type":"integer","description":"Contests with at least one endorsement."},"first_election":{"type":["string","null"],"description":"Earliest election.","format":"date"},"last_election":{"type":["string","null"],"description":"Latest election.","format":"date"},"updated_at":{"type":["string","null"],"description":"When a contest or endorsement last changed.","format":"date-time"}}},"Event":{"type":"object","required":["id","type","occurred_at","endorsement","changed","previous","endorser","contest","election","choice"],"properties":{"id":{"type":"string","description":"The change’s id: increasing and unique. Use it to drop repeats."},"type":{"type":"string","description":"What happened.","enum":["endorsement.created","endorsement.updated","endorsement.deleted"]},"occurred_at":{"type":"string","description":"When the change was made.","format":"date-time"},"endorsement":{"type":"object","required":["id","rank","statement","source_url","published_on","added_by"],"properties":{"id":{"type":"integer","description":"Endorsement id."},"rank":{"type":["integer","null"],"description":"Ranked choice rank."},"statement":{"type":["string","null"],"description":"What the endorser said."},"source_url":{"type":["string","null"],"description":"Where it was published."},"published_on":{"type":["string","null"],"description":"When.","format":"date"},"added_by":{"type":"string","description":"How the endorsement got here: automatic (added by our automatic watch of the endorser’s own page), staff (entered by hand by BallotData staff) or data_load (loaded in a researched data pass).","enum":["automatic","staff","data_load"]}}},"changed":{"type":"array","description":"For endorsement.updated: the fields that changed. Empty for other events.","items":{"type":"string","enum":["choice_id","rank","statement","source_url","published_on"]}},"previous":{"description":"For endorsement.updated: the values before the change. null for other events.","oneOf":[{"type":"object","required":["choice_id","rank","statement","source_url","published_on"],"properties":{"choice_id":{"type":"integer","description":"The choice endorsed before."},"rank":{"type":["integer","null"],"description":"Rank before."},"statement":{"type":["string","null"],"description":"Statement before."},"source_url":{"type":["string","null"],"description":"Source before."},"published_on":{"type":["string","null"],"description":"Date before.","format":"date"}}},{"type":"null"}]},"endorser":{"type":"object","required":["id","slug","name","category","state"],"properties":{"id":{"type":"integer","description":"Endorser id."},"slug":{"type":["string","null"],"description":"Endorser slug."},"name":{"type":["string","null"],"description":"Endorser name."},"category":{"description":"Category.","oneOf":[{"$ref":"#/components/schemas/Category"},{"type":"null"}]},"state":{"type":["string","null"],"description":"Home state, or null for national."}}},"contest":{"type":"object","required":["id","title","short_title","kind","vote_method","votes_allowed","ranks_allowed","measure_code","district"],"properties":{"id":{"type":"integer","description":"Contest id."},"title":{"type":["string","null"],"description":"Title."},"short_title":{"type":["string","null"],"description":"Short title."},"kind":{"type":["string","null"],"description":"candidate, measure or retention."},"vote_method":{"type":["string","null"],"description":"Vote method."},"votes_allowed":{"type":["integer","null"],"description":"Vote for N."},"ranks_allowed":{"type":["integer","null"],"description":"Ranks allowed."},"measure_code":{"type":["string","null"],"description":"Measure code."},"district":{"description":"District.","oneOf":[{"type":"object","required":["id","name","level","ocd_id","state"],"properties":{"id":{"type":"integer","description":"District id."},"name":{"type":"string","description":"Name."},"level":{"type":"string","description":"Level."},"ocd_id":{"type":["string","null"],"description":"OCD id."},"state":{"type":["string","null"],"description":"State."}}},{"type":"null"}]}}},"election":{"description":"Election.","oneOf":[{"type":"object","required":["id","slug","name","date","state","type"],"properties":{"id":{"type":"integer","description":"Election id."},"slug":{"type":"string","description":"Slug."},"name":{"type":"string","description":"Name."},"date":{"type":"string","description":"Date.","format":"date"},"state":{"type":["string","null"],"description":"State."},"type":{"type":"string","description":"Type."}}},{"type":"null"}]},"choice":{"type":"object","required":["id","label","measure_answer","candidate","party","withdrawn","incumbent"],"properties":{"id":{"type":"integer","description":"Choice id."},"label":{"type":["string","null"],"description":"As printed."},"measure_answer":{"type":["string","null"],"description":"yes or no.","enum":["yes","no",null]},"candidate":{"description":"Candidate.","oneOf":[{"type":"object","required":["id","full_name"],"properties":{"id":{"type":"integer","description":"Candidate id."},"full_name":{"type":"string","description":"Full name."}}},{"type":"null"}]},"party":{"description":"Party.","oneOf":[{"type":"object","required":["slug","name"],"properties":{"slug":{"type":"string","description":"Slug."},"name":{"type":"string","description":"Name."}}},{"type":"null"}]},"withdrawn":{"type":["boolean","null"],"description":"Withdrawn."},"incumbent":{"type":["boolean","null"],"description":"The candidate holds this seat now: true or false where we know, null where we don’t (and for a measure answer)."}}}}},"Subscription":{"type":"object","required":["id","name","filter","created_at","updated_at"],"properties":{"id":{"type":"integer","description":"Subscription id."},"name":{"type":"string","description":"Name, unique in your account."},"filter":{"$ref":"#/components/schemas/Filter"},"created_at":{"type":"string","description":"Created.","format":"date-time"},"updated_at":{"type":"string","description":"Last changed.","format":"date-time"}}},"SubscriptionInput":{"type":"object","required":["name"],"properties":{"name":{"type":"string","description":"1–100 letters, digits, spaces, dots, dashes or underscores.","pattern":"^[A-Za-z0-9][A-Za-z0-9 ._-]{0,99}$"},"filter":{"$ref":"#/components/schemas/Filter"}}}},"responses":{"BadRequest":{"description":"A parameter is missing or not valid.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_parameter":{"value":{"error":{"code":"invalid_parameter","message":"states: \"California\" isn't valid.","request_id":"8c1f2a4e-6b7d-4c3e-9f10-2a3b4c5d6e7f"}}},"invalid_cursor":{"value":{"error":{"code":"invalid_cursor","message":"That cursor is not valid.","request_id":"8c1f2a4e-6b7d-4c3e-9f10-2a3b4c5d6e7f"}}}}}}},"Unauthorized":{"description":"No API key, or one that isn’t valid, was revoked or expired.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"missing_api_key":{"value":{"error":{"code":"missing_api_key","message":"Send your API key as \"Authorization: Bearer <key>\".","request_id":"8c1f2a4e-6b7d-4c3e-9f10-2a3b4c5d6e7f"}}},"invalid_api_key":{"value":{"error":{"code":"invalid_api_key","message":"That API key is not valid.","request_id":"8c1f2a4e-6b7d-4c3e-9f10-2a3b4c5d6e7f"}}}}}}},"Forbidden":{"description":"The key can’t be used for this: a missing scope, an IP allowlist, an account not yet approved, or data the account hasn’t bought.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"insufficient_scope":{"value":{"error":{"code":"insufficient_scope","message":"This API key doesn't have the data:read scope.","request_id":"8c1f2a4e-6b7d-4c3e-9f10-2a3b4c5d6e7f"}}},"no_access":{"value":{"error":{"code":"no_access","message":"Your account has no API data yet, or its purchase has ended. Buy API access at ballotdata.ai with the email address you use here.","request_id":"8c1f2a4e-6b7d-4c3e-9f10-2a3b4c5d6e7f"}}},"outside_purchase":{"value":{"error":{"code":"outside_purchase","message":"Your account hasn't bought NY. It covers CA.","request_id":"8c1f2a4e-6b7d-4c3e-9f10-2a3b4c5d6e7f"}}},"account_pending":{"value":{"error":{"code":"account_pending","message":"Your account is waiting for approval.","request_id":"8c1f2a4e-6b7d-4c3e-9f10-2a3b4c5d6e7f"}}}}}}},"NotFound":{"description":"Nothing has that id or slug.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"not_found":{"value":{"error":{"code":"not_found","message":"No contest has that id.","request_id":"8c1f2a4e-6b7d-4c3e-9f10-2a3b4c5d6e7f"}}}}}}},"Conflict":{"description":"A subscription with that name already exists.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"conflict":{"value":{"error":{"code":"conflict","message":"A subscription with that name already exists.","request_id":"8c1f2a4e-6b7d-4c3e-9f10-2a3b4c5d6e7f"}}}}}}},"TooManyRequests":{"description":"Over a rate limit: your account’s requests a minute or a day (UTC), its open streams, or too many requests with a missing or wrong key from your address. Wait Retry-After seconds.","headers":{"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limit_exceeded":{"value":{"error":{"code":"rate_limit_exceeded","message":"Your plan allows 600 requests a minute.","request_id":"8c1f2a4e-6b7d-4c3e-9f10-2a3b4c5d6e7f"}}},"daily_limit_exceeded":{"value":{"error":{"code":"daily_limit_exceeded","message":"Your plan allows 200000 requests a day (UTC).","request_id":"8c1f2a4e-6b7d-4c3e-9f10-2a3b4c5d6e7f"}}},"too_many_streams":{"value":{"error":{"code":"too_many_streams","message":"Your plan allows 5 open stream connections.","request_id":"8c1f2a4e-6b7d-4c3e-9f10-2a3b4c5d6e7f"}}},"too_many_failures":{"value":{"error":{"code":"too_many_failures","message":"Too many requests with a missing or wrong API key.","request_id":"8c1f2a4e-6b7d-4c3e-9f10-2a3b4c5d6e7f"}}}}}}},"Gone":{"description":"The cursor is older than the stream keeps.","headers":{"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"cursor_expired":{"value":{"error":{"code":"cursor_expired","message":"That position is older than the stream keeps. Catch up with GET /v1/endorsements, then start again with from=latest.","request_id":"8c1f2a4e-6b7d-4c3e-9f10-2a3b4c5d6e7f"}}}}}}},"Unavailable":{"description":"Not ready yet on this server. Wait Retry-After seconds.","headers":{"Retry-After":{"description":"Seconds to wait.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"warming_up":{"value":{"error":{"code":"warming_up","message":"The stream is still loading on this server. Try again in a few seconds.","request_id":"8c1f2a4e-6b7d-4c3e-9f10-2a3b4c5d6e7f"}}},"not_available":{"value":{"error":{"code":"not_available","message":"Officeholder data is not loaded yet. Try again later.","request_id":"8c1f2a4e-6b7d-4c3e-9f10-2a3b4c5d6e7f"}}}}}}}}},"paths":{"/v1/states":{"get":{"operationId":"listStates","tags":["Elections"],"summary":"List states","description":"Every state, D.C. and territory, with its USPS and FIPS codes. Use the codes in states= filters.","responses":{"200":{"description":"Every state.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Every item.","items":{"$ref":"#/components/schemas/State"}}}},"example":{"data":[{"code":"AK","fips":"02","name":"Alaska"},{"code":"AL","fips":"01","name":"Alabama"},{"code":"AR","fips":"05","name":"Arkansas"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl \"https://api.ballotdata.ai/v1/states\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst states = await client.states.list();"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\nstates = client.states.list()"}]}},"/v1/elections":{"get":{"operationId":"listElections","tags":["Elections"],"summary":"List elections","description":"Elections, with their voting rules in details (all-mail, ranked choice, fusion, runoff dates) and how many contests, endorsements and results each has. Past elections kept for their results (the November 5, 2024 general in every state) are listed too, with details.historical; upcoming=true leaves them out. Use an election’s slug to ask for its contests or endorsements.","parameters":[{"name":"states","in":"query","description":"USPS codes. Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","pattern":"^[A-Z]{2}$","description":"USPS state code.","examples":["CA"]}},"style":"form","explode":false,"example":"CA"},{"name":"types","in":"query","description":"Kinds of election (general, primary, special, runoff and so on). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","enum":["general","primary","special","special_primary","runoff","consolidated"]}},"style":"form","explode":false,"example":"general"},{"name":"upcoming","in":"query","description":"true for only elections from yesterday on; false (the default) for every election.","schema":{"type":"boolean","default":false},"example":true},{"name":"from","in":"query","description":"Elections on or after this date.","schema":{"type":"string","format":"date"},"example":"2026-11-01"},{"name":"to","in":"query","description":"Elections on or before this date.","schema":{"type":"string","format":"date"},"example":"2026-12-31"},{"name":"lang","in":"query","description":"es for Spanish ballot text (titles, choices, measure text, statements) where the state publishes it; English otherwise. Names are never translated.","schema":{"type":"string","enum":["en","es"],"default":"en"},"example":"es"},{"name":"limit","in":"query","description":"Results per page, 1–500.","schema":{"type":"integer","minimum":1,"maximum":500,"default":100},"example":20},{"name":"cursor","in":"query","description":"Where the next page starts: the next_cursor from the previous page. Leave it out for the first page; it is opaque, so pass it back exactly as you got it.","schema":{"type":"string"},"example":"aTo0NDEw"}],"responses":{"200":{"description":"A page of elections, by id.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","description":"This page.","items":{"$ref":"#/components/schemas/ElectionSummary"}},"next_cursor":{"type":["string","null"],"description":"Pass as cursor= for the next page; null on the last page."}}},"example":{"data":[{"id":3,"slug":"ca-2026-11-03-general","name":"California General Election","short_name":"General Election","date":"2026-11-03","state":"CA","type":"general","primary_system":"top_two","info_url":"https://www.sos.ca.gov/elections/upcoming-elections","details":{"all_mail":true},"contest_count":1184,"endorsement_count":9632,"result_count":1012},{"id":812,"slug":"ca-2024-11-05-general","name":"California General Election","short_name":"General Election","date":"2024-11-05","state":"CA","type":"general","primary_system":"top_two","info_url":"https://www.sos.ca.gov/elections/upcoming-elections","details":{"all_mail":true,"historical":true},"contest_count":1630,"endorsement_count":0,"result_count":1630}],"next_cursor":"aToz"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl \"https://api.ballotdata.ai/v1/elections?states=CA&upcoming=true\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst { data: elections } = await client.elections.list({ states: 'CA', upcoming: true });"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\nelections = client.elections.list(states=\"CA\", upcoming=True).data"}]}},"/v1/coverage":{"get":{"operationId":"getCoverage","tags":["Elections"],"summary":"Coverage by state","description":"What the API has for each state: elections, contests, measures, endorsers and endorsements, and when they last changed. Handy for deciding what to sync.","parameters":[{"name":"upcoming","in":"query","description":"true (the default) counts only upcoming elections; false counts every election.","schema":{"type":"boolean","default":true},"example":false}],"responses":{"200":{"description":"One row per state.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Every item.","items":{"$ref":"#/components/schemas/Coverage"}}}},"example":{"data":[{"state":"CA","name":"California","election_count":1,"contest_count":1184,"measure_count":402,"endorser_count":611,"endorsement_count":9632,"endorsed_contest_count":730,"first_election":"2026-11-03","last_election":"2026-11-03","updated_at":"2026-10-21T14:02:11.408Z"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl \"https://api.ballotdata.ai/v1/coverage\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst coverage = await client.coverage.list();"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\ncoverage = client.coverage.list()"}]}},"/v1/contests":{"get":{"operationId":"listContests","tags":["Contests"],"summary":"List contests","description":"The races on the ballot, each with its district, its election, its voting rules, every choice in ballot order and, once a source has one, its result (votes for each choice and the winners). Filter by election, state, level, kind, district or result. To see who endorsed whom, use GET /v1/contests/{id} or /v1/endorsements.","parameters":[{"name":"elections","in":"query","description":"Election slugs (GET /v1/elections). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"ca-2026-11-03-general"},{"name":"states","in":"query","description":"States the contests are voted in (USPS codes). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","pattern":"^[A-Z]{2}$","description":"USPS state code.","examples":["CA"]}},"style":"form","explode":false,"example":"CA"},{"name":"levels","in":"query","description":"Levels of the contest’s district, from federal down to school and special districts. Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","enum":["federal","state","county","city","school","special"]}},"style":"form","explode":false,"example":"county,city"},{"name":"contest_kinds","in":"query","description":"Contest kinds: candidate (an office), measure, or retention (whether to keep a judge). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","enum":["candidate","measure","retention"]}},"style":"form","explode":false,"example":"measure"},{"name":"districts","in":"query","description":"OCD division ids of the contests’ districts. Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"ocd-division/country:us/state:ca/county:san_francisco"},{"name":"upcoming","in":"query","description":"true for only elections from yesterday on; false (the default) for every election.","schema":{"type":"boolean","default":false},"example":true},{"name":"has_result","in":"query","description":"true for only contests with a result; false for only contests without one yet. Leave it out for both.","schema":{"type":"boolean"},"example":true},{"name":"result_statuses","in":"query","description":"Only contests whose result has one of these statuses: unofficial (counts still changing) or certified. Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","enum":["unofficial","certified"]}},"style":"form","explode":false,"example":"certified"},{"name":"lang","in":"query","description":"es for Spanish ballot text (titles, choices, measure text, statements) where the state publishes it; English otherwise. Names are never translated.","schema":{"type":"string","enum":["en","es"],"default":"en"},"example":"es"},{"name":"limit","in":"query","description":"Results per page, 1–200.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"example":20},{"name":"cursor","in":"query","description":"Where the next page starts: the next_cursor from the previous page. Leave it out for the first page; it is opaque, so pass it back exactly as you got it.","schema":{"type":"string"},"example":"aTo0NDEw"}],"responses":{"200":{"description":"A page of contests, by id.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","description":"This page.","items":{"$ref":"#/components/schemas/Contest"}},"next_cursor":{"type":["string","null"],"description":"Pass as cursor= for the next page; null on the last page."}}},"example":{"data":[{"id":4410,"title":"Member, Board of Supervisors, District 8","short_title":"Supervisor, D8","kind":"candidate","vote_method":"ranked_choice","votes_allowed":1,"ranks_allowed":10,"measure_code":null,"description":null,"ballot_text":null,"full_text_url":null,"unopposed":false,"ballot_order":41,"party":null,"details":{"nonpartisan":true},"source_url":"https://www.sf.gov/departments/department-elections","district":{"id":77,"ocd_id":"ocd-division/country:us/state:ca/county:san_francisco/council_district:8","name":"San Francisco Supervisorial District 8","level":"county","type":"council","state":"CA"},"election":{"id":3,"slug":"ca-2026-11-03-general","name":"California General Election","short_name":"General Election","date":"2026-11-03","state":"CA","type":"general","primary_system":"top_two","info_url":"https://www.sos.ca.gov/elections/upcoming-elections","details":{"all_mail":true}},"result":{"status":"unofficial","source_kind":"official","party_reported":false,"total_votes":52418,"precincts_reporting":41,"precincts_total":41,"source":{"name":"San Francisco Department of Elections","url":"https://www.sf.gov/departments/department-elections/results","contest_name":"Member, Board of Supervisors, District 8","updated_at":"2026-11-12T17:00:00-08:00"},"official_url":null,"more_sources":[],"write_in_votes":212,"blank_votes":null,"over_votes":null,"under_votes":null,"other_choices":[{"label":"Write-in","votes":212,"write_in":true}],"rcv_rounds":[{"round":1,"tallies":{"Jane Doe":24133,"Sam Rivera":21907,"Lee Park":6166},"eliminated":["Lee Park"]},{"round":2,"tallies":{"Jane Doe":27310,"Sam Rivera":23998}}],"notes":"Ranked choice: Jane Doe leads after round 2.","fetched_at":"2026-11-13T06:30:12Z"},"choices":[{"id":20931,"label":"Jane Doe","measure_answer":null,"option":null,"ballot_designation":"Transit Planner","withdrawn":false,"incumbent":true,"vote_meaning":null,"candidate":{"id":8811,"full_name":"Jane Doe","website_url":"https://example.com/janedoe","photo_url":null},"party":null,"party_label":null,"result":{"votes":24133,"vote_pct":46.039,"winner":null}},{"id":20932,"label":"Sam Rivera","measure_answer":null,"option":null,"ballot_designation":"Small Business Owner","withdrawn":false,"incumbent":false,"vote_meaning":null,"candidate":{"id":8812,"full_name":"Sam Rivera","website_url":null,"photo_url":null},"party":null,"party_label":null,"result":{"votes":21907,"vote_pct":41.793,"winner":null}}]},{"id":4502,"title":"Proposition K: Affordable Housing Bond","short_title":"Prop K","kind":"measure","vote_method":"yes_no","votes_allowed":1,"ranks_allowed":null,"measure_code":"K","description":"Affordable housing bond.","ballot_text":"Shall the City and County of San Francisco issue $300,000,000 in general obligation bonds to build and repair affordable housing?","full_text_url":"https://www.sf.gov/departments/department-elections/prop-k-full-text","unopposed":false,"ballot_order":88,"party":null,"details":{"measure_type":"bond","vote_required":"2/3","amount":300000000},"source_url":"https://www.sf.gov/departments/department-elections","district":{"id":70,"ocd_id":"ocd-division/country:us/state:ca/county:san_francisco","name":"San Francisco","level":"county","type":"county","state":"CA"},"election":{"id":3,"slug":"ca-2026-11-03-general","name":"California General Election","short_name":"General Election","date":"2026-11-03","state":"CA","type":"general","primary_system":"top_two","info_url":"https://www.sos.ca.gov/elections/upcoming-elections","details":{"all_mail":true}},"result":{"status":"certified","source_kind":"official","party_reported":false,"total_votes":389104,"precincts_reporting":517,"precincts_total":517,"source":{"name":"San Francisco Department of Elections","url":"https://www.sf.gov/departments/department-elections/results","contest_name":"Proposition K","updated_at":"2026-12-01T12:00:00-08:00"},"official_url":null,"more_sources":[],"write_in_votes":null,"blank_votes":null,"over_votes":null,"under_votes":null,"other_choices":[],"rcv_rounds":null,"notes":null,"fetched_at":"2026-11-13T06:30:12Z"},"choices":[{"id":21050,"label":"Yes","measure_answer":"yes","option":null,"ballot_designation":null,"withdrawn":false,"incumbent":null,"vote_meaning":"A Yes vote means the City may issue the bonds.","candidate":null,"party":null,"party_label":null,"result":{"votes":264590,"vote_pct":68,"winner":true}},{"id":21051,"label":"No","measure_answer":"no","option":null,"ballot_designation":null,"withdrawn":false,"incumbent":null,"vote_meaning":"A No vote means the City may not issue the bonds.","candidate":null,"party":null,"party_label":null,"result":{"votes":124514,"vote_pct":32,"winner":false}}]}],"next_cursor":"aTo0NTAy"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl \"https://api.ballotdata.ai/v1/contests?elections=ca-2026-11-03-general&levels=county&limit=20\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst { data: contests } = await client.contests.list({ elections: 'ca-2026-11-03-general', levels: 'county', limit: 20 });"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\ncontests = client.contests.list(elections=\"ca-2026-11-03-general\", levels=\"county\", limit=20).data"}]}},"/v1/contests/{id}":{"get":{"operationId":"getContest","tags":["Contests"],"summary":"Get a contest with endorsements","description":"One contest with its choices, its result once a source has one (votes and winners per choice, the source’s URL, ranked-choice rounds and write-ins), and every endorser’s position: one pick for vote-for-one, up to N picks for vote-for-N, ranked picks for ranked choice, Yes or No for a measure. Narrow the endorsers with categories, endorsers or endorser_states.","parameters":[{"name":"id","in":"path","required":true,"description":"Contest id (from GET /v1/contests).","schema":{"type":"integer","minimum":1},"example":4410},{"name":"categories","in":"query","description":"Only endorsers in these categories. Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"labor,editorial"},{"name":"endorsers","in":"query","description":"Only these endorsers (slugs). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"sf-labor-council"},{"name":"endorser_states","in":"query","description":"Only endorsers based in these states (USPS codes), or national for endorsers with none. Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"CA,national"},{"name":"lang","in":"query","description":"es for Spanish ballot text (titles, choices, measure text, statements) where the state publishes it; English otherwise. Names are never translated.","schema":{"type":"string","enum":["en","es"],"default":"en"},"example":"es"}],"responses":{"200":{"description":"The contest.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ContestDetail"}}},"example":{"data":{"id":4410,"title":"Member, Board of Supervisors, District 8","short_title":"Supervisor, D8","kind":"candidate","vote_method":"ranked_choice","votes_allowed":1,"ranks_allowed":10,"measure_code":null,"description":null,"ballot_text":null,"full_text_url":null,"unopposed":false,"ballot_order":41,"party":null,"details":{"nonpartisan":true},"source_url":"https://www.sf.gov/departments/department-elections","district":{"id":77,"ocd_id":"ocd-division/country:us/state:ca/county:san_francisco/council_district:8","name":"San Francisco Supervisorial District 8","level":"county","type":"council","state":"CA"},"election":{"id":3,"slug":"ca-2026-11-03-general","name":"California General Election","short_name":"General Election","date":"2026-11-03","state":"CA","type":"general","primary_system":"top_two","info_url":"https://www.sos.ca.gov/elections/upcoming-elections","details":{"all_mail":true}},"result":{"status":"unofficial","source_kind":"official","party_reported":false,"total_votes":52418,"precincts_reporting":41,"precincts_total":41,"source":{"name":"San Francisco Department of Elections","url":"https://www.sf.gov/departments/department-elections/results","contest_name":"Member, Board of Supervisors, District 8","updated_at":"2026-11-12T17:00:00-08:00"},"official_url":null,"more_sources":[],"write_in_votes":212,"blank_votes":null,"over_votes":null,"under_votes":null,"other_choices":[{"label":"Write-in","votes":212,"write_in":true}],"rcv_rounds":[{"round":1,"tallies":{"Jane Doe":24133,"Sam Rivera":21907,"Lee Park":6166},"eliminated":["Lee Park"]},{"round":2,"tallies":{"Jane Doe":27310,"Sam Rivera":23998}}],"notes":"Ranked choice: Jane Doe leads after round 2.","fetched_at":"2026-11-13T06:30:12Z"},"choices":[{"id":20931,"label":"Jane Doe","measure_answer":null,"option":null,"ballot_designation":"Transit Planner","withdrawn":false,"incumbent":true,"vote_meaning":null,"candidate":{"id":8811,"full_name":"Jane Doe","website_url":"https://example.com/janedoe","photo_url":null},"party":null,"party_label":null,"result":{"votes":24133,"vote_pct":46.039,"winner":null}},{"id":20932,"label":"Sam Rivera","measure_answer":null,"option":null,"ballot_designation":"Small Business Owner","withdrawn":false,"incumbent":false,"vote_meaning":null,"candidate":{"id":8812,"full_name":"Sam Rivera","website_url":null,"photo_url":null},"party":null,"party_label":null,"result":{"votes":21907,"vote_pct":41.793,"winner":null}}],"endorsements":[{"endorser":{"id":512,"slug":"sf-labor-council","name":"San Francisco Labor Council","category":{"slug":"labor","name":"Labor"},"state":"CA"},"picks":[{"endorsement_id":90211,"choice_id":20931,"label":"Jane Doe","measure_answer":null,"rank":1,"statement":null,"source_url":"https://sflaborcouncil.org/endorsements","published_on":"2026-09-18","added_by":"data_load"},{"endorsement_id":90212,"choice_id":20932,"label":"Sam Rivera","measure_answer":null,"rank":2,"statement":null,"source_url":"https://sflaborcouncil.org/endorsements","published_on":"2026-09-18","added_by":"data_load"}]},{"endorser":{"id":88,"slug":"sf-chronicle","name":"San Francisco Chronicle","category":{"slug":"editorial","name":"Voter guides & editorial boards"},"state":"CA"},"picks":[{"endorsement_id":90377,"choice_id":20932,"label":"Sam Rivera","measure_answer":null,"rank":null,"statement":"Rivera has the clearer plan for the district’s corridors.","source_url":"https://www.sfchronicle.com/opinion/endorsements","published_on":"2026-10-02","added_by":"automatic"}]}]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl \"https://api.ballotdata.ai/v1/contests/4410?categories=labor,editorial\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst contest = await client.contests.get(4410, { categories: 'labor,editorial' });"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\ncontest = client.contests.get(4410, categories=\"labor,editorial\")"}]}},"/v1/endorser-categories":{"get":{"operationId":"listEndorserCategories","tags":["Endorsers"],"summary":"List endorser categories","description":"The kinds of endorser (labor, editorial, environment, business, party, club, community, officials), with how many endorsers each has. Use the slugs in categories= filters.","parameters":[{"name":"lang","in":"query","description":"es for Spanish ballot text (titles, choices, measure text, statements) where the state publishes it; English otherwise. Names are never translated.","schema":{"type":"string","enum":["en","es"],"default":"en"},"example":"es"}],"responses":{"200":{"description":"Every category, in display order.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Every item.","items":{"$ref":"#/components/schemas/EndorserCategory"}}}},"example":{"data":[{"slug":"editorial","name":"Voter guides & editorial boards","singular_name":"Voter guide","endorser_count":214},{"slug":"labor","name":"Labor","singular_name":"Labor union","endorser_count":391},{"slug":"environment","name":"Environment & transportation","singular_name":"Environmental group","endorser_count":167}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl \"https://api.ballotdata.ai/v1/endorser-categories\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst categories = await client.endorserCategories.list();"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\ncategories = client.endorser_categories.list()"}]}},"/v1/endorsers":{"get":{"operationId":"listEndorsers","tags":["Endorsers"],"summary":"List endorsers","description":"Endorsers by category, home state, or the elections they’ve endorsed in, or search by name. Every endorser we’ve found is here, however few endorsements it has made; min_endorsements leaves out the smallest.","parameters":[{"name":"categories","in":"query","description":"Category slugs. Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"labor"},{"name":"states","in":"query","description":"Home states (USPS codes), or national. Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"CA"},{"name":"elections","in":"query","description":"Only endorsers with endorsements in these elections (slugs). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"ca-2026-11-03-general"},{"name":"q","in":"query","description":"Name contains (case-insensitive, up to 100 characters).","schema":{"type":"string","maxLength":100},"example":"labor council"},{"name":"min_endorsements","in":"query","description":"Only endorsers with at least this many contests (endorsement_count). endorsedby.org lists endorsers with 5 or more by default and finds the rest by search; pass 5 to match.","schema":{"type":"integer","minimum":0,"maximum":100000},"example":5},{"name":"lang","in":"query","description":"es for Spanish ballot text (titles, choices, measure text, statements) where the state publishes it; English otherwise. Names are never translated.","schema":{"type":"string","enum":["en","es"],"default":"en"},"example":"es"},{"name":"limit","in":"query","description":"Results per page, 1–500.","schema":{"type":"integer","minimum":1,"maximum":500,"default":100},"example":20},{"name":"cursor","in":"query","description":"Where the next page starts: the next_cursor from the previous page. Leave it out for the first page; it is opaque, so pass it back exactly as you got it.","schema":{"type":"string"},"example":"aTo0NDEw"}],"responses":{"200":{"description":"A page of endorsers, by id.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","description":"This page.","items":{"$ref":"#/components/schemas/Endorser"}},"next_cursor":{"type":["string","null"],"description":"Pass as cursor= for the next page; null on the last page."}}},"example":{"data":[{"id":512,"slug":"sf-labor-council","name":"San Francisco Labor Council","category":{"slug":"labor","name":"Labor"},"description":"The central labor council for San Francisco, representing over 150 unions.","website_url":"https://sflaborcouncil.org","logo_url":null,"state":"CA","party":null,"endorsement_count":84}],"next_cursor":"aTo1MTI"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl \"https://api.ballotdata.ai/v1/endorsers?categories=labor&states=CA&q=labor+council\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst { data: endorsers } = await client.endorsers.list({ categories: 'labor', states: 'CA', q: 'labor council' });"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\nendorsers = client.endorsers.list(categories=\"labor\", states=\"CA\", q=\"labor council\").data"}]}},"/v1/endorsers/{slug}":{"get":{"operationId":"getEndorser","tags":["Endorsers"],"summary":"Get an endorser","description":"One endorser, with how many contests they’ve taken a position in, and the elections they’ve endorsed in with how many contests in each.","parameters":[{"name":"slug","in":"path","required":true,"description":"Endorser slug (from GET /v1/endorsers).","schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9._-]{0,199}$"},"example":"sf-labor-council"},{"name":"lang","in":"query","description":"es for Spanish ballot text (titles, choices, measure text, statements) where the state publishes it; English otherwise. Names are never translated.","schema":{"type":"string","enum":["en","es"],"default":"en"},"example":"es"}],"responses":{"200":{"description":"The endorser.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/EndorserDetail"}}},"example":{"data":{"id":512,"slug":"sf-labor-council","name":"San Francisco Labor Council","category":{"slug":"labor","name":"Labor"},"description":"The central labor council for San Francisco, representing over 150 unions.","website_url":"https://sflaborcouncil.org","logo_url":null,"state":"CA","party":null,"endorsement_count":84,"elections":[{"election":"ca-2026-11-03-general","name":"California General Election","date":"2026-11-03","contests":61},{"election":"ca-2026-06-02-primary","name":"California Primary Election","date":"2026-06-02","contests":23}]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl \"https://api.ballotdata.ai/v1/endorsers/sf-labor-council\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst endorser = await client.endorsers.get('sf-labor-council');"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\nendorser = client.endorsers.get(\"sf-labor-council\")"}]}},"/v1/endorsements":{"get":{"operationId":"listEndorsements","tags":["Endorsements"],"summary":"List endorsements","description":"Endorsements, one per endorser and choice, with the basics of each: the endorser, contest, election and choice it’s for. A ranked-choice endorsement of three candidates is three rows with ranks 1–3. This is the way to sync everything, or with changed_since just what’s new since your last sync. GET /v1/endorsements/{id} has the full record, and GET /v1/contests/{id} every position in a contest.","parameters":[{"name":"categories","in":"query","description":"Endorser category slugs (GET /v1/endorser-categories). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"labor"},{"name":"endorsers","in":"query","description":"Endorser slugs (GET /v1/endorsers). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"sf-labor-council"},{"name":"endorser_states","in":"query","description":"Endorsers’ home states (USPS codes), or national for endorsers with none. Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"CA,national"},{"name":"elections","in":"query","description":"Election slugs (GET /v1/elections). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"ca-2026-11-03-general"},{"name":"states","in":"query","description":"States the contests are voted in (USPS codes). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","pattern":"^[A-Z]{2}$","description":"USPS state code.","examples":["CA"]}},"style":"form","explode":false,"example":"CA"},{"name":"contest_kinds","in":"query","description":"Contest kinds: candidate (an office), measure, or retention (whether to keep a judge). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","enum":["candidate","measure","retention"]}},"style":"form","explode":false,"example":"measure"},{"name":"levels","in":"query","description":"Levels of the contest’s district, from federal down to school and special districts. Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","enum":["federal","state","county","city","school","special"]}},"style":"form","explode":false,"example":"county,city"},{"name":"contest","in":"query","description":"Only this contest.","schema":{"type":"integer","minimum":1},"example":4410},{"name":"changed_since","in":"query","description":"Only endorsements added or changed at or after this time (ISO 8601). Use the time you last synced to fetch just what’s new; deletions aren’t listed here, so check GET /v1/endorsements/{id}/history for ids you hold that stop appearing.","schema":{"type":"string","format":"date-time"},"example":"2026-11-01T00:00:00Z"},{"name":"upcoming","in":"query","description":"true for only elections from yesterday on; false (the default) for every election.","schema":{"type":"boolean","default":false},"example":true},{"name":"lang","in":"query","description":"es for Spanish ballot text (titles, choices, measure text, statements) where the state publishes it; English otherwise. Names are never translated.","schema":{"type":"string","enum":["en","es"],"default":"en"},"example":"es"},{"name":"limit","in":"query","description":"Results per page, 1–500.","schema":{"type":"integer","minimum":1,"maximum":500,"default":100},"example":20},{"name":"cursor","in":"query","description":"Where the next page starts: the next_cursor from the previous page. Leave it out for the first page; it is opaque, so pass it back exactly as you got it.","schema":{"type":"string"},"example":"aTo0NDEw"}],"responses":{"200":{"description":"A page of endorsements, by id.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","description":"This page.","items":{"$ref":"#/components/schemas/Endorsement"}},"next_cursor":{"type":["string","null"],"description":"Pass as cursor= for the next page; null on the last page."}}},"example":{"data":[{"id":90211,"rank":1,"published_on":"2026-09-18","added_by":"data_load","endorser":{"id":512,"slug":"sf-labor-council","name":"San Francisco Labor Council"},"contest":{"id":4410,"title":"Member, Board of Supervisors, District 8"},"election":{"id":3,"slug":"ca-2026-11-03-general","date":"2026-11-03"},"choice":{"id":20931,"label":"Jane Doe","measure_answer":null}}],"next_cursor":"aTo5MDIxMQ"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl \"https://api.ballotdata.ai/v1/endorsements?elections=ca-2026-11-03-general&categories=labor\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst { data: endorsements } = await client.endorsements.list({ elections: 'ca-2026-11-03-general', categories: 'labor' });\n\n// Or every endorsement, fetching page after page:\n// for await (const endorsement of client.endorsements.list({ ... })) { … }"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\nendorsements = client.endorsements.list(elections=\"ca-2026-11-03-general\", categories=\"labor\").data\n\n# Or every endorsement, fetching page after page:\n# for endorsement in client.endorsements.list(...): ..."}]}},"/v1/endorsements/{id}":{"get":{"operationId":"getEndorsement","tags":["Endorsements"],"summary":"Get an endorsement","description":"One endorsement in full: what the endorser said and where, the endorser, the contest with its district, election and voting rules, and the choice with its candidate and party.","parameters":[{"name":"id","in":"path","required":true,"description":"Endorsement id (from GET /v1/endorsements).","schema":{"type":"integer","minimum":1},"example":90211},{"name":"lang","in":"query","description":"es for Spanish ballot text (titles, choices, measure text, statements) where the state publishes it; English otherwise. Names are never translated.","schema":{"type":"string","enum":["en","es"],"default":"en"},"example":"es"}],"responses":{"200":{"description":"The endorsement.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/EndorsementDetail"}}},"example":{"data":{"id":90211,"rank":1,"statement":null,"source_url":"https://sflaborcouncil.org/endorsements","published_on":"2026-09-18","retrieved_at":"2026-09-19T04:12:08.331Z","added_by":"data_load","endorser":{"id":512,"slug":"sf-labor-council","name":"San Francisco Labor Council","category":{"slug":"labor","name":"Labor"},"state":"CA","description":"The central labor council for San Francisco, representing over 150 unions.","website_url":"https://sflaborcouncil.org","logo_url":null,"party":null},"contest":{"id":4410,"title":"Member, Board of Supervisors, District 8","short_title":"Supervisor, D8","kind":"candidate","vote_method":"ranked_choice","votes_allowed":1,"ranks_allowed":10,"measure_code":null,"description":null,"ballot_text":null,"full_text_url":null,"unopposed":false,"ballot_order":41,"party":null,"details":{"nonpartisan":true},"source_url":"https://www.sf.gov/departments/department-elections","district":{"id":77,"ocd_id":"ocd-division/country:us/state:ca/county:san_francisco/council_district:8","name":"San Francisco Supervisorial District 8","level":"county","type":"council","state":"CA"},"election":{"id":3,"slug":"ca-2026-11-03-general","name":"California General Election","short_name":"General Election","date":"2026-11-03","state":"CA","type":"general","primary_system":"top_two","info_url":"https://www.sos.ca.gov/elections/upcoming-elections","details":{"all_mail":true}},"result":{"status":"unofficial","source_kind":"official","party_reported":false,"total_votes":52418,"precincts_reporting":41,"precincts_total":41,"source":{"name":"San Francisco Department of Elections","url":"https://www.sf.gov/departments/department-elections/results","contest_name":"Member, Board of Supervisors, District 8","updated_at":"2026-11-12T17:00:00-08:00"},"official_url":null,"more_sources":[],"write_in_votes":212,"blank_votes":null,"over_votes":null,"under_votes":null,"other_choices":[{"label":"Write-in","votes":212,"write_in":true}],"rcv_rounds":[{"round":1,"tallies":{"Jane Doe":24133,"Sam Rivera":21907,"Lee Park":6166},"eliminated":["Lee Park"]},{"round":2,"tallies":{"Jane Doe":27310,"Sam Rivera":23998}}],"notes":"Ranked choice: Jane Doe leads after round 2.","fetched_at":"2026-11-13T06:30:12Z"}},"choice":{"id":20931,"label":"Jane Doe","measure_answer":null,"option":null,"ballot_designation":"Transit Planner","withdrawn":false,"incumbent":true,"vote_meaning":null,"candidate":{"id":8811,"full_name":"Jane Doe","website_url":"https://example.com/janedoe","photo_url":null},"party":null,"party_label":null,"result":{"votes":24133,"vote_pct":46.039,"winner":null}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl \"https://api.ballotdata.ai/v1/endorsements/90211\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst endorsement = await client.endorsements.get(90211);"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\nendorsement = client.endorsements.get(90211)"}]}},"/v1/endorsements/{id}/history":{"get":{"operationId":"getEndorsementHistory","tags":["Endorsements"],"summary":"Get an endorsement’s history","description":"Every change to one endorsement, oldest first: when it was added, each change to its choice, rank, statement, source or date with the values before and after, and its deletion. Works after an endorsement is deleted too, so you can tell why an id stopped appearing.","parameters":[{"name":"id","in":"path","required":true,"description":"Endorsement id (from GET /v1/endorsements, or one you stored earlier).","schema":{"type":"integer","minimum":1},"example":90211}],"responses":{"200":{"description":"Every change, oldest first.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Every item.","items":{"$ref":"#/components/schemas/HistoryEntry"}}}},"example":{"data":[{"at":"2026-09-19T04:12:08.331Z","change":"created","changed":[],"before":null,"after":{"choice_id":20931,"choice_label":"Jane Doe","rank":2,"statement":null,"source_url":"https://sflaborcouncil.org/endorsements","published_on":"2026-09-18"}},{"at":"2026-10-03T16:40:51.020Z","change":"updated","changed":["rank"],"before":{"choice_id":20931,"choice_label":"Jane Doe","rank":2,"statement":null,"source_url":"https://sflaborcouncil.org/endorsements","published_on":"2026-09-18"},"after":{"choice_id":20931,"choice_label":"Jane Doe","rank":1,"statement":null,"source_url":"https://sflaborcouncil.org/endorsements","published_on":"2026-09-18"}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl \"https://api.ballotdata.ai/v1/endorsements/90211/history\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst history = await client.endorsements.history(90211);"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\nhistory = client.endorsements.history(90211)"}]}},"/v1/changes":{"get":{"operationId":"listChanges","tags":["Endorsements"],"summary":"List changes since a time","description":"Every endorsement added, changed or deleted since a time, in the order they happened, deletions included: the way to keep a copy current without the stream. Each change has the values before and after. Start with since= (the time of your last sync); page on with the cursor. The cursor follows the order changes were made, so changes made while you page show up on later pages, never missed.","parameters":[{"name":"since","in":"query","description":"Changes at or after this time (ISO 8601). Required unless you pass a cursor.","schema":{"type":"string","format":"date-time"},"example":"2026-11-01T00:00:00Z"},{"name":"categories","in":"query","description":"Endorser category slugs (GET /v1/endorser-categories). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"labor"},{"name":"endorsers","in":"query","description":"Endorser slugs (GET /v1/endorsers). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"sf-labor-council"},{"name":"elections","in":"query","description":"Election slugs (GET /v1/elections). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"ca-2026-11-03-general"},{"name":"states","in":"query","description":"States the contests are voted in (USPS codes). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","pattern":"^[A-Z]{2}$","description":"USPS state code.","examples":["CA"]}},"style":"form","explode":false,"example":"CA"},{"name":"limit","in":"query","description":"Results per page, 1–1000.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":100},"example":20},{"name":"cursor","in":"query","description":"Where the next page starts: the next_cursor from the previous page. Leave it out for the first page; it is opaque, so pass it back exactly as you got it.","schema":{"type":"string"},"example":"aTo0NDEw"}],"responses":{"200":{"description":"A page of changes, oldest first.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","description":"This page.","items":{"$ref":"#/components/schemas/Change"}},"next_cursor":{"type":["string","null"],"description":"Pass as cursor= for the next page; null on the last page."}}},"example":{"data":[{"id":512330,"at":"2026-11-02T18:04:11.902Z","change":"updated","changed":["rank"],"endorsement_id":90211,"endorser":{"id":512,"slug":"sf-labor-council","name":"San Francisco Labor Council","category":{"slug":"labor","name":"Labor"}},"contest":{"id":4410,"title":"Member, Board of Supervisors, District 8"},"election":{"id":3,"slug":"ca-2026-11-03-general","date":"2026-11-03"},"before":{"choice_id":20931,"choice_label":"Jane Doe","rank":2,"statement":null,"source_url":"https://sflaborcouncil.org/endorsements","published_on":"2026-09-18"},"after":{"choice_id":20931,"choice_label":"Jane Doe","rank":1,"statement":null,"source_url":"https://sflaborcouncil.org/endorsements","published_on":"2026-09-18"}},{"id":512331,"at":"2026-11-02T18:04:11.902Z","change":"deleted","changed":[],"endorsement_id":90377,"endorser":{"id":88,"slug":"sf-chronicle","name":"San Francisco Chronicle","category":{"slug":"editorial","name":"Voter guides & editorial boards"}},"contest":{"id":4410,"title":"Member, Board of Supervisors, District 8"},"election":{"id":3,"slug":"ca-2026-11-03-general","date":"2026-11-03"},"before":{"choice_id":20932,"choice_label":"Sam Rivera","rank":null,"statement":null,"source_url":"https://www.sfchronicle.com/opinion/endorsements","published_on":"2026-10-02"},"after":null}],"next_cursor":"aTo1MTIzMzE"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl \"https://api.ballotdata.ai/v1/changes?since=2026-11-01T00%3A00%3A00Z&states=CA\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst { data: changes } = await client.changes.list({ since: '2026-11-01T00:00:00Z', states: 'CA' });"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\nchanges = client.changes.list(since=\"2026-11-01T00:00:00Z\", states=\"CA\").data"}]}},"/v1/candidates":{"get":{"operationId":"listCandidates","tags":["Candidates"],"summary":"List candidates","description":"Candidates, each with the contests they’re on and how many endorsers backed them in each. Search by name, or narrow by state, election or incumbency. GET /v1/candidates/{id} has the endorsements themselves.","parameters":[{"name":"q","in":"query","description":"Name contains (case-insensitive, up to 100 characters).","schema":{"type":"string","maxLength":100},"example":"doe"},{"name":"states","in":"query","description":"States the contests are voted in (USPS codes). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","pattern":"^[A-Z]{2}$","description":"USPS state code.","examples":["CA"]}},"style":"form","explode":false,"example":"CA"},{"name":"elections","in":"query","description":"Election slugs (GET /v1/elections). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"ca-2026-11-03-general"},{"name":"incumbent","in":"query","description":"true for only incumbents, false for only candidates known not to hold the seat. Leave it out for everyone.","schema":{"type":"boolean"},"example":true},{"name":"upcoming","in":"query","description":"true for only elections from yesterday on; false (the default) for every election.","schema":{"type":"boolean","default":false},"example":true},{"name":"lang","in":"query","description":"es for Spanish ballot text (titles, choices, measure text, statements) where the state publishes it; English otherwise. Names are never translated.","schema":{"type":"string","enum":["en","es"],"default":"en"},"example":"es"},{"name":"limit","in":"query","description":"Results per page, 1–500.","schema":{"type":"integer","minimum":1,"maximum":500,"default":100},"example":20},{"name":"cursor","in":"query","description":"Where the next page starts: the next_cursor from the previous page. Leave it out for the first page; it is opaque, so pass it back exactly as you got it.","schema":{"type":"string"},"example":"aTo0NDEw"}],"responses":{"200":{"description":"A page of candidates, by id.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","description":"This page.","items":{"$ref":"#/components/schemas/CandidateSummary"}},"next_cursor":{"type":["string","null"],"description":"Pass as cursor= for the next page; null on the last page."}}},"example":{"data":[{"id":8811,"full_name":"Jane Doe","website_url":"https://example.com/janedoe","photo_url":null,"incumbent":true,"contests":[{"contest":{"id":4410,"title":"Member, Board of Supervisors, District 8","kind":"candidate","vote_method":"ranked_choice","votes_allowed":1,"ranks_allowed":10},"district":{"id":77,"ocd_id":"ocd-division/country:us/state:ca/county:san_francisco/council_district:8","name":"San Francisco Supervisorial District 8","level":"county","state":"CA"},"election":{"id":3,"slug":"ca-2026-11-03-general","name":"California General Election","date":"2026-11-03"},"choice":{"id":20931,"label":"Jane Doe","ballot_designation":"Transit Planner","withdrawn":false,"party":null,"party_label":null,"result":{"votes":24133,"vote_pct":46.039,"winner":null}},"result":{"status":"unofficial","source_kind":"official","party_reported":false,"total_votes":52418,"source_url":"https://www.sf.gov/departments/department-elections/results"},"endorsement_count":14}]}],"next_cursor":"aTo4ODEx"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl \"https://api.ballotdata.ai/v1/candidates?q=doe&states=CA\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst { data: candidates } = await client.candidates.list({ q: 'doe', states: 'CA' });"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\ncandidates = client.candidates.list(q=\"doe\", states=\"CA\").data"}]}},"/v1/candidates/{id}":{"get":{"operationId":"getCandidate","tags":["Candidates"],"summary":"Get a candidate with endorsements","description":"One candidate, with every contest they’re on, how each turned out once it has a result, and every endorsement they received in each: who, the rank for ranked choice, what they said and where.","parameters":[{"name":"id","in":"path","required":true,"description":"Candidate id (from GET /v1/candidates, or a choice’s candidate.id).","schema":{"type":"integer","minimum":1},"example":8811},{"name":"lang","in":"query","description":"es for Spanish ballot text (titles, choices, measure text, statements) where the state publishes it; English otherwise. Names are never translated.","schema":{"type":"string","enum":["en","es"],"default":"en"},"example":"es"}],"responses":{"200":{"description":"The candidate.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/CandidateDetail"}}},"example":{"data":{"id":8811,"full_name":"Jane Doe","website_url":"https://example.com/janedoe","photo_url":null,"incumbent":true,"contests":[{"contest":{"id":4410,"title":"Member, Board of Supervisors, District 8","kind":"candidate","vote_method":"ranked_choice","votes_allowed":1,"ranks_allowed":10},"district":{"id":77,"ocd_id":"ocd-division/country:us/state:ca/county:san_francisco/council_district:8","name":"San Francisco Supervisorial District 8","level":"county","state":"CA"},"election":{"id":3,"slug":"ca-2026-11-03-general","name":"California General Election","date":"2026-11-03"},"choice":{"id":20931,"label":"Jane Doe","ballot_designation":"Transit Planner","withdrawn":false,"party":null,"party_label":null,"result":{"votes":24133,"vote_pct":46.039,"winner":null}},"result":{"status":"unofficial","source_kind":"official","party_reported":false,"total_votes":52418,"source_url":"https://www.sf.gov/departments/department-elections/results"},"endorsement_count":14,"endorsements":[{"endorsement_id":90211,"endorser":{"id":512,"slug":"sf-labor-council","name":"San Francisco Labor Council","category":{"slug":"labor","name":"Labor"},"state":"CA"},"rank":1,"statement":null,"source_url":"https://sflaborcouncil.org/endorsements","published_on":"2026-09-18","added_by":"data_load"}]}],"issue_positions":[{"id":502,"issue":{"slug":"abortion","name":"Abortion"},"stance":"supports","summary":"Says abortion should stay legal and supports writing protections into state law.","quote":"Decisions about pregnancy belong to patients and their doctors.","checked_on":"2026-11-12","sources":[{"url":"https://example.com/janedoe/issues","kind":"candidate_website","publisher":"Campaign website","title":"Issues","published_on":null},{"url":"https://example.com/news/supervisor-questionnaire","kind":"questionnaire","publisher":"Example News","title":"District 8 candidate questionnaire","published_on":"2026-09-30"}]}]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl \"https://api.ballotdata.ai/v1/candidates/8811\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst candidate = await client.candidates.get(8811);"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\ncandidate = client.candidates.get(8811)"}]}},"/v1/issues":{"get":{"operationId":"listIssues","tags":["Candidates"],"summary":"List issues","description":"The policy issues we record candidates’ positions on (abortion, guns, immigration, schools, the safety net and more), each phrased as a yes/no question that a position’s stance answers. Use the slugs in issues= filters.","parameters":[],"responses":{"200":{"description":"Every issue, in display order.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Every item.","items":{"$ref":"#/components/schemas/Issue"}}}},"example":{"data":[{"slug":"abortion","name":"Abortion","question":"Should abortion be legal in most or all cases?","description":"Access to abortion, including bans, gestational limits, exceptions, federal protections and medication abortion."},{"slug":"guns","name":"Gun laws","question":"Should gun laws be made stricter?","description":"Background checks, assault-weapon and magazine limits, red-flag laws, permit and carry rules."}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl \"https://api.ballotdata.ai/v1/issues\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst issues = await client.issues.list();"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\nissues = client.issues.list()"}]}},"/v1/issue-positions":{"get":{"operationId":"listIssuePositions","tags":["Candidates"],"summary":"List issue positions","description":"Where candidates stand on common issues, one row per candidate and issue, each with a neutral summary, the candidate’s own words where we have them, and the links it came from: their own site and statements, debates, questionnaires, reputable news, and official roll-call votes for members of Congress. Narrow by issue, stance, candidate, contest, election or state. GET /v1/candidates/{id} has one candidate’s positions with everything else about them.","parameters":[{"name":"issues","in":"query","description":"Issue slugs (GET /v1/issues). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"abortion,guns"},{"name":"stances","in":"query","description":"Stances: supports, opposes or mixed. Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","enum":["supports","opposes","mixed"]}},"style":"form","explode":false,"example":"supports"},{"name":"candidates","in":"query","description":"Candidate ids. Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"integer","minimum":1}},"style":"form","explode":false,"example":"8811"},{"name":"contests","in":"query","description":"Contest ids: positions of the candidates on these contests. Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"integer","minimum":1}},"style":"form","explode":false,"example":"4410"},{"name":"elections","in":"query","description":"Election slugs (GET /v1/elections). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"ca-2026-11-03-general"},{"name":"states","in":"query","description":"States the contests are voted in (USPS codes). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","pattern":"^[A-Z]{2}$","description":"USPS state code.","examples":["CA"]}},"style":"form","explode":false,"example":"CA"},{"name":"limit","in":"query","description":"Results per page, 1–1000.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":100},"example":20},{"name":"cursor","in":"query","description":"Where the next page starts: the next_cursor from the previous page. Leave it out for the first page; it is opaque, so pass it back exactly as you got it.","schema":{"type":"string"},"example":"aTo0NDEw"}],"responses":{"200":{"description":"A page of positions, by id.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","description":"This page.","items":{"$ref":"#/components/schemas/IssuePositionItem"}},"next_cursor":{"type":["string","null"],"description":"Pass as cursor= for the next page; null on the last page."}}},"example":{"data":[{"id":502,"issue":{"slug":"abortion","name":"Abortion"},"stance":"supports","summary":"Says abortion should stay legal and supports writing protections into state law.","quote":"Decisions about pregnancy belong to patients and their doctors.","checked_on":"2026-11-12","sources":[{"url":"https://example.com/janedoe/issues","kind":"candidate_website","publisher":"Campaign website","title":"Issues","published_on":null},{"url":"https://example.com/news/supervisor-questionnaire","kind":"questionnaire","publisher":"Example News","title":"District 8 candidate questionnaire","published_on":"2026-09-30"}],"candidate":{"id":8811,"full_name":"Jane Doe"},"contests":[{"id":4410,"title":"Member, Board of Supervisors, District 8","election":{"slug":"ca-2026-11-03-general","date":"2026-11-03"},"state":"CA"}]}],"next_cursor":"aTo1MDI"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl \"https://api.ballotdata.ai/v1/issue-positions?issues=abortion&states=CA\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst { data: positions } = await client.issuePositions.list({ issues: 'abortion', states: 'CA' });"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\npositions = client.issue_positions.list(issues=\"abortion\", states=\"CA\").data"}]}},"/v1/elections/{slug}/export":{"get":{"operationId":"exportElection","tags":["Elections"],"summary":"Download a whole election","description":"Everything you can read in one election in one response: the election, and each contest with its choices, its result once a source has one, and every endorser’s position, in ballot order. Past elections (details.historical, such as the November 5, 2024 general) export the same way. format=csv gives one row per endorsement instead, each repeating its election, contest, district, choice, the choice’s result and the endorser (contests no one endorsed in have no rows).","parameters":[{"name":"slug","in":"path","required":true,"description":"Election slug (from GET /v1/elections).","schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9._-]{0,199}$"},"example":"ca-2026-11-03-general"},{"name":"format","in":"query","description":"json (the default) or csv.","schema":{"type":"string","enum":["json","csv"],"default":"json"},"example":"csv"},{"name":"lang","in":"query","description":"es for Spanish ballot text (titles, choices, measure text, statements) where the state publishes it; English otherwise. Names are never translated.","schema":{"type":"string","enum":["en","es"],"default":"en"},"example":"es"}],"responses":{"200":{"description":"The election. With format=csv, a CSV file (text/csv) with the columns election_slug, election_date, contest_id, contest_title, contest_kind, vote_method, votes_allowed, ranks_allowed, measure_code, district_id, district_name, district_level, district_ocd_id, state, choice_id, choice_label, measure_answer, candidate_id, candidate_name, party, incumbent, withdrawn, endorsement_id, endorser_id, endorser_slug, endorser_name, endorser_category, endorser_state, rank, statement, source_url, published_on, added_by, choice_votes, choice_vote_pct, choice_winner, result_status, result_source_kind, result_total_votes, result_source_url.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ElectionExport"}}},"example":{"data":{"election":{"id":3,"slug":"ca-2026-11-03-general","name":"California General Election","short_name":"General Election","date":"2026-11-03","state":"CA","type":"general","primary_system":"top_two","info_url":"https://www.sos.ca.gov/elections/upcoming-elections","details":{"all_mail":true}},"contests":[{"id":4410,"title":"Member, Board of Supervisors, District 8","short_title":"Supervisor, D8","kind":"candidate","vote_method":"ranked_choice","votes_allowed":1,"ranks_allowed":10,"measure_code":null,"description":null,"ballot_text":null,"full_text_url":null,"unopposed":false,"ballot_order":41,"party":null,"details":{"nonpartisan":true},"source_url":"https://www.sf.gov/departments/department-elections","district":{"id":77,"ocd_id":"ocd-division/country:us/state:ca/county:san_francisco/council_district:8","name":"San Francisco Supervisorial District 8","level":"county","type":"council","state":"CA"},"result":{"status":"unofficial","source_kind":"official","party_reported":false,"total_votes":52418,"precincts_reporting":41,"precincts_total":41,"source":{"name":"San Francisco Department of Elections","url":"https://www.sf.gov/departments/department-elections/results","contest_name":"Member, Board of Supervisors, District 8","updated_at":"2026-11-12T17:00:00-08:00"},"official_url":null,"more_sources":[],"write_in_votes":212,"blank_votes":null,"over_votes":null,"under_votes":null,"other_choices":[{"label":"Write-in","votes":212,"write_in":true}],"rcv_rounds":[{"round":1,"tallies":{"Jane Doe":24133,"Sam Rivera":21907,"Lee Park":6166},"eliminated":["Lee Park"]},{"round":2,"tallies":{"Jane Doe":27310,"Sam Rivera":23998}}],"notes":"Ranked choice: Jane Doe leads after round 2.","fetched_at":"2026-11-13T06:30:12Z"},"choices":[{"id":20931,"label":"Jane Doe","measure_answer":null,"option":null,"ballot_designation":"Transit Planner","withdrawn":false,"incumbent":true,"vote_meaning":null,"candidate":{"id":8811,"full_name":"Jane Doe","website_url":"https://example.com/janedoe","photo_url":null},"party":null,"party_label":null,"result":{"votes":24133,"vote_pct":46.039,"winner":null}},{"id":20932,"label":"Sam Rivera","measure_answer":null,"option":null,"ballot_designation":"Small Business Owner","withdrawn":false,"incumbent":false,"vote_meaning":null,"candidate":{"id":8812,"full_name":"Sam Rivera","website_url":null,"photo_url":null},"party":null,"party_label":null,"result":{"votes":21907,"vote_pct":41.793,"winner":null}}],"endorsements":[{"endorser":{"id":512,"slug":"sf-labor-council","name":"San Francisco Labor Council","category":{"slug":"labor","name":"Labor"},"state":"CA"},"picks":[{"endorsement_id":90211,"choice_id":20931,"label":"Jane Doe","measure_answer":null,"rank":1,"statement":null,"source_url":"https://sflaborcouncil.org/endorsements","published_on":"2026-09-18","added_by":"data_load"},{"endorsement_id":90212,"choice_id":20932,"label":"Sam Rivera","measure_answer":null,"rank":2,"statement":null,"source_url":"https://sflaborcouncil.org/endorsements","published_on":"2026-09-18","added_by":"data_load"}]},{"endorser":{"id":88,"slug":"sf-chronicle","name":"San Francisco Chronicle","category":{"slug":"editorial","name":"Voter guides & editorial boards"},"state":"CA"},"picks":[{"endorsement_id":90377,"choice_id":20932,"label":"Sam Rivera","measure_answer":null,"rank":null,"statement":"Rivera has the clearer plan for the district’s corridors.","source_url":"https://www.sfchronicle.com/opinion/endorsements","published_on":"2026-10-02","added_by":"automatic"}]}]}]}}},"text/csv":{"schema":{"type":"string"},"example":"election_slug,election_date,contest_id,contest_title,contest_kind,vote_method,votes_allowed,ranks_allowed,measure_code,district_id,district_name,district_level,district_ocd_id,state,choice_id,choice_label,measure_answer,candidate_id,candidate_name,party,incumbent,withdrawn,endorsement_id,endorser_id,endorser_slug,endorser_name,endorser_category,endorser_state,rank,statement,source_url,published_on,added_by,choice_votes,choice_vote_pct,choice_winner,result_status,result_source_kind,result_total_votes,result_source_url\nca-2026-11-03-general,2026-11-03,4410,\"Member, Board of Supervisors, District 8\",candidate,ranked_choice,1,10,,77,San Francisco Supervisorial District 8,county,ocd-division/country:us/state:ca/county:san_francisco/council_district:8,CA,20931,Jane Doe,,8811,Jane Doe,,true,false,90211,512,sf-labor-council,San Francisco Labor Council,labor,CA,1,,https://sflaborcouncil.org/endorsements,2026-09-18,data_load,24133,46.039,,unofficial,official,52418,https://www.sf.gov/departments/department-elections/results\n"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl \"https://api.ballotdata.ai/v1/elections/ca-2026-11-03-general/export?format=csv\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst csv = await client.elections.export('ca-2026-11-03-general', { format: 'csv' });\n\n// Or the JSON, with every contest:\n// const { election, contests } = await client.elections.export('ca-2026-11-03-general');"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\ncsv = client.elections.export(\"ca-2026-11-03-general\", format=\"csv\")\n\n# Or the JSON, with every contest:\n# export = client.elections.export(\"ca-2026-11-03-general\")"}]}},"/v1/officeholders":{"get":{"operationId":"listOfficeholders","tags":["Officeholders"],"summary":"List officeholders","description":"People in office now, with party, term and contact details. Filter by state, office, level or seat (OCD id). Federal and state offices, and the D.C. Council.","parameters":[{"name":"states","in":"query","description":"USPS codes. Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","pattern":"^[A-Z]{2}$","description":"USPS state code.","examples":["CA"]}},"style":"form","explode":false,"example":"CA"},{"name":"offices","in":"query","description":"Offices held. Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","enum":["us_senator","us_representative","governor","lieutenant_governor","attorney_general","secretary_of_state","chief_election_officer","treasurer","auditor","state_senator","state_representative","state_legislator","city_council"]}},"style":"form","explode":false,"example":"us_senator,governor"},{"name":"levels","in":"query","description":"Levels of office: federal, state, or city (the D.C. Council). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","enum":["federal","state","city"]}},"style":"form","explode":false,"example":"federal"},{"name":"districts","in":"query","description":"OCD division ids of the seats. Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"ocd-division/country:us/state:ca/sldl:17"},{"name":"q","in":"query","description":"Name contains (case-insensitive, up to 100 characters).","schema":{"type":"string","maxLength":100},"example":"morgan"},{"name":"lang","in":"query","description":"es for Spanish ballot text (titles, choices, measure text, statements) where the state publishes it; English otherwise. Names are never translated.","schema":{"type":"string","enum":["en","es"],"default":"en"},"example":"es"},{"name":"limit","in":"query","description":"Results per page, 1–500.","schema":{"type":"integer","minimum":1,"maximum":500,"default":100},"example":20},{"name":"cursor","in":"query","description":"Where the next page starts: the next_cursor from the previous page. Leave it out for the first page; it is opaque, so pass it back exactly as you got it.","schema":{"type":"string"},"example":"aTo0NDEw"}],"responses":{"200":{"description":"A page of officeholders.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","description":"This page.","items":{"$ref":"#/components/schemas/Officeholder"}},"next_cursor":{"type":["string","null"],"description":"Pass as cursor= for the next page; null on the last page."}}},"example":{"data":[{"person_id":"4f6a2c1e-8b3d-4e5f-9a7b-1c2d3e4f5a6b","name":"Alex Morgan","given_name":"Alex","family_name":"Morgan","office":"state_representative","office_title":"Assemblymember","level":"state","state":"CA","district":"17","district_ocd_id":"ocd-division/country:us/state:ca/sldl:17","district_id":1203,"party":"Democratic","party_abbreviation":"DEM","term_start":"2024-12-02","term_end":"2026-11-30","email":"assemblymember.morgan@example.gov","phone":"916-555-0117","website_url":"https://example.gov/morgan","image_url":null,"offices":[{"classification":"capitol","address":"1021 O Street, Sacramento, CA 95814","voice":"916-555-0117"}],"social":{"twitter":"AsmMorgan"},"source_urls":["https://www.assembly.ca.gov/assemblymembers"],"updated_at":"2026-10-12T08:00:00Z"}],"next_cursor":"aToxMjA0"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"503":{"$ref":"#/components/responses/Unavailable"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl \"https://api.ballotdata.ai/v1/officeholders?states=CA&offices=state_representative\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst { data: officeholders } = await client.officeholders.list({ states: 'CA', offices: 'state_representative' });"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\nofficeholders = client.officeholders.list(states=\"CA\", offices=\"state_representative\").data"}]}},"/v1/officeholders/{id}":{"get":{"operationId":"getOfficeholder","tags":["Officeholders"],"summary":"Get an officeholder","description":"One officeholder by person_id, with every seat they hold.","parameters":[{"name":"id","in":"path","required":true,"description":"The officeholder’s person_id (from GET /v1/officeholders).","schema":{"type":"string","format":"uuid"},"example":"4f6a2c1e-8b3d-4e5f-9a7b-1c2d3e4f5a6b"},{"name":"lang","in":"query","description":"es for Spanish ballot text (titles, choices, measure text, statements) where the state publishes it; English otherwise. Names are never translated.","schema":{"type":"string","enum":["en","es"],"default":"en"},"example":"es"}],"responses":{"200":{"description":"The officeholder.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/OfficeholderDetail"}}},"example":{"data":{"person_id":"4f6a2c1e-8b3d-4e5f-9a7b-1c2d3e4f5a6b","name":"Alex Morgan","given_name":"Alex","family_name":"Morgan","office":"state_representative","office_title":"Assemblymember","level":"state","state":"CA","district":"17","district_ocd_id":"ocd-division/country:us/state:ca/sldl:17","district_id":1203,"party":"Democratic","party_abbreviation":"DEM","term_start":"2024-12-02","term_end":"2026-11-30","email":"assemblymember.morgan@example.gov","phone":"916-555-0117","website_url":"https://example.gov/morgan","image_url":null,"offices":[{"classification":"capitol","address":"1021 O Street, Sacramento, CA 95814","voice":"916-555-0117"}],"social":{"twitter":"AsmMorgan"},"source_urls":["https://www.assembly.ca.gov/assemblymembers"],"updated_at":"2026-10-12T08:00:00Z","seats":[{"person_id":"4f6a2c1e-8b3d-4e5f-9a7b-1c2d3e4f5a6b","name":"Alex Morgan","given_name":"Alex","family_name":"Morgan","office":"state_representative","office_title":"Assemblymember","level":"state","state":"CA","district":"17","district_ocd_id":"ocd-division/country:us/state:ca/sldl:17","district_id":1203,"party":"Democratic","party_abbreviation":"DEM","term_start":"2024-12-02","term_end":"2026-11-30","email":"assemblymember.morgan@example.gov","phone":"916-555-0117","website_url":"https://example.gov/morgan","image_url":null,"offices":[{"classification":"capitol","address":"1021 O Street, Sacramento, CA 95814","voice":"916-555-0117"}],"social":{"twitter":"AsmMorgan"},"source_urls":["https://www.assembly.ca.gov/assemblymembers"],"updated_at":"2026-10-12T08:00:00Z"}]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"503":{"$ref":"#/components/responses/Unavailable"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl \"https://api.ballotdata.ai/v1/officeholders/4f6a2c1e-8b3d-4e5f-9a7b-1c2d3e4f5a6b\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst officeholder = await client.officeholders.get('4f6a2c1e-8b3d-4e5f-9a7b-1c2d3e4f5a6b');"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\nofficeholder = client.officeholders.get(\"4f6a2c1e-8b3d-4e5f-9a7b-1c2d3e4f5a6b\")"}]}},"/v1/stream":{"get":{"operationId":"streamEvents","tags":["Stream"],"summary":"Stream endorsement changes (SSE)","description":"Every endorsement change matching a subscription or inline filter, as Server-Sent Events, within seconds of it happening. Events: ready (first, with the subscription name), endorsement (an Event), checkpoint (moves your position past changes your filter skipped), reconnect (we end connections after an hour), and error (cursor_expired). A comment line arrives every 20 seconds.\n\nEach event id is a cursor: reconnect with Last-Event-ID (EventSource does this itself) or ?cursor= to continue. Cursors work on /v1/events too. Without one you start at the newest change (from=latest); from=earliest or since= start further back. A cursor older than the stream keeps (7 days, at most 50,000 changes) ends the stream with an error event, cursor_expired, or gets 410 before it opens. There is no limit on how many streams you hold open.\n\nThe Endorsement Stream ends at the close of December 31, 2027 (Pacific).","parameters":[{"name":"subscription","in":"query","description":"A saved subscription’s id or name (GET /v1/subscriptions). Use it instead of the filter parameters below, not together with them.","schema":{"type":"string","maxLength":100},"example":"CA labor"},{"name":"categories","in":"query","description":"Endorser category slugs (GET /v1/endorser-categories). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"labor"},{"name":"endorsers","in":"query","description":"Endorser slugs (GET /v1/endorsers). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"sf-labor-council"},{"name":"endorser_states","in":"query","description":"Endorsers’ home states (USPS codes), or national for endorsers with none. Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"CA,national"},{"name":"states","in":"query","description":"States the contests are voted in (USPS codes). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","pattern":"^[A-Z]{2}$","description":"USPS state code.","examples":["CA"]}},"style":"form","explode":false,"example":"CA"},{"name":"elections","in":"query","description":"Election slugs (GET /v1/elections). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"ca-2026-11-03-general"},{"name":"contest_kinds","in":"query","description":"Contest kinds: candidate (an office), measure, or retention (whether to keep a judge). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","enum":["candidate","measure","retention"]}},"style":"form","explode":false,"example":"measure"},{"name":"levels","in":"query","description":"Levels of the contest’s district, from federal down to school and special districts. Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","enum":["federal","state","county","city","school","special"]}},"style":"form","explode":false,"example":"county,city"},{"name":"types","in":"query","description":"Kinds of change: created (a new endorsement), updated (one that changed) or deleted (one withdrawn or removed). All three by default. Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","enum":["created","updated","deleted"]}},"style":"form","explode":false,"example":"created,updated"},{"name":"cursor","in":"query","description":"Resume after this position: a next_cursor from /v1/events or an event id from /v1/stream (either works on both). Also read from the Last-Event-ID header. Takes precedence over since and from. Opaque; valid for 7 days.","schema":{"type":"string"},"example":"c1.eyJzIjp7IjAiOiIxODM0MSJ9fQ"},{"name":"from","in":"query","description":"Where to start without a cursor or since: latest (only changes from now on) or earliest (the oldest change the stream still holds, up to 7 days back).","schema":{"type":"string","enum":["latest","earliest"],"default":"latest"},"example":"earliest"},{"name":"since","in":"query","description":"Start at the first change at or after this time (ISO 8601). Must be within the last 7 days, or you get 410 cursor_expired. Use instead of cursor.","schema":{"type":"string","format":"date-time"},"example":"2026-10-27T00:00:00Z"}],"responses":{"200":{"description":"An open event stream.","content":{"text/event-stream":{"schema":{"type":"string"},"example":"retry: 5000\n\nid: c1.eyJzIjp7IjAiOiIxODM0MSJ9fQ\nevent: ready\ndata: {\"subscription\":\"CA labor\"}\n\nid: c1.eyJzIjp7IjAiOiIxODM0MiJ9fQ\nevent: endorsement\ndata: {\"id\":\"18342\",\"type\":\"endorsement.created\",\"occurred_at\":\"2026-10-14T21:07:33.512Z\",\"endorsement\":{\"id\":90211,\"rank\":1,\"statement\":null,\"source_url\":\"https://sflaborcouncil.org/endorsements\",\"published_on\":\"2026-09-18\",\"added_by\":\"data_load\"},\"changed\":[],\"previous\":null,\"endorser\":{\"id\":512,\"slug\":\"sf-labor-council\",\"name\":\"San Francisco Labor Council\",\"category\":{\"slug\":\"labor\",\"name\":\"Labor\"},\"state\":\"CA\"},\"contest\":{\"id\":4410,\"title\":\"Member, Board of Supervisors, District 8\",\"short_title\":\"Supervisor, D8\",\"kind\":\"candidate\",\"vote_method\":\"ranked_choice\",\"votes_allowed\":1,\"ranks_allowed\":10,\"measure_code\":null,\"district\":{\"id\":77,\"name\":\"San Francisco Supervisorial District 8\",\"level\":\"county\",\"ocd_id\":\"ocd-division/country:us/state:ca/county:san_francisco/council_district:8\",\"state\":\"CA\"}},\"election\":{\"id\":3,\"slug\":\"ca-2026-11-03-general\",\"name\":\"California General Election\",\"date\":\"2026-11-03\",\"state\":\"CA\",\"type\":\"general\"},\"choice\":{\"id\":20931,\"label\":\"Jane Doe\",\"measure_answer\":null,\"candidate\":{\"id\":8811,\"full_name\":\"Jane Doe\"},\"party\":null,\"withdrawn\":false,\"incumbent\":true}}\n\n: ping\n\nid: c1.eyJzIjp7IjAiOiIxODM1MCJ9fQ\nevent: checkpoint\ndata: {}\n"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"429":{"$ref":"#/components/responses/TooManyRequests"},"503":{"$ref":"#/components/responses/Unavailable"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -N \"https://api.ballotdata.ai/v1/stream?subscription=CA%20labor\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\n\n// Reconnects by itself and resumes from the last event. Save the cursor to\n// pick up where you stopped after a restart.\nconst stream = client.stream.connect(\n  { subscription: 'CA labor', cursor: await loadCursor() },\n  { onCursor: (cursor) => saveCursor(cursor) },\n);\nfor await (const message of stream) {\n  if (message.type === 'endorsement') console.log(message.event);\n}"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\n\n# Reconnects by itself and resumes from the last event. Save the cursor to\n# pick up where you stopped after a restart.\nwith client.stream.connect(subscription=\"CA labor\", cursor=load_cursor(), on_cursor=save_cursor) as stream:\n    for message in stream:\n        if message.type == \"endorsement\":\n            print(message.event)"}]}},"/v1/events":{"get":{"operationId":"pollEvents","tags":["Stream"],"summary":"Poll endorsement changes","description":"The same changes as GET /v1/stream, a page at a time, for servers that can’t hold a connection open (cron jobs, serverless functions, strict firewalls). Each response has the changes after your cursor that match your filter, oldest first, and a next_cursor (even when there are none): store it and send it as cursor next time. has_more means more are waiting, so call again right away. wait (up to 25 seconds) holds a request with nothing new open until a matching change arrives (long polling).\n\nWithout a cursor you start at the newest change (from=latest); from=earliest or since= start further back. Cursors work on /v1/stream too. A cursor older than the stream keeps (7 days, at most 50,000 changes) gets 410 cursor_expired: catch up with GET /v1/endorsements, then start again with from=latest.\n\nThe Endorsement Stream ends at the close of December 31, 2027 (Pacific).","parameters":[{"name":"subscription","in":"query","description":"A saved subscription’s id or name (GET /v1/subscriptions). Use it instead of the filter parameters below, not together with them.","schema":{"type":"string","maxLength":100},"example":"CA labor"},{"name":"categories","in":"query","description":"Endorser category slugs (GET /v1/endorser-categories). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"labor"},{"name":"endorsers","in":"query","description":"Endorser slugs (GET /v1/endorsers). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"sf-labor-council"},{"name":"endorser_states","in":"query","description":"Endorsers’ home states (USPS codes), or national for endorsers with none. Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"CA,national"},{"name":"states","in":"query","description":"States the contests are voted in (USPS codes). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","pattern":"^[A-Z]{2}$","description":"USPS state code.","examples":["CA"]}},"style":"form","explode":false,"example":"CA"},{"name":"elections","in":"query","description":"Election slugs (GET /v1/elections). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":false,"example":"ca-2026-11-03-general"},{"name":"contest_kinds","in":"query","description":"Contest kinds: candidate (an office), measure, or retention (whether to keep a judge). Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","enum":["candidate","measure","retention"]}},"style":"form","explode":false,"example":"measure"},{"name":"levels","in":"query","description":"Levels of the contest’s district, from federal down to school and special districts. Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","enum":["federal","state","county","city","school","special"]}},"style":"form","explode":false,"example":"county,city"},{"name":"types","in":"query","description":"Kinds of change: created (a new endorsement), updated (one that changed) or deleted (one withdrawn or removed). All three by default. Comma-separated (up to 200 values); any of them matches.","schema":{"type":"array","items":{"type":"string","enum":["created","updated","deleted"]}},"style":"form","explode":false,"example":"created,updated"},{"name":"cursor","in":"query","description":"Resume after this position: a next_cursor from /v1/events or an event id from /v1/stream (either works on both). Also read from the Last-Event-ID header. Takes precedence over since and from. Opaque; valid for 7 days.","schema":{"type":"string"},"example":"c1.eyJzIjp7IjAiOiIxODM0MSJ9fQ"},{"name":"from","in":"query","description":"Where to start without a cursor or since: latest (only changes from now on) or earliest (the oldest change the stream still holds, up to 7 days back).","schema":{"type":"string","enum":["latest","earliest"],"default":"latest"},"example":"earliest"},{"name":"since","in":"query","description":"Start at the first change at or after this time (ISO 8601). Must be within the last 7 days, or you get 410 cursor_expired. Use instead of cursor.","schema":{"type":"string","format":"date-time"},"example":"2026-10-27T00:00:00Z"},{"name":"limit","in":"query","description":"Events per page, 1–1000. has_more in the response says more are waiting.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":100},"example":500},{"name":"wait","in":"query","description":"Seconds to hold the request open when nothing new matches (long polling), 0–25. 0 answers at once.","schema":{"type":"integer","minimum":0,"maximum":25,"default":0},"example":20}],"responses":{"200":{"description":"Events and the next cursor.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data","next_cursor","has_more"],"properties":{"data":{"type":"array","description":"Changes, oldest first.","items":{"$ref":"#/components/schemas/Event"}},"next_cursor":{"type":"string","description":"Send as cursor= next time."},"has_more":{"type":"boolean","description":"More are waiting: call again now."}}},"example":{"data":[{"id":"18342","type":"endorsement.created","occurred_at":"2026-10-14T21:07:33.512Z","endorsement":{"id":90211,"rank":1,"statement":null,"source_url":"https://sflaborcouncil.org/endorsements","published_on":"2026-09-18","added_by":"data_load"},"changed":[],"previous":null,"endorser":{"id":512,"slug":"sf-labor-council","name":"San Francisco Labor Council","category":{"slug":"labor","name":"Labor"},"state":"CA"},"contest":{"id":4410,"title":"Member, Board of Supervisors, District 8","short_title":"Supervisor, D8","kind":"candidate","vote_method":"ranked_choice","votes_allowed":1,"ranks_allowed":10,"measure_code":null,"district":{"id":77,"name":"San Francisco Supervisorial District 8","level":"county","ocd_id":"ocd-division/country:us/state:ca/county:san_francisco/council_district:8","state":"CA"}},"election":{"id":3,"slug":"ca-2026-11-03-general","name":"California General Election","date":"2026-11-03","state":"CA","type":"general"},"choice":{"id":20931,"label":"Jane Doe","measure_answer":null,"candidate":{"id":8811,"full_name":"Jane Doe"},"party":null,"withdrawn":false,"incumbent":true}}],"next_cursor":"c1.eyJzIjp7IjAiOiIxODM0MiJ9fQ","has_more":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"429":{"$ref":"#/components/responses/TooManyRequests"},"503":{"$ref":"#/components/responses/Unavailable"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl \"https://api.ballotdata.ai/v1/events?subscription=CA+labor&cursor=c1.eyJzIjp7IjAiOiIxODM0MSJ9fQ&wait=20\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst page = await client.events.list({ subscription: 'CA labor', cursor: 'c1.eyJzIjp7IjAiOiIxODM0MSJ9fQ', wait: 20 });\n// page.data, then page.next_cursor next time\n\n// Or follow changes for good, polling as you go:\n// for await (const { event, cursor } of client.events.follow({ subscription: 'CA labor' })) { … }"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\npage = client.events.list(subscription=\"CA labor\", cursor=\"c1.eyJzIjp7IjAiOiIxODM0MSJ9fQ\", wait=20)\n# page[\"data\"], then page[\"next_cursor\"] next time\n\n# Or follow changes for good, polling as you go:\n# for message in client.events.follow(subscription=\"CA labor\"): ..."}]}},"/v1/subscriptions":{"get":{"operationId":"listSubscriptions","tags":["Subscriptions"],"summary":"List subscriptions","description":"Your account’s saved subscriptions, by name.","responses":{"200":{"description":"Every subscription.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","description":"Every item.","items":{"$ref":"#/components/schemas/Subscription"}}}},"example":{"data":[{"id":7,"name":"CA labor","filter":{"categories":["labor"],"states":["CA"]},"created_at":"2026-10-15T17:20:04.112Z","updated_at":"2026-10-15T17:20:04.112Z"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl \"https://api.ballotdata.ai/v1/subscriptions\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst subscriptions = await client.subscriptions.list();"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\nsubscriptions = client.subscriptions.list()"}]},"post":{"operationId":"createSubscription","tags":["Subscriptions"],"summary":"Create a subscription","description":"Save a named filter. Use it as subscription=<id or name> on /v1/stream and /v1/events. An account can have up to 50.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionInput"},"example":{"name":"CA labor","filter":{"categories":["labor"],"states":["CA"]}}}}},"responses":{"201":{"description":"Created.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Subscription"}}},"example":{"data":{"id":7,"name":"CA labor","filter":{"categories":["labor"],"states":["CA"]},"created_at":"2026-10-15T17:20:04.112Z","updated_at":"2026-10-15T17:20:04.112Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X POST \"https://api.ballotdata.ai/v1/subscriptions\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"CA labor\",\"filter\":{\"categories\":[\"labor\"],\"states\":[\"CA\"]}}'"},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst subscription = await client.subscriptions.create({\n  name: 'CA labor',\n  filter: { categories: ['labor'], states: ['CA'] },\n});"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\nsubscription = client.subscriptions.create(\n    name=\"CA labor\",\n    filter={\"categories\": [\"labor\"], \"states\": [\"CA\"]},\n)"}]}},"/v1/subscriptions/{id}":{"get":{"operationId":"getSubscription","tags":["Subscriptions"],"summary":"Get a subscription","description":"One saved subscription.","parameters":[{"name":"id","in":"path","required":true,"description":"Subscription id (from GET /v1/subscriptions).","schema":{"type":"integer"},"example":7}],"responses":{"200":{"description":"The subscription.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Subscription"}}},"example":{"data":{"id":7,"name":"CA labor","filter":{"categories":["labor"],"states":["CA"]},"created_at":"2026-10-15T17:20:04.112Z","updated_at":"2026-10-15T17:20:04.112Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl \"https://api.ballotdata.ai/v1/subscriptions/7\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst subscription = await client.subscriptions.get(7);"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\nsubscription = client.subscriptions.get(7)"}]},"put":{"operationId":"updateSubscription","tags":["Subscriptions"],"summary":"Replace a subscription","description":"Replace a subscription’s name and filter. Open streams using it keep their old filter until they reconnect.","parameters":[{"name":"id","in":"path","required":true,"description":"Subscription id (from GET /v1/subscriptions).","schema":{"type":"integer"},"example":7}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionInput"},"example":{"name":"CA labor","filter":{"categories":["labor"],"states":["CA","NV"]}}}}},"responses":{"200":{"description":"Saved.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"},"X-Daily-Limit":{"$ref":"#/components/headers/X-Daily-Limit"},"X-Daily-Remaining":{"$ref":"#/components/headers/X-Daily-Remaining"},"X-Request-Id":{"$ref":"#/components/headers/X-Request-Id"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Subscription"}}},"example":{"data":{"id":7,"name":"CA labor","filter":{"categories":["labor"],"states":["CA","NV"]},"created_at":"2026-10-15T17:20:04.112Z","updated_at":"2026-10-21T09:12:40.006Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X PUT \"https://api.ballotdata.ai/v1/subscriptions/7\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"CA labor\",\"filter\":{\"categories\":[\"labor\"],\"states\":[\"CA\",\"NV\"]}}'"},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nconst subscription = await client.subscriptions.update(7, {\n  name: 'CA labor',\n  filter: { categories: ['labor'], states: ['CA', 'NV'] },\n});"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\nsubscription = client.subscriptions.update(\n    7,\n    name=\"CA labor\",\n    filter={\"categories\": [\"labor\"], \"states\": [\"CA\", \"NV\"]},\n)"}]},"delete":{"operationId":"deleteSubscription","tags":["Subscriptions"],"summary":"Delete a subscription","description":"Delete a subscription.","parameters":[{"name":"id","in":"path","required":true,"description":"Subscription id (from GET /v1/subscriptions).","schema":{"type":"integer"},"example":7}],"responses":{"204":{"description":"Deleted. No body."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}},"x-codeSamples":[{"lang":"Shell","label":"curl","source":"curl -X DELETE \"https://api.ballotdata.ai/v1/subscriptions/7\" \\\n  -H \"Authorization: Bearer $BALLOTDATA_API_KEY\""},{"lang":"JavaScript","label":"JavaScript","source":"import { BallotData } from 'ballotdata';\n\nconst client = new BallotData(); // reads BALLOTDATA_API_KEY\nawait client.subscriptions.delete(7);"},{"lang":"Python","label":"Python","source":"from ballotdata import BallotData\n\nclient = BallotData()  # reads BALLOTDATA_API_KEY\nclient.subscriptions.delete(7)"}]}}}}