Skip to contents

What it covers

The school district scraper collects school board election data from Ballotpedia’s annual school board election pages (2013 to present, all US states). Use office = "school_district" to activate it.


Arguments

Argument Default Description
office Must be "school_district"
state NULL State name (e.g. "Alabama"), or NULL for all states
year NULL Single election year. Required for mode = "results" or "joined".
mode "districts" What to return — see below
start_year 2013 Earliest year for a multi-year district scrape
end_year NULL Latest year (default: current calendar year)

The mode parameter

Value Returns Speed
"districts" (default) District-level metadata — one request per year page Fast
"results" Candidate names, votes, percentages per race Slower — one extra request per district
"joined" Districts + candidates in a single merged data frame Slower

Examples

District metadata for all states

districts_2024 <- scrape_elections(
  office = "school_district",
  year   = 2024
)

districts_2024 %>%
  filter(as.integer(seats_up) > 3) %>%
  select(state, district, seats_up, total_board_seats, enrollment) %>%
  arrange(desc(as.integer(seats_up)))

Filter to one state

alabama_2024 <- scrape_elections(
  state  = "Alabama",
  office = "school_district",
  year   = 2024
)

alabama_2024 %>%
  select(district, general_election, seats_up, total_board_seats, enrollment)

Competitive districts (many seats up)

scrape_elections(
  state  = "Texas",
  office = "school_district",
  year   = 2024
) %>%
  filter(as.integer(seats_up) > 1) %>%
  select(district, general_election, seats_up, total_board_seats, enrollment) %>%
  arrange(desc(as.integer(seats_up)))

Full candidate results for one year + state

mode = "results" follows each district link — slower but returns candidate names, vote counts, and winner flags:

results <- scrape_elections(
  state  = "New Jersey",
  office = "school_district",
  year   = 2024,
  mode   = "results"
)

# Winners only
results %>%
  filter(is_winner) %>%
  select(district, race, candidate, party, pct, votes) %>%
  arrange(district, race)

Districts + candidates joined

joined <- scrape_elections(
  state  = "Texas",
  office = "school_district",
  year   = 2024,
  mode   = "joined"
)

joined %>%
  filter(is_winner) %>%
  select(district, enrollment, candidate, party, pct, votes)

Multi-year district metadata

Use start_year / end_year when year is omitted:

multi_year <- scrape_elections(
  state      = "Ohio",
  office     = "school_district",
  start_year = 2020,
  end_year   = 2024
)

multi_year %>%
  group_by(year) %>%
  summarise(n_districts = n(), .groups = "drop")

Columns returned

mode = "districts"

  • year, state, district, district_url
  • primary, primary_runoff, general_election, general_runoff
  • term_length, seats_up, total_board_seats, enrollment

mode = "results" / "joined"

All district columns plus:

  • race, election_type (General / Primary / Primary Runoff / Other)
  • candidate, candidate_url, party
  • is_winner, is_incumbent
  • pct, votes