tidycops is an incident-focused R package for pulling public police
data across cities with one interface.
It supports two core modes:
- Comparable cross-city output (
std_*schema) - Full city-native output (all exposed source fields)
As of April 30, 2026, incident adapters are available for:
- Boston
- Chicago
- Cincinnati
- Cleveland
- Dallas
- Denver
- Detroit
- Fort Lauderdale
- Gainesville
- Grand Rapids
- Hartford
- Houston
- Indianapolis
- Kansas City
- Minneapolis
- Naperville
- New Orleans
- New York City
- Pittsburgh
- Providence
- Rochester
- San Antonio
- San Francisco
- Seattle
- Washington, DC
Run list_supported_incident_cities() for the latest in-package list.
You can install the development version of tidycops like so:
# install.packages("remotes")
remotes::install_github("Steal-This-Code/tidycops")library(dplyr)
# Which cities are wired?
list_supported_incident_cities()
# Which fields are most comparable across cities?
list_common_incident_fields(min_cities = 4)
# What fields (standardized and source) are available for a specific city?
list_incident_fields("chicago")
list_incident_fields("san_francisco")Use this when you want harmonized fields for city-to-city analysis.
chicago_compare <- get_incidents(
city = "chicago",
view = "comparable",
start_date = "2026-04-01",
end_date = "2026-04-07",
as_sf = TRUE,
limit = 500
)Use this when you want all city-native fields plus adapter metadata.
cincy_full <- get_incidents(
city = "cincy",
view = "city_full",
start_date = "2026-04-01",
end_date = "2026-04-07",
limit = 1000
)Use this when you want untouched source payload rows only.
cincy_raw <- get_incidents(
city = "cincy",
view = "city_raw",
start_date = "2026-04-01",
end_date = "2026-04-07",
limit = 1000
)
# Equivalent convenience helper:
cincy_raw_2 <- download_city_incidents_raw(
city = "cincy",
start_date = "2026-04-01",
end_date = "2026-04-07",
limit = 1000
)Use last_n_days when you want a trailing window without manual date
math.
recent_cincy <- get_incidents(
city = "cincinnati",
view = "comparable",
last_n_days = 14,
limit = 1000
)Some adapters are rolling windows, capped historical feeds, or scope-limited (for example calls-for-service or Part I only). Check source metadata before analysis.
list_incident_sources() |>
dplyr::filter(source_status %in% c("historical_capped", "rolling_window") | scope_status != "all_incidents")Before expanding to arrests/UOF, these are the highest-value additions for incident workflows:
dedupecontrols- Add optional dedupe modes (
none,incident_id,incident_number) for city feeds that publish multiple offense rows per incident.
- Add optional dedupe modes (
fields = "core"projection- Add a quick column subset option for the most cross-city stable fields.
- Source diagnostics helper
- Add a helper to report expected coverage, null rates, and key field completeness by city/date window.
These keep the package incident-first while making pull/analyze loops faster.