The below document provides some quantitative insights into Immigration and Customs Enforcement (ICE) activity within the Kansas City metro area between October 2022 and February 2026, particularly focusing on the changes seen during the current presidential administration.

Some key takeaways:

  • The overall rate of arrests has more than tripled during the current administration, with particularly high numbers during the summer of 2025 and February 2026.
  • Eighteen to twenty-one year-olds have experienced the largest increase in ICE arrests, with a 278% increase.
  • During the current administration, there have been particular peaks in ICE activity within communities on Tuesday and Wednesday from 8-10 AM.
    • Note that this does not be mean that this will continue to be the case in the future.
  • Arrests do not seem targeted towards those with the most severe criminal background.
    • People convicted of aggravated felonies have seen the smallest increase in arrests.
    • The groups with the largest increases are people convicted of 1-2 misdemeanors and those with a criminal charge but no convictions.

I would love to collaborate both with additional analysts and those who are working on immigration issues. For questions and feedback, feel free to reach out at dylan.knaggs@gmail.com.

All analysis is based on data provided in response to a FOIA request to the Deportation Data Project. Many thanks to them for making this data and for making their work available to the public.

Configuration

library(tidyverse)
library(knitr)
library(gt)
library(ggrepel)
library(here)
library(arrow)
source("fns.R")

Repeated plot elements

theme_set(theme_bw())
inaug_line <- geom_vline(xintercept = as.Date("2025-01-20"), 
                         color="darkblue", size=.75)

add_inaug_text <- function(text_date=as.Date("2024-08-01")){
  annotate("text",label="Trump's Second Inauguration",
           x=text_date,y=180,size=3)  # top left
#           x=as.Date("2025-06-28"),y=10,size=3) + # bottom right
  
}

color_vec <- c("Before Jan 2025"="#E69A8D","After Jan 2025"="#5F4B8B")
loc_color_vec <- c("Community"="salmon3","Prison/Jail"="palegreen4",
                   "Others"="grey75")

Load Data

For simplicity in plotting and calculations, removing March 2026, which only has data for a few days.

arrests_kc <- read_parquet(here("data/cleaned","arrests_kc.parquet")) |>
    filter(apprehension_month!=as.Date("2026-03-01")) 

Calculate repeated values

key_dates <- arrests_kc |> 
  summarise(min=min(apprehension_date),max=max(apprehension_date))
key_dates_admin <- arrests_kc |>
  filter(post_0125) |>
  summarise(min=min(apprehension_date),max=max(apprehension_date))
first_date <- key_dates$min
first_date_admin <- key_dates_admin$min
last_date <- key_dates_admin$max

Analysis

Overview

Total Arrests

Before and after Trump II Inauguration

The average number of arrests each month within the Kansas City area has more than tripled from Oct 2022-Jan 2025 to Feb 2025-Feb 2026.

arrests_kc |>
  mutate(n_arr=1) |>
  comp_table(n_arr) |>
  add_before_after_change() |>
  select(-n_arr) |>
  gt_defaults("Average Monthly Arrests",decimals=1)
Average Monthly Arrests
Before Jan 2025 After Jan 2025 Percent Change
43.5 132.7 205.1%

Number of arrests over time

arrests_kc |>
  ggplot(aes(x=apprehension_month)) +
  inaug_line + 
  add_inaug_text() + 
  geom_line(stat="count") +
  geom_point(stat="count") +
  geom_text_repel(aes(label = after_stat(count)), 
                  stat = "count", hjust = 0.5,size=2.75)+ #,
#                  linewidth=0,fill="grey80",alpha=.5) + # code for geom_label
  labs(title="Number of ICE Arrests in the Kansas City Area",
       x="Month",y="Number of Apprehensions") +
  scale_x_date(date_breaks = "6 months", date_labels =  "%b %y") +
  ylim(0,NA)

Demographics

Place of citizenship

Citizens of Mexico have been apprehended more than any other country both before and after the second Trump inauguration. However, apprehensions from citizens of countries throughout the world have increased since Jan 2025, though countries with the notably large increases include Honduras, Guatemala, Venezuela, and El Salvador.

country_tbl <- arrests_kc |> 
  comp_table(citizenship_region,month_norm=TRUE) |>
  make_output_cols() 

country_tbl |>
  add_before_after_change() |>
  head(15) |>
  gt_defaults("Average Monthly Arrests by Country/Region of Citizenship") 
Average Monthly Arrests by Country/Region of Citizenship
Citizenship Region Before Jan 2025 After Jan 2025 Percent Change
Mexico 20.7 55.2 167%
Honduras 6.4 27.7 333%
Guatemala 5.4 20.6 282%
Venezuela 1.3 7.8 500%
El Salvador 1.3 4.7 262%
Nicaragua 2.4 2.7 12%
Colombia 1.1 2.4 118%
Cuba 1.4 1.8 29%
Southern Asia 0.1 1.5 1,400%
Ecuador 0.2 1.0 400%
South America 0.8 0.9 12%
South-Eastern Asia 0.0 0.9 -
Eastern Asia 0.1 0.8 700%
Eastern Europe 0.6 0.8 33%
Western Asia 0.0 0.6 -
n <- 10
country_tbl |>
  head(10) |>
  pivot_for_plot() |>
  ggplot(aes(x=reorder(`Citizenship Region`,`value`,decreasing=FALSE),y=`value`,
             label=signif(`value`,2),fill=`Period`)) +
  geom_col(position="dodge",width=.7) +
  scale_fill_manual(values=color_vec) +
  geom_text(position = position_dodge(width = .7),vjust=-.3,size=2.75) +   
  labs(
    title="Average Monthly Arrests by Country of Citizenship",
    subtitle=paste0(
      "Kansas City Area, ",
      format(first_date,"%b %Y"),"-",
      format(last_date,"%b %Y"),"\n",
      "Top ",n, " Countries with the Most Apprehensions"
    ),
    x="Citizenship Country",y="Average Monthly Arrests") +
  theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1))

Deportations to third countries

For arrested individuals who are later deported, this table shows how many individuals were sent to a different country than the one for which they have citizenship.

This is fairly uncommon for Kansas City arrests, though it has increased since Jan 2025, with Mexico being the most common departure country by far.

arrests_kc |>
  mutate(
    departure_country=str_to_title(departure_country),
    citizenship_country=str_to_title(citizenship_country)
  ) |>
  filter(!is.na(departure_country) & departure_country!=citizenship_country) |>
  filter(!post_0125) |>
  count(citizenship_country,departure_country,name="number_of_people") |>
  arrange(desc(number_of_people)) |>
  make_output_cols() |>
  gt() |>
  tab_header("Arrests Leading to Departures to Third Countries (Oct 2022-Jan 2025)")
Arrests Leading to Departures to Third Countries (Oct 2022-Jan 2025)
Citizenship Country Departure Country Number Of People
Mexico Honduras 3
Brazil Belize 1
Cuba Canada 1
Guatemala Ethiopia 1
Honduras Guatemala 1
Honduras Mexico 1
Romania Italy 1
Romania Poland 1
United Kingdom Ukraine 1
Venezuela Dominican Republic 1
Venezuela Honduras 1
arrests_kc |>
  mutate(
    departure_country=str_to_title(departure_country),
    citizenship_country=str_to_title(citizenship_country)
  ) |>
  filter(!is.na(departure_country) & departure_country!=citizenship_country) |>
  filter(post_0125) |>
  count(citizenship_country,departure_country,name="number_of_people") |>
  arrange(desc(number_of_people)) |>
  make_output_cols() |>
  gt() |>
  tab_header("Arrests Leading to Departures to Third Countries (Feb 2025-Feb 2026)")
Arrests Leading to Departures to Third Countries (Feb 2025-Feb 2026)
Citizenship Country Departure Country Number Of People
Cuba Mexico 9
Venezuela Mexico 9
Honduras Mexico 2
Mexico Honduras 2
El Salvador Mexico 1
Guatemala Honduras 1
Guatemala Mexico 1
Nicaragua Mexico 1
Peru Honduras 1
Rwanda Burundi 1
United Kingdom Kosovo 1

Age and gender

Each column in this table shows the percent each age group makes up of all arrests within a gender. For example, 6-10 year olds make up 4.5% of all female arrests.

For both men and women, people in their 20s and 30s make up over half of all arrests.

arrests_kc |>
  count(age_bin,gender) |>
  pivot_wider(names_from=gender,values_from=n) |>
  column_percents() |>
  make_output_cols() |>
  select(-`Unknown`) |>
  gt() |>
  tab_header("Percent of Arrests by Age (Oct 2022-Feb 2026)") |>
  sub_values(
    fn = function(x) is.na(x),
    replacement = "-"
  ) |>  
  fmt_percent(decimals = 1,scale_values = FALSE)
## Warning in `[<-.factor`(`*tmp*`, vec_logical, value = "-"): invalid factor
## level, NA generated
Percent of Arrests by Age (Oct 2022-Feb 2026)
Age Bin Female Male
0-1 0.7% 0.1%
2-5 3.1% 0.2%
6-10 4.5% 0.5%
11-14 0.7% 0.5%
15-17 1.0% 0.3%
18-21 8.7% 6.7%
22-29 30.2% 28.3%
30-39 28.8% 34.2%
40-49 16.7% 21.7%
50-59 4.2% 6.0%
60-69 1.0% 1.5%
70-79 - 0.1%
80-84 0.3% -

The below table and chart show the change in the number of arrests by age. Arrests of people under 18 have decreased since the end of January 2025. However, apprehensions of 18-21 year olds have more than tripled, as have arrests of those 50 and older.

age_tbl <- arrests_kc |>
  comp_table(age_bin,sort_by_var = TRUE,str_title = FALSE) |>
  make_output_cols() 

age_tbl |>
  add_before_after_change() |>
  gt_defaults("Average Monthly Arrests by Age")
## Warning in `[<-.factor`(`*tmp*`, vec_logical, value = "-"): invalid factor
## level, NA generated
## Warning in `[<-.factor`(`*tmp*`, vec_logical, value = "-"): invalid factor
## level, NA generated
Average Monthly Arrests by Age
Age Bin Before Jan 2025 After Jan 2025 Percent Change
0-1 0.1 0.0 −100%
2-5 0.5 0.1 −80%
6-10 0.7 0.5 −29%
11-14 0.4 0.2 −50%
15-17 0.4 0.0 −100%
18-21 2.6 9.8 277%
22-29 11.6 39.2 238%
30-39 14.9 44.3 197%
40-49 9.0 28.8 220%
50-59 2.6 7.5 188%
60-69 0.6 1.8 200%
70-79 0.0 0.2 -
80-84 0.0 0.1 -
age_tbl |>
  pivot_for_plot() |>
  ggplot(aes(x=`Age Bin`,y=`value`,label=signif(`value`,2),fill=`Period`)) +
  geom_col(position="dodge",width=.7) +
  scale_fill_manual(values=color_vec) +
  geom_text(position = position_dodge(width = .8),vjust=-.6,size=2.75) +   
  labs(title="Average Monthly Arrests by Age",
       subtitle=paste0(
         "Kansas City Area, ",
         format(first_date,"%b %Y"),"-",
         format(last_date,"%b %Y")
        ),
       x="Age Bin",y="Average Monthly Arrests") 

Looking at the number of arrests by gender shows that both men and women are being arrested at much higher rates, with a larger increase in arrests of men.

gender_tbl <- arrests_kc |>
  comp_table(gender,sort_by_var = TRUE) |>
  make_output_cols() 

gender_tbl |>
  add_before_after_change() |>
  gt_defaults("Average Monthly Arrests by Gender")
Average Monthly Arrests by Gender
Gender Before Jan 2025 After Jan 2025 Percent Change
Female 5.4 10.5 94%
Male 37.4 120.4 222%
Unknown 0.6 1.8 200%
gender_tbl |>
  pivot_for_plot() |>
  ggplot(aes(x=`Gender`,y=`value`,label=signif(`value`,2),fill=`Period`)) +
  geom_col(position="dodge",width=.7) +
  scale_fill_manual(values=color_vec) +
  geom_text(position = position_dodge(width = .7),vjust=-.6,size=2.75) +   
  labs(title="Average Monthly Arrests by Gender",
       subtitle=paste0(
         "Kansas City Area, ",
         format(first_date,"%b %Y"),"-",
         format(last_date,"%b %Y")
        ),
       x="Gender",y="Average Monthly Arrests") 

Apprehension Tactics

Locations

There are a very large number of values in the apprehension_method column.

arrests_kc |> 
  count(apprehension_method) |> 
  arrange(desc(n)) |>
  rename(`Apprehension Method`=apprehension_method,`Number of Arrests`=n) |>
  gt()
Apprehension Method Number of Arrests
CAP LOCAL INCARCERATION 1004
CUSTODIAL ARREST 814
NON-CUSTODIAL ARREST 499
LOCATED 352
CAP FEDERAL INCARCERATION 173
OTHER EFFORTS 52
INSPECTIONS 31
CAP STATE INCARCERATION 7
PATROL INTERIOR 3
LAW ENFORCEMENT AGENCY RESPONSE UNIT 2
ANTI-SMUGGLING 1
BOAT PATROL 1
ORGANIZED CRIME DRUG ENFORCEMENT TASK FORCE 1
OTHER AGENCY (TURNED OVER TO INS) 1
PROBATION AND PAROLE 1

To simplify this, creating groups of apprehension methods to get easier-to-use place information. Per the Deportations Data Project FAQ

…there are two indicators that may be useful: in the arrests table when “Apprehension Method” is “Located” or “Non-Custodial Arrest” we think that these records are more likely to indicate arrests in the community.

Based on this and other research, setting those values as community arrests, CAP Incarcerations and custodial arrests as prison/jail arrests and creating an “Other” category for the rest.

arrests_kc <- arrests_kc |>
  mutate(
    apprehension_location = factor(
      case_when(
        apprehension_method %in% c("LOCATED","NON-CUSTODIAL ARREST") ~ 
          "Community",
        str_detect(apprehension_method,"^CAP .* INCARCERATION$") ~ 
        "Prison/Jail",
        apprehension_method == "CUSTODIAL ARREST" ~ 
          "Prison/Jail",      
        TRUE ~ "Others"
      ), levels = c("Community","Prison/Jail","Others")
    )
  )
  
arrests_kc |>
  count(apprehension_location)
## # A tibble: 3 × 2
##   apprehension_location     n
##   <fct>                 <int>
## 1 Community               851
## 2 Prison/Jail            1998
## 3 Others                   93

Both community and prison/jail arrests have seen large increases since Jan 2025, with the former occurring twice as often each month and the latter more than tripling.


arrests_kc |>
  make_output_cols() |>
  ggplot(aes(x=`Apprehension Month`,fill=`Apprehension Location`)) +
  geom_bar(stat="count") +
  scale_fill_manual(values=loc_color_vec) +  
  inaug_line + 
  add_inaug_text(as.Date("2024-06-03")) +
  labs(title="Average Monthly Arrests by Location Type",
       subtitle=paste0(
         "Kansas City Area, ",
         format(first_date,"%b %Y"),"-",
         format(last_date,"%b %Y")
        ),
       y="Number of Apprehensions") +
  scale_x_date(date_breaks = "6 months", date_labels =  "%b %y") +
  ylim(0,NA)

loc_tbl <- arrests_kc |>
  comp_table(apprehension_location,sort_by_var = TRUE) |>
  make_output_cols() 

loc_tbl |>
  arrange(desc(`After Jan 2025`)) |>
  add_before_after_change() |>
  gt_defaults("Average Monthly Arrests by Location Type")
Average Monthly Arrests by Location Type
Apprehension Location Before Jan 2025 After Jan 2025 Percent Change
Prison/Jail 26.0 97.6 275%
Community 14.6 33.9 132%
Others 2.8 1.2 −57%

Daily peaks

Looking at arrests over time we can see that certain months have peaks in arrests, particularly arrests within the community. Breaking this out by specific days shows that June 3rd - June 4th and June 25th, 2025 had the highest number of community-based arrests, though August 2025 and February 2026 both have multiple days with at least 5 arrests.

There are a handful of days prior to Feb 2026 with 5 or more arrests, though the large majority have occurred during the current administration.

arrests_kc |>
  filter(apprehension_location=="Community") |>
  count(apprehension_date) |>
  filter(n>=5) |> 
  arrange(desc(n)) |>
  mutate(apprehension_date=format(apprehension_date,"%B %d, %Y")) |>
  rename(`Apprehension Date`=apprehension_date,
         `Number of Arrests`=n) |>
  gt()
Apprehension Date Number of Arrests
June 04, 2025 9
June 25, 2025 9
January 26, 2025 8
March 26, 2024 7
June 03, 2025 7
November 04, 2025 7
February 03, 2026 7
October 11, 2022 6
December 11, 2023 6
January 30, 2024 6
April 23, 2024 6
February 01, 2025 6
June 16, 2025 6
July 09, 2025 6
August 09, 2025 6
January 27, 2026 6
February 05, 2026 6
October 07, 2022 5
July 01, 2024 5
July 10, 2024 5
September 30, 2024 5
March 18, 2025 5
May 20, 2025 5
June 11, 2025 5
August 12, 2025 5
August 15, 2025 5
December 16, 2025 5
February 10, 2026 5
February 11, 2026 5

Criminality

Overall criminality

Looking at arrests based on whether people have been charged/convicted of a crime. For this section, note that “Other Immigration Violator” indicates the person does not have any charges or convictions against them, per the Deportation Data Project Codebook.

While all groups have seen increases in arrests, the number of arrests of people who have been charged with a crime but not yet convicted has skyrocketed.

crim_tbl <- arrests_kc |>
  comp_table(apprehension_criminality,sort_by_var = TRUE) |>
  make_output_cols() 

crim_tbl |>
  add_before_after_change() |>
  gt_defaults("Average Monthly Arrests by Criminality")
Average Monthly Arrests by Criminality
Apprehension Criminality Before Jan 2025 After Jan 2025 Percent Change
1 Convicted Criminal 25.0 52.7 111%
2 Pending Criminal Charges 7.3 60.4 727%
3 Other Immigration Violator 11.1 19.6 77%
crim_tbl |>
  pivot_for_plot() |>
  ggplot(aes(x=`Apprehension Criminality`,y=`value`,
             label=signif(`value`,2),fill=`Period`)) +
  geom_col(position="dodge",width=.7) +
  scale_fill_manual(values=color_vec) +
  geom_text(position = position_dodge(width = .7),vjust=-.6,size=2.75) +   
  labs(title="Average Monthly Arrests by Criminality",
       subtitle=paste0(
         "Kansas City Area, ",
         format(first_date,"%b %Y"),"-",
         format(last_date,"%b %Y")
        ),
       x="Criminality",y="Average Monthly Arrests") 

Looking at this by month shows an immediate increase in the number of individuals arrested with pending criminal charges, the smallest group prior to the current administration.

This also shows some clear peaks in arrests of those with neither a criminal charge or conviction, similar to the peaks in community-based arrests shown in the previous section.

crim_color_vec <- c("1 Convicted Criminal"="lightcoral",
                    "2 Pending Criminal Charges"="lightblue4",
                    "3 Other Immigration Violator"="darkmagenta")

arrests_kc |>
  mutate(apprehension_criminality=str_to_title(apprehension_criminality)) |>
  make_output_cols() |>
  ggplot(aes(x=`Apprehension Month`,fill=`Apprehension Criminality`)) +
  geom_bar(stat="count") +
  scale_fill_manual(values=crim_color_vec) +  
  inaug_line + 
  add_inaug_text(as.Date("2024-05-20")) +
  labs(title="Number of Apprehensions by Criminality",
       subtitle=paste0(
         "Kansas City Area, ",
         format(first_date,"%b %Y"),"-",
         format(last_date,"%b %Y")
        ),
       y="Number of Apprehensions") +
  scale_x_date(date_breaks = "6 months", date_labels =  "%b %y") +
  ylim(0,NA)

Severity of crimes

For those convicted of a crime, also looking at the severity of that crime, using the case_threat_level variable.

Per the Deportation Data Project, this data is potentially based on criteria from a 2011 ICE Memo

  • Level 1: Aggravated felony
  • Level 2: Felony or at least 3 misdemeanors
  • Level 3: Misdemeanors

Creating a single variable to combine criminality and severity.

crim_levels <- 5:0
crim_labels <- c(
  "No Criminal Charges",
  "Criminal Charges, No Convictions",
  "Conviction: 1-2 Misdemeanors",
  "Conviction: Felony/3 or More Misdemeanors",    
  "Conviction: Aggravated Felony",
  "Conviction: Unknown Severity"
)

arrests_kc <- arrests_kc |> 
  mutate(crim_summ=
    case_when(
      is.na(apprehension_criminality) ~ NA,
      apprehension_criminality == "3 OTHER IMMIGRATION VIOLATOR" ~ 5,
      apprehension_criminality == "2 PENDING CRIMINAL CHARGES" ~ 4,
      case_threat_level==3 ~ 3,
      case_threat_level==2 ~ 2,    
      case_threat_level==1 ~ 1,
      TRUE ~ 0
    )
  )
arrests_kc <- arrests_kc |>
  mutate(crim_summ=factor(crim_summ,levels = crim_levels,labels=crim_labels))

Breaking down arrests by threat level shows that those convicted of only 1-2 misdemeanors have seen much larger increases in arrests than those convicted of more serious crimes. Meanwhile, those who are convicted of an aggravated felony actually have the smallest increase in number of arrests out of any group.

crim_levels <- 5:0
crim_labels <- c(
  "No Criminal Charges",
  "Criminal Charges, No Convictions",
  "Conviction: 1-2 Misdemeanors",
  "Conviction: Felony/3 or More Misdemeanors",    
  "Conviction: Aggravated Felony",
  "Conviction: Unknown Severity"
)

sev_tbl <- arrests_kc |>
  mutate(crim_summ=
    case_when(
      is.na(apprehension_criminality) ~ NA,
      apprehension_criminality == "3 OTHER IMMIGRATION VIOLATOR" ~ 5,
      apprehension_criminality == "2 PENDING CRIMINAL CHARGES" ~ 4,
      case_threat_level==3 ~ 3,
      case_threat_level==2 ~ 2,    
      case_threat_level==1 ~ 1,
      TRUE ~ 0
    )
  ) |>
  comp_table(crim_summ) |>
  mutate(crim_summ=factor(crim_summ,levels=crim_levels,
                          labels=crim_labels)) |>
  arrange(crim_summ) |>
  filter(crim_summ!="Conviction: Unknown Severity") |>
  make_output_cols() 

sev_tbl |>
  add_before_after_change() |>
  rename(`Criminality`=`Crim Summ`) |>
  gt_defaults("Average Monthly Arrests by Criminality/Severity") 
## Warning in `[<-.factor`(`*tmp*`, vec_logical, value = "-"): invalid factor
## level, NA generated
## Warning in `[<-.factor`(`*tmp*`, vec_logical, value = "-"): invalid factor
## level, NA generated
Average Monthly Arrests by Criminality/Severity
Criminality Before Jan 2025 After Jan 2025 Percent Change
No Criminal Charges 11.1 19.6 77%
Criminal Charges, No Convictions 7.3 60.4 727%
Conviction: 1-2 Misdemeanors 8.7 25.9 198%
Conviction: Felony/3 or More Misdemeanors 6.5 12.3 89%
Conviction: Aggravated Felony 9.9 14.2 43%
sev_tbl |>
  pivot_for_plot() |>
  ggplot(aes(x=`Crim Summ`,y=`value`,
             label=signif(`value`,2),fill=`Period`)) +
  geom_col(position="dodge",width=.7) +
  scale_fill_manual(values=color_vec) +
  geom_text(position = position_dodge(width = .7),vjust=-.6,size=2.75) +   
  scale_x_discrete(labels = scales::wrap_format(10)) + 
  labs(title="Average Monthly Arrests by Criminality/Severity",
       subtitle=paste0(
         "Kansas City Area, ",
         format(first_date,"%b %Y"),"-",
         format(last_date,"%b %Y")
        ),
       x="Criminality/Severity",y="Average Monthly Arrests")