Data dictionaries

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 totals and in each row of by_kind, by_stage and elections.

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. picks can therefore be more than contests.
  • A pick is undecided until its choice’s result.winner is true or false: no result yet, a party-reported result with no counts, or a runoff still to come.
  • win_rate describes the endorser’s picks. It does not show that an endorsement changed an outcome: endorsers often back incumbents and favorites, which is why incumbent_picks and non_incumbent_picks are counted apart.
  • In a primary, won means the candidate advanced or was nominated.

Fields

FieldTypeMeaningExample
endorserEndorserWho made an endorsement.Only in GET /v1/endorsers/{slug}/record.—
totalsEndorser track recordHow a set of an endorser’s picks did.Only in GET /v1/endorsers/{slug}/record.—
by_kindarray of objectThe same counts for each kind of contest.Only in GET /v1/endorsers/{slug}/record.—
by_kind[].kindstringcandidate (an office), measure (a proposition) or retention (keep a judge?).candidate, measure, retention meaningsOnly in GET /v1/endorsers/{slug}/record.candidate
by_kind[].contestsintegerContests counted.Only in GET /v1/endorsers/{slug}/record.66
by_kind[].picksintegerTop 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[].wonintegerPicks 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[].lostintegerPicks whose choice lost (result.winner is false).Only in GET /v1/endorsers/{slug}/record.20
by_kind[].undecidedintegerPicks 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_ratenumberor nullwon / (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_pctnumberor nullThe 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_picksintegerPicks of a candidate who holds the seat now.Only in GET /v1/endorsers/{slug}/record.31
by_kind[].incumbent_wonintegerOf those, how many won.Only in GET /v1/endorsers/{slug}/record.29
by_kind[].non_incumbent_picksintegerPicks 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_wonintegerOf those, how many won.Only in GET /v1/endorsers/{slug}/record.28
by_stagearray of objectThe same counts for each election stage: primary, runoff, general.Only in GET /v1/endorsers/{slug}/record.—
by_stage[].stagestringThe 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[].contestsintegerContests counted.Only in GET /v1/endorsers/{slug}/record.30
by_stage[].picksintegerTop 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[].wonintegerPicks 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[].lostintegerPicks whose choice lost (result.winner is false).Only in GET /v1/endorsers/{slug}/record.12
by_stage[].undecidedintegerPicks 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_ratenumberor nullwon / (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_pctnumberor nullThe 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_picksintegerPicks of a candidate who holds the seat now.Only in GET /v1/endorsers/{slug}/record.11
by_stage[].incumbent_wonintegerOf those, how many won.Only in GET /v1/endorsers/{slug}/record.11
by_stage[].non_incumbent_picksintegerPicks 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_wonintegerOf those, how many won.Only in GET /v1/endorsers/{slug}/record.9
electionsarray of objectThe same counts for each election, newest first.Only in GET /v1/endorsers/{slug}/record.—
elections[].electionobjectThe election.Only in GET /v1/endorsers/{slug}/record.—
elections[].election.slugstringElection slug.Only in GET /v1/endorsers/{slug}/record.ca-2026-11-03-general
elections[].election.namestringElection name.Only in GET /v1/endorsers/{slug}/record.California General Election
elections[].election.datestringYYYY-MM-DDElection day.Only in GET /v1/endorsers/{slug}/record.2026-11-03
elections[].election.typestringKind of election.general, primary, special, special_primary, runoff, consolidated meaningsOnly in GET /v1/endorsers/{slug}/record.general
elections[].contestsintegerContests counted.Only in GET /v1/endorsers/{slug}/record.54
elections[].picksintegerTop 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[].wonintegerPicks 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[].lostintegerPicks whose choice lost (result.winner is false).Only in GET /v1/endorsers/{slug}/record.10
elections[].undecidedintegerPicks 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_ratenumberor nullwon / (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_pctnumberor nullThe 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_picksintegerPicks of a candidate who holds the seat now.Only in GET /v1/endorsers/{slug}/record.20
elections[].incumbent_wonintegerOf those, how many won.Only in GET /v1/endorsers/{slug}/record.18
elections[].non_incumbent_picksintegerPicks 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_wonintegerOf those, how many won.Only in GET /v1/endorsers/{slug}/record.19
contestsintegerContests counted.Only in each set of counts.84
picksintegerTop 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
wonintegerPicks 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
lostintegerPicks whose choice lost (result.winner is false).Only in each set of counts.22
undecidedintegerPicks 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_ratenumberor nullwon / (won + lost), 0 to 1, to three places; null until a pick is decided.Only in each set of counts.0.735
avg_vote_pctnumberor nullThe 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_picksintegerPicks of a candidate who holds the seat now.Only in each set of counts.31
incumbent_wonintegerOf those, how many won.Only in each set of counts.29
non_incumbent_picksintegerPicks 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_wonintegerOf 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.

ColumnTypeMeaningExample
endorser_slugstringEndorser slug.From endorser.slug in the JSON.sf-labor-council
election_slugstringElection slug.From elections[].election.slug in the JSON.ca-2026-11-03-general
election_namestringElection name.From elections[].election.name in the JSON.California General Election
election_datestringYYYY-MM-DDElection day.From elections[].election.date in the JSON.2026-11-03
election_typestringKind of election.general, primary, special, special_primary, runoff, consolidated meaningsFrom elections[].election.type in the JSON.general
contestsintegerContests counted.From elections[].contests in the JSON.54
picksintegerTop 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
wonintegerPicks 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
lostintegerPicks whose choice lost (result.winner is false).From elections[].lost in the JSON.10
undecidedintegerPicks 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_ratenumbercan be emptywon / (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_pctnumbercan be emptyThe 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_picksintegerPicks of a candidate who holds the seat now.From elections[].incumbent_picks in the JSON.20
incumbent_wonintegerOf those, how many won.From elections[].incumbent_won in the JSON.18
non_incumbent_picksintegerPicks 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_wonintegerOf 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
Endorser track record data dictionary · BallotData API