tidyusafec is an R package for accessing campaign finance data through the Federal Election Commission’s OpenFEC API. It depends on having R installed (windows | mac), and suggests you also install the integrated development environment RStudio if you are just beginning with R. tidyusafec is an independent open source project under MIT license maintained by Stephen Holzman. It is not affiliated with or endorsed by the FEC or any of the other software projects mentioned.
Do your due diligence before publishing data gathered with tidyusafec. While this package eases the technical burden of working with FEC data, it does not do anything to expedite understanding the different products.
This package is still young. As the package author, I’m still double or triple checking numbers with other sources to make sure the numbers I think I have and the numbers I actually have are the same. Even then FEC data has some quirks.
In this early stage, please report unexpected package behavior in an issue on GitHub.
Depending on where R packages are hosted, you can install them from inside R with functions. Use
install.packages to get the
devtools package from CRAN. Use
install_github to get
tidyusafec from the Github repository.
tidyusafec makes it easy to send requests to the FEC website’s API for data. Some of the functions can realistically trigger thousands of requests spread out over several minutes. Check your email for an API key and substitute it for YOUR_API_KEY inside these quotes:
save_datagov_apikey(key = "YOUR_API_KEY")
Running this saves the API Key on your machine so tidyusafec functions can use it automatically.
By default, the FEC limits individuals to 1000 requests every hour. This is probably enough for general use like:
200 candidate-by-election searches leading to 800 requests for an individual candidate’s financial totals per hour
100,000 itemized contributions per hour.
If you see yourself going over that limit, you can send APIinfo@fec.gov a request for their 120 calls per minute upgrade.
search_candidates is the most common way to begin an analysis. An example search might look like:
wv_senate <- search_candidates(state = "WV", election_year = "2018", office = "S", unnest_committees = TRUE)
Each row returned by
unnest_committees = TRUE contains a unique
committee_id pair. Candidates just getting started or not raising money may have no committee_id, some candidates may have multiple rows if they changed principal committees for different elections, and most will have one pair. Use
unnest_committees = FALSE if you would prefer to keep committee information in a non-tidy list column.
candidate_status is a notable column to filter on and be aware of. It goes by FEC status according to records filed and processed, not necessarily what candidates have announced about their intentions.
Some filter strategies are below, or you could use
search_candidates better when you know exactly what you are looking for.
#Just select candidates. wv_senate %>% filter(str_detect(name, "MANCHIN|MORRISEY")) #Candidates who are still current as far as the FEC knows. wv_senate %>% filter(candidate_status == "C") #An alternative search if we know we only want current candidates from the start. wv_senate <- search_candidates(state = "WV", election_year = "2018", office = "S", candidate_status = "C")
The results of
search_candidates can be piped (using
%>%) into one of the appropriate functions that begin with a
Start by getting financial totals for each candidate.
wv_senate_totals <- wv_senate %>% get_candidate_totals()
The tibbles returned by
get_ functions are tidy (except for some list columns that remain while developing the package). This makes it possible to cleanly pipe results into existing tidyverse tools with minimal data wrangling.
wv_senate_totals %>% filter(type_of_funds == "receipts", cycle == 2018) %>% ggplot() + geom_bar(aes(x = name, y = amount), stat = "identity") + coord_flip()
A list is available called
tidyusafec_filters that contain useful vectors to filter on. For example:
wv_senate_totals %>% filter(str_detect(name, "MANCHIN|MORRISEY"), type_of_funds %in% tidyusafec_filters$candidate_totals$type_of_funds$receipts_smallest_components) %>% mutate(type_of_funds = type_of_funds %>% str_replace_all("_"," ") %>% str_to_title() ) %>% ggplot() + geom_bar(aes(x = name, y = amount), stat = "identity") + facet_wrap(~type_of_funds,labeller = label_wrap_gen(width=15)) + coord_flip()