What it covers
The Utah scraper returns election results from
electionresults.utah.gov. It covers all elections available
on the site — generals, primaries, and special elections — currently
from 2023 to 2025.
Results are available at three levels: statewide candidate totals,
county breakdowns, and precinct-level results. Precinct data is obtained
by navigating each county page and following “View results by precinct”
links for each contest.
Note: The Utah elections site uses the same
Angular/PrimeNG framework as the Georgia SOS site. All scraping uses a
headless Chromium browser via Playwright. County and precinct scraping
is parallelised across up to max_workers browsers (default
4). Utah has 29 counties.
Arguments
state |
— |
"UT", "utah", or "Utah"
|
year_from |
NULL |
Start year, inclusive; NULL = no lower bound |
year_to |
NULL |
End year, inclusive; NULL = no upper bound |
level |
"all" |
Constituency level of returned results. "all" —
statewide + county + precinct; "state" — state-level
constituency (statewide totals) only (fastest); "county" —
county-level constituency only; "precinct" — precinct-level
constituency only |
max_workers |
4L |
Parallel Chromium browsers for county and precinct scraping |
Examples
Statewide + county results for a single year
res <- scrape_elections(state = "UT", year_from = 2024, year_to = 2024)
# Statewide candidate totals
res$state %>%
select(election_name, office, candidate, party, votes, vote_pct)
# County-level breakdown
res$county %>%
select(county, office, candidate, party, votes, vote_pct) %>%
arrange(county, office, desc(votes))
Statewide totals only (faster — skips county scraping)
state_df <- scrape_elections(
state = "UT",
year_from = 2024,
year_to = 2024,
level = "state"
)
state_df %>%
select(election_name, office, candidate, party, votes, vote_pct)
All available years
res <- scrape_elections(
state = "UT",
year_from = 2023,
year_to = 2025,
level = "state"
)
Precinct-level results
# level = "all" includes precinct data automatically
res <- scrape_elections(state = "UT", year_from = 2024, year_to = 2024)
res$precinct %>%
filter(office == "President of the United States", county == "Salt Lake") %>%
select(precinct, candidate, party, votes, vote_pct) %>%
arrange(precinct, desc(votes))
# Precinct only (skips statewide + county scraping)
precinct_df <- scrape_elections(
state = "UT",
year_from = 2024,
year_to = 2024,
level = "precinct"
)
Filter to a specific county
Output columns
$state (when level = "all" or
"state")
state |
State abbreviation ("UT") |
election_name |
Human-readable election name |
election_type |
Election type (e.g. "General",
"Primary") |
election_year |
Calendar year of the election |
election_date |
Date string from the page header |
office_level |
"Federal", "State", or
"Local"
|
office |
Office name |
district |
District identifier; NA when not applicable |
candidate |
Candidate name |
party |
Party abbreviation |
votes |
Vote total |
vote_pct |
Percentage of votes within the contest |
winner |
TRUE if the candidate won the statewide contest |
url |
URL of the election results page scraped |
$county (when level = "all" or
"county")
state |
State abbreviation ("UT") |
election_name |
Human-readable election name |
election_type |
Election type (e.g. "General",
"Primary") |
election_year |
Calendar year of the election |
election_date |
Date string from the page header |
office_level |
"Federal", "State", or
"Local"
|
office |
Office name |
district |
District identifier; NA when not applicable |
county |
County name |
candidate |
Candidate name |
party |
Party abbreviation |
votes |
County-level vote total |
vote_pct |
Percentage of votes within the contest for this county |
county_winner |
TRUE if the candidate won within this county |
url |
URL of the election results page scraped |
$precinct (when level = "all" or
"precinct")
state |
State abbreviation ("UT") |
election_name |
Human-readable election name |
election_type |
Election type (e.g. "General",
"Primary") |
election_year |
Calendar year of the election |
election_date |
Date string from the page header |
office_level |
"Federal", "State", or
"Local"
|
office |
Office name |
district |
District identifier; NA when not applicable |
county |
County name |
precinct |
Precinct name |
candidate |
Candidate name |
party |
Party abbreviation |
votes |
Precinct-level vote total |
vote_pct |
Percentage of votes within the contest for this precinct |
precinct_winner |
TRUE if the candidate won within this precinct |
url |
URL of the precinct results page scraped |
-
level = "state" is much faster. County
scraping opens up to max_workers parallel Chromium browsers
across Utah’s 29 counties.
-
Precinct scraping is significantly slower. It
navigates each county page, fetches one ballot-item page per contest (in
parallel), and then scrapes each individual precinct page. Use
level = "county" or level = "state" when
precinct data is not needed.
-
Virtual scroll. The site uses Angular virtual
scrolling; the scraper automatically scrolls to load all panels before
parsing.
-
Limited history. The Utah elections site currently
lists elections from 2023 onward. Earlier data is not available through
this scraper.