Endorser track record
How the choices one endorser backed did: contests, picks, wins, losses, win rate and average vote share, in total and by contest kind, election stage and election.
- Returned by
GET /v1/endorsers/{slug}/record- Shapes
- The full record is in GET /v1/endorsers/{slug}/record. The shorter forms in each set of counts leave some fields out; a field that isn’t in every form says where it is.
How it connects
- Worked out from the endorser’s endorsements and each endorsed choice’s
result(votes,vote_pct,winner), so it only counts contests you can read. - The same eleven counts repeat in
totalsand in each row ofby_kind,by_stageandelections.
Good to know
- Only top picks count: a ranked-choice endorsement’s first choice, every candidate of a vote-for-N slate, and the Yes or No on a measure.
pickscan therefore be more thancontests. - A pick is
undecideduntil its choice’sresult.winneristrueorfalse: no result yet, a party-reported result with no counts, or a runoff still to come. win_ratedescribes the endorser’s picks. It does not show that an endorsement changed an outcome: endorsers often back incumbents and favorites, which is whyincumbent_picksandnon_incumbent_picksare counted apart.- In a primary,
wonmeans the candidate advanced or was nominated.
Fields
| Field | Type | Meaning | Example |
|---|---|---|---|
endorser | Endorser | Who made an endorsement.Only in GET /v1/endorsers/{slug}/record. | — |
totals | Endorser track record | How a set of an endorser’s picks did.Only in GET /v1/endorsers/{slug}/record. | — |
by_kind | array of object | The same counts for each kind of contest.Only in GET /v1/endorsers/{slug}/record. | — |
by_kind[].kind | string | candidate (an office), measure (a proposition) or retention (keep a judge?).candidate, measure, retention meaningsOnly in GET /v1/endorsers/{slug}/record. | candidate |
by_kind[].contests | integer | Contests counted.Only in GET /v1/endorsers/{slug}/record. | 66 |
by_kind[].picks | integer | Top picks in those contests: a ranked-choice endorsement’s first choice, every candidate of a vote-for-N slate, the Yes or No on a measure.Only in GET /v1/endorsers/{slug}/record. | 79 |
by_kind[].won | integer | Picks whose choice won (the choice’s result.winner is true): the candidate was elected or advanced, or the measure answer carried.Only in GET /v1/endorsers/{slug}/record. | 47 |
by_kind[].lost | integer | Picks whose choice lost (result.winner is false).Only in GET /v1/endorsers/{slug}/record. | 20 |
by_kind[].undecided | integer | Picks with no winner yet: no result, a result that doesn’t name winners, or a runoff to come.Only in GET /v1/endorsers/{slug}/record. | 12 |
by_kind[].win_rate | numberor null | won / (won + lost), 0 to 1, to three places; null until a pick is decided.Only in GET /v1/endorsers/{slug}/record. | 0.701 |
by_kind[].avg_vote_pct | numberor null | The average vote share (0 to 100) of the picked choices that have one; null if none does.Only in GET /v1/endorsers/{slug}/record. | 52.307 |
by_kind[].incumbent_picks | integer | Picks of a candidate who holds the seat now.Only in GET /v1/endorsers/{slug}/record. | 31 |
by_kind[].incumbent_won | integer | Of those, how many won.Only in GET /v1/endorsers/{slug}/record. | 29 |
by_kind[].non_incumbent_picks | integer | Picks of a candidate known not to hold the seat (challengers and open seats). Picks where incumbency isn’t known, and measure answers, are in neither count.Only in GET /v1/endorsers/{slug}/record. | 58 |
by_kind[].non_incumbent_won | integer | Of those, how many won.Only in GET /v1/endorsers/{slug}/record. | 28 |
by_stage | array of object | The same counts for each election stage: primary, runoff, general.Only in GET /v1/endorsers/{slug}/record. | — |
by_stage[].stage | string | The election stage the endorsement is for: general, primary or runoff (from the election it is in).general, primary, runoff meaningsOnly in GET /v1/endorsers/{slug}/record. | primary |
by_stage[].contests | integer | Contests counted.Only in GET /v1/endorsers/{slug}/record. | 30 |
by_stage[].picks | integer | Top picks in those contests: a ranked-choice endorsement’s first choice, every candidate of a vote-for-N slate, the Yes or No on a measure.Only in GET /v1/endorsers/{slug}/record. | 34 |
by_stage[].won | integer | Picks whose choice won (the choice’s result.winner is true): the candidate was elected or advanced, or the measure answer carried.Only in GET /v1/endorsers/{slug}/record. | 21 |
by_stage[].lost | integer | Picks whose choice lost (result.winner is false).Only in GET /v1/endorsers/{slug}/record. | 12 |
by_stage[].undecided | integer | Picks with no winner yet: no result, a result that doesn’t name winners, or a runoff to come.Only in GET /v1/endorsers/{slug}/record. | 1 |
by_stage[].win_rate | numberor null | won / (won + lost), 0 to 1, to three places; null until a pick is decided.Only in GET /v1/endorsers/{slug}/record. | 0.636 |
by_stage[].avg_vote_pct | numberor null | The average vote share (0 to 100) of the picked choices that have one; null if none does.Only in GET /v1/endorsers/{slug}/record. | 47.551 |
by_stage[].incumbent_picks | integer | Picks of a candidate who holds the seat now.Only in GET /v1/endorsers/{slug}/record. | 11 |
by_stage[].incumbent_won | integer | Of those, how many won.Only in GET /v1/endorsers/{slug}/record. | 11 |
by_stage[].non_incumbent_picks | integer | Picks of a candidate known not to hold the seat (challengers and open seats). Picks where incumbency isn’t known, and measure answers, are in neither count.Only in GET /v1/endorsers/{slug}/record. | 20 |
by_stage[].non_incumbent_won | integer | Of those, how many won.Only in GET /v1/endorsers/{slug}/record. | 9 |
elections | array of object | The same counts for each election, newest first.Only in GET /v1/endorsers/{slug}/record. | — |
elections[].election | object | The election.Only in GET /v1/endorsers/{slug}/record. | — |
elections[].election.slug | string | Election slug.Only in GET /v1/endorsers/{slug}/record. | ca-2026-11-03-general |
elections[].election.name | string | Election name.Only in GET /v1/endorsers/{slug}/record. | California General Election |
elections[].election.date | stringYYYY-MM-DD | Election day.Only in GET /v1/endorsers/{slug}/record. | 2026-11-03 |
elections[].election.type | string | Kind of election.general, primary, special, special_primary, runoff, consolidated meaningsOnly in GET /v1/endorsers/{slug}/record. | general |
elections[].contests | integer | Contests counted.Only in GET /v1/endorsers/{slug}/record. | 54 |
elections[].picks | integer | Top picks in those contests: a ranked-choice endorsement’s first choice, every candidate of a vote-for-N slate, the Yes or No on a measure.Only in GET /v1/endorsers/{slug}/record. | 63 |
elections[].won | integer | Picks whose choice won (the choice’s result.winner is true): the candidate was elected or advanced, or the measure answer carried.Only in GET /v1/endorsers/{slug}/record. | 40 |
elections[].lost | integer | Picks whose choice lost (result.winner is false).Only in GET /v1/endorsers/{slug}/record. | 10 |
elections[].undecided | integer | Picks with no winner yet: no result, a result that doesn’t name winners, or a runoff to come.Only in GET /v1/endorsers/{slug}/record. | 13 |
elections[].win_rate | numberor null | won / (won + lost), 0 to 1, to three places; null until a pick is decided.Only in GET /v1/endorsers/{slug}/record. | 0.8 |
elections[].avg_vote_pct | numberor null | The average vote share (0 to 100) of the picked choices that have one; null if none does.Only in GET /v1/endorsers/{slug}/record. | 58.649 |
elections[].incumbent_picks | integer | Picks of a candidate who holds the seat now.Only in GET /v1/endorsers/{slug}/record. | 20 |
elections[].incumbent_won | integer | Of those, how many won.Only in GET /v1/endorsers/{slug}/record. | 18 |
elections[].non_incumbent_picks | integer | Picks of a candidate known not to hold the seat (challengers and open seats). Picks where incumbency isn’t known, and measure answers, are in neither count.Only in GET /v1/endorsers/{slug}/record. | 38 |
elections[].non_incumbent_won | integer | Of those, how many won.Only in GET /v1/endorsers/{slug}/record. | 19 |
contests | integer | Contests counted.Only in each set of counts. | 84 |
picks | integer | Top picks in those contests: a ranked-choice endorsement’s first choice, every candidate of a vote-for-N slate, the Yes or No on a measure.Only in each set of counts. | 97 |
won | integer | Picks whose choice won (the choice’s result.winner is true): the candidate was elected or advanced, or the measure answer carried.Only in each set of counts. | 61 |
lost | integer | Picks whose choice lost (result.winner is false).Only in each set of counts. | 22 |
undecided | integer | Picks with no winner yet: no result, a result that doesn’t name winners, or a runoff to come.Only in each set of counts. | 14 |
win_rate | numberor null | won / (won + lost), 0 to 1, to three places; null until a pick is decided.Only in each set of counts. | 0.735 |
avg_vote_pct | numberor null | The average vote share (0 to 100) of the picked choices that have one; null if none does.Only in each set of counts. | 54.812 |
incumbent_picks | integer | Picks of a candidate who holds the seat now.Only in each set of counts. | 31 |
incumbent_won | integer | Of those, how many won.Only in each set of counts. | 29 |
non_incumbent_picks | integer | Picks of a candidate known not to hold the seat (challengers and open seats). Picks where incumbency isn’t known, and measure answers, are in neither count.Only in each set of counts. | 58 |
non_incumbent_won | integer | Of those, how many won.Only in each set of counts. | 28 |
Download every dictionary as CSV or JSON.
Sample (JSON)
One endorser track record as the API returns it, in GET /v1/endorsers/{slug}/record. More in the sample data.
{
"endorser": {
"id": 512,
"slug": "sf-labor-council",
"name": "San Francisco Labor Council",
"category": {
"slug": "labor",
"name": "Labor"
},
"state": "CA"
},
"totals": {
"contests": 84,
"picks": 97,
"won": 61,
"lost": 22,
"undecided": 14,
"win_rate": 0.735,
"avg_vote_pct": 54.812,
"incumbent_picks": 31,
"incumbent_won": 29,
"non_incumbent_picks": 58,
"non_incumbent_won": 28
},
"by_kind": [
{
"kind": "candidate",
"contests": 66,
"picks": 79,
"won": 47,
"lost": 20,
"undecided": 12,
"win_rate": 0.701,
"avg_vote_pct": 52.307,
"incumbent_picks": 31,
"incumbent_won": 29,
"non_incumbent_picks": 58,
"non_incumbent_won": 28
},
{
"kind": "measure",
"contests": 18,
"picks": 18,
"won": 14,
"lost": 2,
"undecided": 2,
"win_rate": 0.875,
"avg_vote_pct": 63.918,
"incumbent_picks": 0,
"incumbent_won": 0,
"non_incumbent_picks": 0,
"non_incumbent_won": 0
}
],
"by_stage": [
{
"stage": "primary",
"contests": 30,
"picks": 34,
"won": 21,
"lost": 12,
"undecided": 1,
"win_rate": 0.636,
"avg_vote_pct": 47.551,
"incumbent_picks": 11,
"incumbent_won": 11,
"non_incumbent_picks": 20,
"non_incumbent_won": 9
},
{
"stage": "general",
"contests": 54,
"picks": 63,
"won": 40,
"lost": 10,
"undecided": 13,
"win_rate": 0.8,
"avg_vote_pct": 58.649,
"incumbent_picks": 20,
"incumbent_won": 18,
"non_incumbent_picks": 38,
"non_incumbent_won": 19
}
],
"elections": [
{
"election": {
"slug": "ca-2026-11-03-general",
"name": "California General Election",
"date": "2026-11-03",
"type": "general"
},
"contests": 54,
"picks": 63,
"won": 40,
"lost": 10,
"undecided": 13,
"win_rate": 0.8,
"avg_vote_pct": 58.649,
"incumbent_picks": 20,
"incumbent_won": 18,
"non_incumbent_picks": 38,
"non_incumbent_won": 19
},
{
"election": {
"slug": "ca-2026-06-02-primary",
"name": "California Primary Election",
"date": "2026-06-02",
"type": "primary"
},
"contests": 30,
"picks": 34,
"won": 21,
"lost": 12,
"undecided": 1,
"win_rate": 0.636,
"avg_vote_pct": 47.551,
"incumbent_picks": 11,
"incumbent_won": 11,
"non_incumbent_picks": 20,
"non_incumbent_won": 9
}
]
}CSV: GET /v1/endorsers/{slug}/record?format=csv
One row per election the endorser took positions in, newest first. The JSON also has totals and breakdowns by contest kind and stage. An empty cell is a null or a value that doesn’t apply. The next page’s cursor comes in the X-Next-Cursor header.
| Column | Type | Meaning | Example |
|---|---|---|---|
endorser_slug | string | Endorser slug.From endorser.slug in the JSON. | sf-labor-council |
election_slug | string | Election slug.From elections[].election.slug in the JSON. | ca-2026-11-03-general |
election_name | string | Election name.From elections[].election.name in the JSON. | California General Election |
election_date | stringYYYY-MM-DD | Election day.From elections[].election.date in the JSON. | 2026-11-03 |
election_type | string | Kind of election.general, primary, special, special_primary, runoff, consolidated meaningsFrom elections[].election.type in the JSON. | general |
contests | integer | Contests counted.From elections[].contests in the JSON. | 54 |
picks | integer | Top picks in those contests: a ranked-choice endorsement’s first choice, every candidate of a vote-for-N slate, the Yes or No on a measure.From elections[].picks in the JSON. | 63 |
won | integer | Picks whose choice won (the choice’s result.winner is true): the candidate was elected or advanced, or the measure answer carried.From elections[].won in the JSON. | 40 |
lost | integer | Picks whose choice lost (result.winner is false).From elections[].lost in the JSON. | 10 |
undecided | integer | Picks with no winner yet: no result, a result that doesn’t name winners, or a runoff to come.From elections[].undecided in the JSON. | 13 |
win_rate | numbercan be empty | won / (won + lost), 0 to 1, to three places; null until a pick is decided.From elections[].win_rate in the JSON. | 0.8 |
avg_vote_pct | numbercan be empty | The average vote share (0 to 100) of the picked choices that have one; null if none does.From elections[].avg_vote_pct in the JSON. | 58.649 |
incumbent_picks | integer | Picks of a candidate who holds the seat now.From elections[].incumbent_picks in the JSON. | 20 |
incumbent_won | integer | Of those, how many won.From elections[].incumbent_won in the JSON. | 18 |
non_incumbent_picks | integer | Picks of a candidate known not to hold the seat (challengers and open seats). Picks where incumbency isn’t known, and measure answers, are in neither count.From elections[].non_incumbent_picks in the JSON. | 38 |
non_incumbent_won | integer | Of those, how many won.From elections[].non_incumbent_won in the JSON. | 19 |
Sample (CSV)
The same records as the JSON sample, as format=csv returns them.
endorser_slug,election_slug,election_name,election_date,election_type,contests,picks,won,lost,undecided,win_rate,avg_vote_pct,incumbent_picks,incumbent_won,non_incumbent_picks,non_incumbent_won sf-labor-council,ca-2026-11-03-general,California General Election,2026-11-03,general,54,63,40,10,13,0.8,58.649,20,18,38,19 sf-labor-council,ca-2026-06-02-primary,California Primary Election,2026-06-02,primary,30,34,21,12,1,0.636,47.551,11,11,20,9