---
title: "One Year of Immigration Enforcement Under the Second Trump Administration"
image: /img/first-nine-months-cover.svg
author:
- name: "Graeme Blair"
url: "https://graemeblair.com"
affiliation: "Professor of Political Science, UCLA"
- name: "David Hausman"
url: "https://www.david-hausman.com"
affiliation: "Assistant Professor, UC Berkeley School of Law"
date: "April 7, 2026" # make sure to change date
format:
html:
code-tools: true
fig-cap-location: top
reference-location: margin
pdf:
knitr:
opts_chunk:
dev: "cairo_pdf"
execute:
out-width: "0.6\\linewidth"
fig-width: 6.5
fig-height: 4
linkcolor: black
urlcolor: black
citecolor: black
colorlinks: true
titlepage: false
filters:
- ../filters/report-cover.lua
fig-cap-location: top
fontsize: 11pt
documentclass: report
subparagraph: yes
geometry: left=1.25in, right=1.25in, top=1.25in, bottom=1.25in
pdf-engine: lualatex
mainfont: Times New Roman
include-in-header:
text: |
\usepackage[normalem]{ulem}
\usepackage{xurl}
\makeatletter
\AtBeginDocument{%
\@ifpackageloaded{hyperref}{%
\let\oldhref\href
\renewcommand{\href}[2]{\oldhref{#1}{\uline{#2}}}%
}{}%
}
\makeatother
\usepackage[dvipsnames]{xcolor}
\definecolor{bgcolor}{HTML}{3C4D61}
\usepackage{setspace}
\setstretch{1.2}
\setlength{\parindent}{2em}
\newfontfamily\headingfont{Playfair Display}
\newfontfamily\titlefont{Playfair Display}
\usepackage{caption}
\DeclareCaptionFont{playfair}{\titlefont}
\captionsetup[figure]{
font={large,bf,playfair},
labelfont={large,bf,playfair}
}
\makeatletter
\renewcommand{\section}{\@startsection{section}{1}{0pt}%
{-3.5ex plus -1ex minus -.2ex}%
{2.3ex plus .2ex}%
{\headingfont\Large\bfseries}}
\makeatother
echo: false
warning: false
error: false
cache: true
appendix-cite-as: display
title-block-banner: true
abstract-title: "Executive Summary"
custom-titlepage:
partial: ../partials/report-cover.tex
abstract: |
The number of deportations within the United States increased by a factor of five over the course of the first year of the second Trump administration. This new finding comes from a Deportation Data Project release of Immigration and Customs Enforcement (ICE) data covering the first year-plus of the Trump administration (updating a previous report covering the first nine months of the administration). The new dataset covers the period from the middle of the Biden administration through March 10, 2026. Comparing the last six months of the Biden administration to the recent peak of enforcement in January 2026, we find the following key trends: \
First, ICE arrests more than quadrupled (4.4x). ICE transfers from jails and prisons, which accounted for most ICE arrests before 2025, roughly doubled. ICE street arrests went up by a factor of eleven — including not only arrests in neighborhoods, but also immigration court and at ICE field offices during regular check-ins. Arrests not in jails or prisons at this order of magnitude are a new phenomenon. For both types of arrests, ICE was much less likely to target people with criminal convictions. These changes led to over an eightfold increase in arrests of people without criminal convictions. \
Second, the quadrupling (4.4x) of arrests resulted in an even larger rise (5x) in deportations because of increased detention space and decreased releases. The administration more than quadrupled (4.5x) the number of detention beds used for people arrested within the United States. That capacity increase was a result both of new funding (for new detention centers and more beds in existing detention centers) and of a decrease in arrests at the border. The large new detention centers ICE built or reopened accounted for less than a quarter of the new space.
In the past, people without criminal convictions were often released from detention on bond. That changed in 2025. Release within 60 days of arrest, common for people without criminal convictions who did not have a removal order before arrest in the last six months of the Biden administration (35%), became rare (7%). The rate of deportation within two months of arrest doubled for this group, from 27% to 57%. The declining release rate accounted for most of that increase in deportations. Perhaps because of the lower release rate, many more people chose to give up on their cases: voluntary departures and returns (which are rare compared to removals) increased by 28 times.
A small decline in enforcement in February and early March 2026, after Renée Good and Alex Pretti's killings in Minneapolis, did little to change these basic patterns.
---
::: {.content-visible when-format="html"}
[Read report as a PDF](/analysis/immigration-enforcement-first-year.pdf){.btn-action-primary .btn-action .btn .btn-outline-primary .btn-sm role="button"}
:::
```{r}
#| cache: false
library(tidyverse)
label_months <- function(x) {
labs <- format(x, "%B")
is_jan <- !is.na(x) & month(x) == 1
labs[is_jan] <- paste0(labs[is_jan], "\n", format(x[is_jan], "%Y"))
first_ok <- which(!is.na(x))[1]
if (!is.na(first_ok)) {
labs[first_ok] <- paste0(
format(x[first_ok], "%B"),
"\n",
format(x[first_ok], "%Y")
)
}
labs
}
if (knitr::is_html_output()) {
knitr::opts_chunk$set(
dev = "svglite",
dev.args = list(
bg = "transparent",
system_fonts = list(sans = "Arial")
)
)
} else if (knitr::is_latex_output()) {
knitr::opts_chunk$set(
dev.args = list(family = "Arial")
)
}
knitr::opts_chunk$set()
cb_palette <- c("#0072B2", "#D55E00", "#009E73", "#CC79A7")
theme_minimal_transparent <- function(sz = 13) {
theme_minimal(base_size = sz) +
theme(
plot.background = element_rect(fill = "transparent", color = NA),
panel.background = element_rect(fill = "transparent", color = NA),
legend.background = element_rect(fill = "transparent", color = NA),
legend.box.background = element_rect(fill = "transparent", color = NA),
legend.position = "bottom",
axis.title.y = element_text(color = "grey30", margin = margin(r = 15)),
axis.title.x = element_text(color = "grey30", margin = margin(t = 15))
)
}
```
```{r}
detention_stays <- arrow::read_parquet(
"https://github.com/deportationdata/ice/raw/refs/heads/main/data/detention-stays-latest.parquet"
)
detention_stints <- arrow::read_parquet(
"https://github.com/deportationdata/ice/raw/refs/heads/main/data/detention-stints-latest.parquet"
)
arrests <- arrow::read_parquet(
"https://github.com/deportationdata/ice/raw/refs/heads/main/data/arrests-latest.parquet"
)
removals <- arrow::read_feather(
"~/github/ice/data/removals-latest.feather"
)
detention_arrest_join <-
detention_stays |>
select(stay_ID, unique_identifier, stay_book_in_date_time) |>
left_join(
arrests |>
filter(!is.na(unique_identifier)) |>
mutate(
reprocessed_record = apprehension_method == "ERO Reprocessed Arrest"
) |>
arrange(unique_identifier, reprocessed_record, apprehension_date_time) |>
mutate(
new_episode = is.na(lag(apprehension_date_time)) |
difftime(
apprehension_date_time,
lag(apprehension_date_time),
units = "hours"
) >
24,
episode_id = cumsum(new_episode),
.by = "unique_identifier"
) |>
# keep the last arrest in each <=24h-connected episode
filter(row_number() == 1, .by = c("unique_identifier", "episode_id")) |>
# if any episodes still have exact timestamp duplicates, keep one
filter(
row_number() == 1,
.by = c("unique_identifier", "episode_id", "apprehension_date_time")
) |>
select(-new_episode, -episode_id) |>
select(
unique_identifier,
apprehension_date_time_arrest = apprehension_date_time,
apprehension_criminality,
apprehension_method,
apprehension_aor,
file_original_arrest = file_original,
row_original_arrest = row_original,
sheet_original_arrest = sheet_original
),
by = "unique_identifier",
relationship = "many-to-many"
) |>
mutate(
time_diff = as.numeric(difftime(
stay_book_in_date_time,
apprehension_date_time_arrest,
units = "hours"
))
) |>
group_by(stay_ID) |>
filter(time_diff <= 24 * 10, time_diff >= 24 * -5) |>
slice_min(order_by = abs(time_diff), n = 1, with_ties = FALSE) |>
ungroup() |>
select(-time_diff, -stay_book_in_date_time, -unique_identifier)
detention_removal_join <-
detention_stays |>
select(
stay_ID,
unique_identifier,
stay_book_out_date_time,
departed_date,
departure_country
) |>
# candidate 1: departed_date from the stay record itself
mutate(
departed_date_stay = departed_date,
departure_country_stay = departure_country
) |>
select(-departed_date, -departure_country) |>
# candidate 2: departed_date from the removals file
left_join(
removals |>
filter(!is.na(unique_identifier)) |>
select(
unique_identifier,
departed_date_removal = departed_date,
departure_country_removal = departure_country
),
by = "unique_identifier",
relationship = "many-to-many"
) |>
# stack both candidates per stay
pivot_longer(
cols = c(departed_date_stay, departed_date_removal),
names_to = "source",
values_to = "departed_date_candidate",
values_drop_na = TRUE
) |>
mutate(
departure_country = if_else(
source == "departed_date_stay",
departure_country_stay,
departure_country_removal
)
) |>
select(-departure_country_stay, -departure_country_removal) |>
mutate(
time_diff = as.numeric(difftime(
departed_date_candidate,
stay_book_out_date_time,
units = "days"
))
) |>
filter(time_diff >= -5, time_diff <= 10) |>
slice_min(
order_by = abs(time_diff),
n = 1,
with_ties = FALSE,
by = stay_ID
) |>
select(
stay_ID,
departed_date_removal = departed_date_candidate,
departure_country
)
detention_stays <-
detention_stays |>
mutate(
border_arrest = final_program %in%
c(
"Border Patrol",
"Inspections - Air",
"Inspections - Land",
"Inspections - Sea",
"Coast Guard"
)
)
detentions_with_arrests <-
detention_stays |>
left_join(
detention_arrest_join |> mutate(has_arrest = 1),
by = "stay_ID",
relationship = "one-to-one"
) |>
left_join(
detention_removal_join,
by = "stay_ID",
relationship = "one-to-one"
) |>
mutate(
has_arrest = if_else(is.na(has_arrest), 0L, has_arrest)
) |>
filter(
stay_book_in_date_time >= as.Date("2024-05-01") |
stay_book_out_date_time >= as.Date("2024-05-01") |
is.na(stay_book_out_date_time)
)
new_facilities <-
detention_stints |>
# identify all of the facilities used in 2025 or 2026 but not in 2022-2024
group_by(detention_facility_code) |>
summarize(
used_before = any(as.Date(book_in_date_time) < as.Date("2025-01-20")),
used_after = any(as.Date(book_in_date_time) >= as.Date("2025-01-20"))
) |>
filter(used_after == TRUE, used_before == FALSE) |>
pull(detention_facility_code)
detention_stints <-
detention_stints |>
filter(
book_in_date_time >= as.Date("2024-05-01") |
book_out_date_time >= as.Date("2024-05-01") |
is.na(book_out_date_time)
)
rm(
detention_stays,
arrests,
removals,
detention_arrest_join,
detention_removal_join
)
```
## Introduction
The story of immigration enforcement in the first year-plus of the second Trump administration is a story of a drastic increase in interior immigration enforcement: enforcement that occurs within the United States, usually far from the border. Interior deportations surged to five times their pre-inauguration level (see @fig-deportations).[^1]
The steep increase in ICE deportations reflects two other large-scale changes.
First, ICE began making more arrests of people without criminal convictions and more arrests at large in communities and at ICE check-ins.^[The at large arrests principally target Latinos, according to journalists, advocates, and human rights observers. See for example, Human Rights Watch, [US: ICE Abuses in Los Angeles Set Stage for Other Cities](https://www.hrw.org/news/2025/11/04/us-ice-abuses-in-los-angeles-set-stage-for-other-cities).] For the two decades before 2025, ICE made very few such arrests, which we call street arrests. Instead, ICE relied on transfers from jails and prisons for its interior enforcement. In 2025, the number of street arrests went up by 11 times (see @fig-jails-street). Meanwhile, the number of transfers[^2] more than doubled, partly because ICE arrested more people without criminal convictions or with convictions for minor crimes.
::: {.content-visible when-format="html"}
Second, even as ICE arrested people more randomly, which should have made deportation less likely in each case, deportation following arrest became more likely because ICE started releasing fewer people.[^3] ICE more than quadrupled the number of detention beds used for interior enforcement (see @fig-border-interior). It did so not only by spending more money on detention, but also because of a decline in arrivals at the border. That decline in arrivals, which [mostly occurred](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=6124006) in the last year of the Biden administration (see sidebar figure), freed up detention beds for people arrested within the United States. At the same time, as the administration increased the use of detention, detention has begun to lead to removal more often. Overall, the chance of deportation within 60 days of detention increased from 56 percent to 63 percent. This effect was more dramatic for the group of detainees most likely to be released in the past: those without criminal convictions or a final order of removal. In this group, the chance of deportation within sixty days more than doubled, from 26 percent to 57 percent. As the release rate decreased, the overall number of voluntary departures increased by 28 times.
:::
::: {.content-visible when-format="pdf"}
Second, even as ICE arrested people more randomly, which should have made deportation less likely in each case, deportation following arrest became more likely because ICE started releasing fewer people.[^3] ICE more than quadrupled the number of detention beds used for interior enforcement (see @fig-border-interior). It did so not only by spending more money on detention, but also because of a decline in arrivals at the border. That decline in arrivals, which [mostly occurred](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=6124006) in the last year of the Biden administration (see @fig-bp-arrests-pdf), freed up detention beds for people arrested within the United States. At the same time, as the administration increased the use of detention, detention has begun to lead to removal more often. Overall, the chance of deportation within 60 days of detention increased from 56 percent to 63 percent. This effect was more dramatic for the group of detainees most likely to be released in the past: those without criminal convictions or a final order of removal. In this group, the chance of deportation within sixty days more than doubled, from 26 percent to 57 percent. As the release rate decreased, the overall number of voluntary departures increased by 28 times.
:::
::: {.content-visible when-format="html"}
```{r}
#| fig-cap: "Border Patrol Arrests"
#| column: margin
#| fig-cap-location: top
# manually extracted from from nationwide encounters dashboard https://www.cbp.gov/newsroom/stats/nationwide-encounters
bp_df <-
tribble(
~fiscal_year , ~OCT , ~NOV , ~DEC , ~JAN , ~FEB , ~MAR , ~APR , ~MAY , ~JUN , ~JUL , ~AUG , ~SEP ,
"2026" , 9847 , 9233 , 8080 , 7883 , 8236 , NA , NA , NA , NA , NA , NA , NA ,
"2025" , 58115 , 47534 , 48137 , 30127 , 9241 , 8193 , 10007 , 10359 , 8014 , 6168 , 8085 , 10203 ,
"2024" , 190454 , 192357 , 251178 , 125440 , 142104 , 139124 , 131078 , 121646 , 87606 , 59655 , 60684 , 55993 ,
"2023" , 206902 , 209496 , 224017 , 131720 , 131547 , 165068 , 185149 , 172423 , 100606 , 134064 , 182377 , 220323 ,
) |>
pivot_longer(-fiscal_year, names_to = "month", values_to = "bp_arrests") |>
filter(!is.na(bp_arrests)) |>
mutate(
fiscal_year = as.numeric(fiscal_year),
year = if_else(
month %in% c("OCT", "NOV", "DEC"),
fiscal_year - 1,
fiscal_year
)
) |>
mutate(date = ymd(str_c(year, "-", month, "-1")))
bp_df |>
filter(year >= 2023) |>
ggplot(aes(date, bp_arrests)) +
geom_line(size = 3) +
geom_vline(
color = "grey30",
xintercept = as.Date("2025-01-20"),
lty = "dashed"
) +
scale_y_continuous(
breaks = c(100000, 200000),
labels = scales::label_number(scale = 1e-3, suffix = "k")
) +
scale_x_date(
breaks = c(
as.Date("2023-01-01"),
as.Date("2024-01-01"),
as.Date("2025-01-01"),
as.Date("2026-01-01")
),
labels = c("2023", "2024", "2025", "2026")
) +
scale_color_manual(values = cb_palette, name = NULL) +
labs(x = "Date of arrest", y = "Monthly arrests") +
theme_minimal_transparent(35)
```
:::
The increase in available detention space and immigrants’ decisions not to fight their cases from detention help explain why, even as ICE arrested people who would be more likely to win their cases, such as those without any criminal convictions, the deportation rate among arrestees increased rather than decreased.
In this report, we document these two patterns using individual-level data on arrests and detentions obtained from ICE through litigation over a Freedom of Information Act request filed by Deportation Data Project Faculty Fellow Elora Mukherjee and represented by the Law Office of Amber Qureshi, who is Co-Director of the project. We draw on data from the second half of 2024 through March 10, 2026. We posted these datasets on the project website on March 30, promptly after receiving them from ICE. We do not begin analyzing data until we have released it to the public.
This report updates a previous [analysis](https://deportationdata.org/analysis/immigration-enforcement-first-nine-months-trump.html) of the first nine months of the second Trump administration, drawing on almost five months of new data.
## Interior Deportations Increased by Five Times
We know of no reliable count of the total number of deportations during the first year of the Trump administration.[^5] Instead of producing a total count, we focus on a key trend: the number of deportations over time following an ICE arrest and detention.[^6] @fig-deportations shows deportations following ICE arrest and detention over time.[^7] The number increased more than fivefold:[^8] there were five times as many deportations following ICE arrest and detention in January 2026 as during the average month in the second half of 2024.
We arrive at this estimate of the change in the number of deportations based on two key analytical choices. First, we consider only deportations that started with an arrest by ICE inside the United States. By separating interior and border enforcement, two countervailing trends emerge: the rise in interior enforcement on the one hand and the decline in border enforcement on the other.
Second, we compare January 2026 to the last half of 2024. The number of deportations rose dramatically over the course of 2025, and so our choice leads to a comparison of enforcement policy at roughly its recent peak, in January 2026, with that in the final months of the Biden Administration.
The rest of this report explains how such a large increase in deportations occurred, tracing that increase to changes in arrests and detention policy.
```{r}
#| fig-cap: "Interior Deportations by ICE Increased by Five Times"
#| label: fig-deportations
plot_df <-
detentions_with_arrests |>
filter(stay_book_out_date >= as.Date("2024-07-01")) |>
mutate(
remove = stay_release_reason %in%
c("Removed", "Voluntary Return", "Voluntary departure") |
!is.na(departed_date_removal)
) |>
mutate(
week = lubridate::round_date(stay_book_out_date, "week")
) |>
filter(has_arrest == TRUE, border_arrest == FALSE) |>
filter(remove == TRUE)
change_ratio <-
plot_df |>
mutate(
time_period = case_when(
as.Date(stay_book_out_date) >= as.Date("2024-07-01") &
as.Date(stay_book_out_date) <= as.Date("2024-12-31") ~ "before",
as.Date(stay_book_out_date) >= as.Date("2026-01-01") &
as.Date(stay_book_out_date) <= as.Date("2026-01-31") ~ "current"
),
month = case_when(
time_period == "before" ~ month(stay_book_out_date),
time_period == "current" ~ 1
)
) |>
filter(!is.na(time_period)) |>
group_by(time_period, month) |>
summarize(total = n()) |>
summarize(avg_monthly = mean(total)) |>
pivot_wider(names_from = time_period, values_from = avg_monthly) |>
summarize(current / before)
plot_df |>
count(week) |>
filter(week < as.Date("2026-03-08")) |>
ggplot(aes(week, n)) +
geom_vline(
color = "grey30",
xintercept = as.Date("2025-01-20"),
lty = "dashed"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-07"),
lty = "dotted"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-24"),
lty = "dotted"
) +
annotate(
color = "grey30",
"text",
x = as.Date("2025-01-10"),
y = Inf,
label = "Biden",
hjust = 1,
vjust = 1.5,
size = 4
) +
annotate(
color = "grey30",
"text",
x = as.Date("2025-01-30"),
y = Inf,
label = "Trump II",
hjust = 0,
vjust = 1.5,
size = 4
) +
annotate(
color = "#CC9999",
"text",
x = as.Date("2025-12-15"),
y = 0,
label = "Renee Good killed by ICE,\nAlex Pretti killed by CBP",
hjust = 1,
vjust = -0.3,
size = 3.2,
lineheight = 0.9
) +
geom_line(size = 0.8) +
scale_y_continuous(labels = scales::label_comma(), limits = c(0, NA)) +
scale_x_date(
breaks = function(x) {
seq(
floor_date(min(x, na.rm = TRUE), "quarter"),
ceiling_date(max(x, na.rm = TRUE), "quarter"),
by = "quarter"
)
},
labels = label_months
) +
labs(
x = "Date of book-out from detention for removal",
y = "Weekly removals"
) +
theme_minimal_transparent()
```
## More Arrests, Especially Street Arrests
The first reason the increase in deportations was possible was a 4.4x increase in ICE arrests resulting in detention. @fig-arrests illustrates the overall trend.[^9] ICE started making more of the arrests it has always made -- and also started making vastly more street arrests and arrests of people without criminal convictions.^[An early report of this pattern was published in the [Los Angeles Times](https://www.latimes.com/california/story/2025-06-24/detention-centers-swell-with-immigrants-with-no-criminal-record).]
<!-- Figure 2: Total ICE Arrests Resulting in Detention -->
```{r}
#| label: fig-arrests
#| fig.cap: "ICE Arrests Resulting in Detention More Than Quadrupled"
plot_df <-
detentions_with_arrests |>
filter(stay_book_in_date_time >= as.Date("2024-07-01")) |>
mutate(
week = lubridate::round_date(stay_book_in_date_time, "week")
) |>
filter(has_arrest == TRUE, border_arrest == FALSE)
change_ratio <-
plot_df |>
mutate(
time_period = case_when(
as.Date(stay_book_in_date_time) >= as.Date("2024-07-01") &
as.Date(stay_book_in_date_time) <= as.Date("2024-12-31") ~ "before",
as.Date(stay_book_in_date_time) >= as.Date("2026-01-01") &
as.Date(stay_book_in_date_time) <= as.Date("2026-01-31") ~ "current"
),
month = case_when(
time_period == "before" ~ month(stay_book_in_date_time),
time_period == "current" ~ 1
)
) |>
filter(!is.na(time_period)) |>
group_by(time_period, month) |>
summarize(total = n()) |>
summarize(avg_monthly = mean(total)) |>
pivot_wider(names_from = time_period, values_from = avg_monthly) |>
summarize(current / before)
plot_df |>
count(week) |>
filter(week < as.Date("2026-03-08")) |>
ggplot(aes(week, n)) +
geom_vline(
color = "grey30",
xintercept = as.Date("2025-01-20"),
lty = "dashed"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-07"),
lty = "dotted"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-24"),
lty = "dotted"
) +
geom_line(size = 0.8) +
annotate(
color = "grey30",
"text",
x = as.Date("2025-01-10"),
y = Inf,
label = "Biden",
hjust = 1,
vjust = 1.5,
size = 4
) +
annotate(
color = "grey30",
"text",
x = as.Date("2025-01-30"),
y = Inf,
label = "Trump II",
hjust = 0,
vjust = 1.5,
size = 4
) +
scale_y_continuous(
breaks = c(2000, 4000, 6000),
labels = scales::label_comma(),
limits = c(0, NA)
) +
scale_x_date(
breaks = function(x) {
seq(
floor_date(min(x, na.rm = TRUE), "quarter"),
ceiling_date(max(x, na.rm = TRUE), "quarter"),
by = "quarter"
)
},
labels = label_months
) +
labs(x = "Date of book-in to detention", y = "Weekly arrests") +
theme_minimal_transparent()
```
Before 2025, ICE arrests were mostly not arrests in the usual sense of the word. Instead, they were transfers of custody from jails or prisons. In such transfers, noncitizens who were initially arrested on criminal charges (typically unrelated to immigration law), are moved by ICE to immigration detention facilities, where they are held for civil violations of immigration law. In the first year of the new administration, ICE more than quadrupled total arrests by (1) more than doubling the number of such transfers and (2) increasing arrests *not* in jails or prisons by more than a factor of 11. @fig-jails-street illustrates these two trends.[^10] The over elevenfold increase from a low base explains why ICE street arrests seem like a new phenomenon. These arrests outside jails and prisons took place in places like neighborhoods, immigration courts, and check-in appointments at ICE offices.
```{r}
#| label: fig-jails-street
#| fig-cap: "Transfers from Jails and Prisons Doubled and Street Arrests Increased by 11x"
plot_df <-
detentions_with_arrests |>
filter(stay_book_in_date_time >= as.Date("2024-07-01")) |>
mutate(
week = lubridate::round_date(stay_book_in_date_time, "week"),
custodial = case_when(
apprehension_method %in%
c(
"CAP Federal Incarceration",
"CAP Local Incarceration",
"CAP State Incarceration",
"Criminal Alien Program",
"Custodial Arrest"
) ~ "Custodial arrests\n(Transfers from jail or prison)",
apprehension_method %in%
c(
"Non-Custodial Arrest",
"Located",
"Worksite Enforcement",
"Probation and Parole"
) ~ "Street Arrests"
# TRUE ~ "Non-custodial arrest\n(Street arrests, worksite raids)"
)
) |>
filter(has_arrest == TRUE, border_arrest == FALSE) |>
filter(!is.na(custodial))
change_ratio <-
plot_df |>
mutate(
time_period = case_when(
as.Date(stay_book_in_date_time) >= as.Date("2024-07-01") &
as.Date(stay_book_in_date_time) <= as.Date("2024-12-31") ~ "before",
as.Date(stay_book_in_date_time) >= as.Date("2026-01-01") &
as.Date(stay_book_in_date_time) <= as.Date("2026-01-31") ~ "current"
),
month = case_when(
time_period == "before" ~ month(stay_book_in_date_time),
time_period == "current" ~ 1
)
) |>
filter(!is.na(time_period)) |>
group_by(custodial, time_period, month) |>
summarize(total = n()) |>
summarize(avg_monthly = mean(total)) |>
pivot_wider(names_from = time_period, values_from = avg_monthly) |>
summarize(current / before)
plot_df |>
count(week, custodial) |>
filter(week < as.Date("2026-03-08")) |>
ggplot(aes(week, n, color = custodial)) +
geom_vline(
color = "grey30",
xintercept = as.Date("2025-01-20"),
lty = "dashed"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-07"),
lty = "dotted"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-24"),
lty = "dotted"
) +
geom_line(size = 0.8) +
annotate(
color = "grey30",
"text",
x = as.Date("2025-01-10"),
y = Inf,
label = "Biden",
hjust = 1,
vjust = 1.5,
size = 4
) +
annotate(
color = "grey30",
"text",
x = as.Date("2025-01-30"),
y = Inf,
label = "Trump II",
hjust = 0,
vjust = 1.5,
size = 4
) +
scale_color_manual(values = cb_palette, name = NULL) +
scale_y_continuous(labels = scales::label_comma(), limits = c(0, NA)) +
scale_x_date(
breaks = function(x) {
seq(
floor_date(min(x, na.rm = TRUE), "quarter"),
ceiling_date(max(x, na.rm = TRUE), "quarter"),
by = "quarter"
)
},
labels = label_months
) +
labs(x = "Date of book-in to detention", y = "Weekly arrests") +
theme_minimal_transparent()
```
The street arrests reflect several kinds of enforcement. The first and most visible are the large-scale operations in several U.S. cities that began in June 2025 in Los Angeles (see @fig-arrests-city). These operations led to thousands of street arrests in Los Angeles, Washington, D.C., Chicago, Minneapolis-St. Paul, and several other cities. But the arrests in those high-profile operations represented only a very small proportion of total arrests nationwide. Arrests also occurred in the rest of the country at high rates, as illustrated in @fig-jails-street; in fact, street arrests in Minneapolis represented only about 15% of nationwide street arrests even in the week in which they peaked.
```{r}
#| label: fig-arrests-city
#| fig-cap: "ICE Arrests Resulting in Detention by Area of Responsibility"
target_aors <- c(
"Los Angeles Area of Responsibility",
"Chicago Area of Responsibility",
"St. Paul Area of Responsibility",
"Washington Area of Responsibility"
)
plot_df <-
detentions_with_arrests |>
filter(
has_arrest == TRUE,
border_arrest == FALSE,
apprehension_method %in%
c(
"Non-Custodial Arrest",
"Located",
"Worksite Enforcement",
"Probation and Parole"
)
) |>
filter(stay_book_in_date_time >= as.Date("2024-07-01")) |>
mutate(
aor = case_when(
apprehension_aor %in% target_aors ~ apprehension_aor,
TRUE ~ "All other AORs"
),
aor = factor(
aor,
levels = c(
"Los Angeles Area of Responsibility",
"Washington Area of Responsibility",
"Chicago Area of Responsibility",
"St. Paul Area of Responsibility",
"All other AORs"
),
labels = c(
"Los Angeles",
"Washington D.C.",
"Chicago",
"Minneapolis-St. Paul",
"All other AORs"
)
),
week = lubridate::round_date(stay_book_in_date_time, "week")
) |>
filter(aor != "All other AORs") |>
mutate(aor = droplevels(aor))
aor_colors <- cb_palette[1:4]
names(aor_colors) <- levels(plot_df$aor)
# label data: one row per AOR, positioned at top-left of each panel
label_df <-
tibble(
aor = factor(levels(plot_df$aor), levels = levels(plot_df$aor)),
x = as.Date("2024-07-15"),
y = Inf
)
# Biden/Trump labels only in the first panel
admin_label_df <- tibble(
aor = factor(levels(plot_df$aor)[1], levels = levels(plot_df$aor)),
x = as.Date(c("2025-01-10", "2025-01-30")),
label = c("Biden", "Trump II"),
hjust = c(1, 0)
)
plot_df |>
count(week, aor) |>
filter(week < as.Date("2026-03-08")) |>
ggplot(aes(week, n)) +
geom_vline(
color = "grey30",
xintercept = as.Date("2025-01-20"),
lty = "dashed"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-07"),
lty = "dotted"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-24"),
lty = "dotted"
) +
geom_line(aes(color = aor), size = 0.8) +
geom_text(
data = label_df,
aes(x = x, y = y, label = aor, color = aor),
hjust = 0,
vjust = 2.2,
size = 4,
fontface = "bold"
) +
geom_text(
data = admin_label_df,
aes(x = x, y = Inf, label = label, hjust = hjust),
vjust = 1.5,
color = "grey30",
size = 4
) +
facet_wrap(~aor, ncol = 1) +
scale_color_manual(values = cb_palette, name = NULL, guide = "none") +
scale_y_continuous(labels = scales::label_comma(), limits = c(0, NA)) +
scale_x_date(
breaks = function(x) {
seq(
floor_date(min(x, na.rm = TRUE), "quarter"),
ceiling_date(max(x, na.rm = TRUE), "quarter"),
by = "quarter"
)
},
labels = label_months
) +
labs(x = "Date of book-in to detention", y = "Weekly street arrests") +
theme_minimal_transparent() +
theme(
strip.text = element_blank()
)
rm(plot_df)
```
The new administration has also more than doubled transfers from jails and prisons to ICE custody. Under the Biden administration, ICE often declined to transfer a person from a local jail to immigration detention if the person did not meet one of the government’s enforcement priorities -- for example, if the noncitizen had not been convicted of certain crimes.[^11] The new administration does not prioritize in this way.
For both transfers and street arrests, the Trump administration’s decision to stop prioritizing arrests based on factors such as criminal convictions resulted in another well-known trend: the huge increase in the number of arrests of noncitizens not convicted of any crime.
@fig-convictions shows trends in arrests by criminal conviction. Comparing the six months before the inauguration to January 2026, arrests of people without convictions went up by over eight times (8.7x); arrests of people with nonviolent convictions doubled, and arrests of people with convictions for violent crimes increased by over a third (37%).^[[CBS News](https://www.cbsnews.com/news/ice-detentions-non-criminal-immigrants-violent-crime-convictions-analysis/) published an early analysis of the crimes noncitizens were charged with in the first months of the new administration.]
```{r}
#| label: fig-convictions
#| fig-cap: "Arrests of Noncitizens Without Criminal Convictions Up 8x"
plot_df <-
detentions_with_arrests |>
mutate(
msc_code = as.character(most_serious_conviction_code),
# keep only pure 4-digit numeric NCIC-style codes for the UCR logic
msc4 = if_else(str_detect(msc_code, "^[0-9]{4}$"), msc_code, NA_character_),
# Homicide (09xx) EXCLUDING negligent manslaughter (0909, 0910)
ucr_violent = (str_detect(msc4, "^09") & !msc4 %in% c("0909", "0910")) |
# Rape / Sexual Assault (11xx) EXCLUDING statutory rape - no force (1116)
(str_detect(msc4, "^11") & msc4 != "1116") |
# Robbery (12xx)
str_detect(msc4, "^12") |
# Aggravated assault ONLY: 1301–1312 plus 1314–1315
msc4 %in%
c(
sprintf("13%02d", 1:12),
"1314",
"1315"
),
conviction = case_when(
ucr_violent ~ "Violent crime",
!is.na(most_serious_conviction_code) ~ "Nonviolent crime",
TRUE ~ "None"
)
) |>
filter(has_arrest == TRUE, border_arrest == FALSE) |>
filter(stay_book_in_date_time >= as.Date("2024-07-01")) |>
mutate(
week = lubridate::round_date(stay_book_in_date_time, "week")
)
change_ratio <-
plot_df |>
mutate(
time_period = case_when(
as.Date(stay_book_in_date_time) >= as.Date("2024-07-01") &
as.Date(stay_book_in_date_time) <= as.Date("2024-12-31") ~ "before",
as.Date(stay_book_in_date_time) >= as.Date("2026-01-01") &
as.Date(stay_book_in_date_time) <= as.Date("2026-01-31") ~ "current"
),
month = case_when(
time_period == "before" ~ month(stay_book_in_date_time),
time_period == "current" ~ 1
)
) |>
filter(!is.na(time_period)) |>
group_by(conviction, time_period, month) |>
summarize(total = n()) |>
summarize(avg_monthly = mean(total)) |>
pivot_wider(names_from = time_period, values_from = avg_monthly) |>
summarize(current / before)
plot_df |>
count(week, conviction) |>
filter(week < as.Date("2026-03-08")) |>
ggplot(aes(week, n, color = conviction)) +
geom_vline(
color = "grey30",
xintercept = as.Date("2025-01-20"),
lty = "dashed"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-07"),
lty = "dotted"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-24"),
lty = "dotted"
) +
geom_line(size = 0.8) +
annotate(
color = "grey30",
"text",
x = as.Date("2025-01-10"),
y = Inf,
label = "Biden",
hjust = 1,
vjust = 1.5,
size = 4
) +
annotate(
color = "grey30",
"text",
x = as.Date("2025-01-30"),
y = Inf,
label = "Trump II",
hjust = 0,
vjust = 1.5,
size = 4
) +
scale_y_continuous(labels = scales::label_comma()) +
scale_x_date(
breaks = function(x) {
seq(
floor_date(min(x, na.rm = TRUE), "quarter"),
ceiling_date(max(x, na.rm = TRUE), "quarter"),
by = "quarter"
)
},
labels = label_months
) +
scale_color_manual(values = cb_palette, name = "Convictions") +
labs(x = "Date of book-in to detention", y = "Weekly arrests") +
theme_minimal_transparent()
```
The end of prioritization affected both street arrests and transfers from jails and prisons. @fig-convictions-street-transfers illustrates both and shows that the trend is more pronounced among street arrests.^[Note that the numbers in the left and right panels of @fig-convictions-street-transfers do not quite add up to the number in @fig-jails-street because we are unsure how to categorize some ICE arrests.]
```{r}
#| label: fig-convictions-street-transfers
#| fig-cap: "Arrests of Noncitizens without Criminal Convictions Increased among Street Arrests and Also Among Transfers from Jails"
label_months_short <- function(x) {
labs <- format(x, "%b.")
is_jan <- !is.na(x) & month(x) == 1
labs[is_jan] <- paste0(labs[is_jan], "\n", format(x[is_jan], "%Y"))
first_ok <- which(!is.na(x))[1]
if (!is.na(first_ok)) {
labs[first_ok] <- paste0(
format(x[first_ok], "%b."),
"\n",
format(x[first_ok], "%Y")
)
}
labs
}
plot_df <-
detentions_with_arrests |>
mutate(
custodial = case_when(
apprehension_method %in%
c(
"CAP Federal Incarceration",
"CAP Local Incarceration",
"CAP State Incarceration",
"Criminal Alien Program",
"Custodial Arrest"
) ~ "Custodial arrests\n(Transfers from jail or prison)",
TRUE ~ "Non-custodial arrest\n(Street arrests)"
),
msc_code = as.character(most_serious_conviction_code),
# keep only pure 4-digit numeric NCIC-style codes for the UCR logic
msc4 = if_else(str_detect(msc_code, "^[0-9]{4}$"), msc_code, NA_character_),
# Homicide (09xx) EXCLUDING negligent manslaughter (0909, 0910)
ucr_violent = (str_detect(msc4, "^09") & !msc4 %in% c("0909", "0910")) |
# Rape / Sexual Assault (11xx) EXCLUDING statutory rape - no force (1116)
(str_detect(msc4, "^11") & msc4 != "1116") |
# Robbery (12xx)
str_detect(msc4, "^12") |
# Aggravated assault ONLY: 1301–1312 plus 1314–1315
msc4 %in%
c(
sprintf("13%02d", 1:12),
"1314",
"1315"
),
conviction = case_when(
ucr_violent ~ "Violent crime",
!is.na(most_serious_conviction_code) ~ "Nonviolent crime",
TRUE ~ "None"
)
) |>
filter(has_arrest == TRUE, border_arrest == FALSE) |>
filter(stay_book_in_date_time >= as.Date("2024-07-01")) |>
mutate(
week = lubridate::round_date(stay_book_in_date_time, "week")
) |>
count(week, conviction, custodial) |>
filter(week < as.Date("2026-03-08"))
plot_df |>
ggplot(aes(week, n, color = conviction)) +
geom_line(size = 0.8) +
geom_vline(
color = "grey30",
xintercept = as.Date("2025-01-20"),
lty = "dashed"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-07"),
lty = "dotted"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-24"),
lty = "dotted"
) +
annotate(
color = "grey30",
"text",
x = as.Date("2025-01-10"),
y = Inf,
label = "Biden",
hjust = 1,
vjust = 1.5,
size = 4
) +
annotate(
color = "grey30",
"text",
x = as.Date("2025-01-30"),
y = Inf,
label = "Trump II",
hjust = 0,
vjust = 1.5,
size = 4
) +
scale_y_continuous(labels = scales::label_comma()) +
scale_x_date(
breaks = function(x) {
seq(
floor_date(min(x, na.rm = TRUE), "quarter"),
ceiling_date(max(x, na.rm = TRUE), "quarter"),
by = "quarter"
)
},
labels = label_months_short
) +
facet_grid(~custodial) +
scale_color_manual(values = cb_palette, name = "Convictions") +
labs(x = "Week of book-in to detention", y = "Weekly arrests") +
theme_minimal_transparent()
```
## More Detentions, Fewer Releases
Deportations increased not only because more people were arrested but also because, first, ICE had more space to hold them, and second, the government changed the rules to prevent detainees from being released while fighting their cases.
The number of daily detention beds holding people arrested in the interior of the United States quadrupled over the course of 2025, increasing from a daily average of around 14,000 in the second half of 2024 to around 57,000 in January 2026. This was possible for two reasons. First, as widely reported, the total number of detention beds increased from around 40,000 in January 2025 to around 70,000 in January 2026,[^12] in large part because ICE received a huge infusion of cash from the One Big Beautiful Bill. @fig-facilities-by-type shows that, though several new detention facilities were opened or reopened in 2025, those accounted for less than a quarter of detention bed space. The majority of the increase in detention bed space came from ICE using more of the beds it already had access to.
```{r }
#| label: fig-facilities-by-type
#| fig-cap: "Detention Bed Space Use Increased Dramatically; Less than a Quarter Came from New and Reopened Large Facilities"
plot_df <-
detention_stints |>
left_join(
detentions_with_arrests |> select(stay_ID, has_arrest, border_arrest),
by = "stay_ID"
) |>
as_tibble() |>
mutate(
unique_identifier_nona = if_else(
is.na(unique_identifier),
str_c("noid_", row_number()),
unique_identifier
),
bop = str_detect(detention_facility_code, "^BOP"),
hold = str_detect(detention_facility_code, "HOLD"),
# county = str_detect(detention_facility, "COUNTY|SHERIFF|JAIL"),
big_new = detention_facility_code %in%
c(
"EROFCB", # Camp East Montana
"EPSSFTX", # El Paso soft-sided
"FLDSSFS", # Florida soft-sided / alligator alcatraz
"GTMOBCU", # Guantanamo
"GTMODCU", # Guantanamo
"GTMOACU", # Guantanamo
"INMIAMI", # Speedway slammer
"NEMCCOI", # Cornhusker clink
"LICEPLA", # Angola / camp 57
"DHDFNJ", # Delaney Hall
"CACTYCA", # California City Corrections - reopened by CoreCivic in 2025
"IRWINGA", # Irwin County - reopened by LaSalle Corrections in 2025
"NRLKCMI", # North Lake Processing Faciliy - reopened by Geo in 2025
"FIPCAGA", # Folkston annex - opened by Geo in 2025
"OKCIMMC", # Cimarron - CoreCivic 2025
"OKDBACK", # Diamondback - CoreCivic 2026
"STFRCTX", # Dilley
"FLBAKCI" # Baker Correctional Institution - reopened in 2025
),
type = case_when(
big_new ~ "Newly-built or reopened large facility",
# hold ~ "Hold room",
# county ~ "County",
TRUE ~ "All other facilities"
)
) |>
filter(has_arrest == TRUE, border_arrest == FALSE) |>
mutate(
start_date = as_date(book_in_date_time),
end_date = as_date(book_out_date_time),
end_date = if_else(is.na(end_date), as_date("2026-03-10"), end_date)
) |>
# ensure end_date is not before start_date (recode to start_date if so)
mutate(end_date = pmax(start_date, end_date)) |>
rowwise() |>
mutate(date = list(seq(start_date, end_date, by = "day"))) |>
unnest(date) |>
ungroup() |>
mutate(
week = lubridate::round_date(date, "week"),
detained_at_midnight = as.Date(book_in_date_time) <= date &
(is.na(book_out_date_time) |
as.Date(book_out_date_time) >= (date + 1))
)
plot_df |>
filter(detained_at_midnight) |>
group_by(date, type) |>
summarize(n = n_distinct(unique_identifier_nona), .groups = "drop") |>
mutate(week = lubridate::round_date(date, "week")) |>
group_by(week, type) |>
summarize(n = mean(n), .groups = "drop") |>
filter(week >= as.Date("2024-07-01"), week < as.Date("2026-03-08")) |>
ggplot(aes(week, n, color = type)) +
geom_vline(
color = "grey30",
xintercept = as.Date("2025-01-20"),
lty = "dashed"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-07"),
lty = "dotted"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-24"),
lty = "dotted"
) +
geom_line(size = 0.8) +
annotate(
color = "grey30",
"text",
x = as.Date("2025-01-10"),
y = Inf,
label = "Biden",
hjust = 1,
vjust = 1.5,
size = 4
) +
annotate(
color = "grey30",
"text",
x = as.Date("2025-01-30"),
y = Inf,
label = "Trump II",
hjust = 0,
vjust = 1.5,
size = 4
) +
scale_y_continuous(labels = scales::label_comma()) +
scale_x_date(
breaks = function(x) {
seq(
floor_date(min(x, na.rm = TRUE), "quarter"),
ceiling_date(max(x, na.rm = TRUE), "quarter"),
by = "quarter"
)
},
labels = label_months,
limits = c(as.Date("2024-07-01"), as.Date("2026-03-08"))
) +
scale_color_manual(values = cb_palette, name = NULL) +
labs(x = "Week of detentions", y = "Average overnight detainees\nby week") +
theme_minimal_transparent()
facility_persondays_df <-
plot_df |>
filter(
detained_at_midnight,
date >= as.Date("2025-01-31"),
date <= as.Date("2026-01-31")
) |>
mutate(
facility_label = case_when(
detention_facility_code == "EROFCB" ~ "Camp East Montana, TX",
detention_facility_code == "EPSSFTX" ~ "El Paso Soft-sided, TX",
detention_facility_code == "FLDSSFS" ~ "Florida Soft-sided, FL",
detention_facility_code %in%
c("GTMOBCU", "GTMODCU", "GTMOACU") ~ "Guantánamo Bay, CU",
detention_facility_code == "INMIAMI" ~ "Miami Speedway, IN",
detention_facility_code == "NEMCCOI" ~ "Nebraska (Cornhusker), NE",
detention_facility_code == "LICEPLA" ~ "Angola (Camp 57), LA",
detention_facility_code == "DHDFNJ" ~ "Delaney Hall, NJ",
detention_facility_code == "CACTYCA" ~ "California City Corrections, CA",
detention_facility_code == "IRWINGA" ~ "Irwin County, GA",
detention_facility_code == "NRLKCMI" ~ "North Lake, MI",
detention_facility_code == "FIPCAGA" ~ "Folkston Annex, GA",
detention_facility_code == "OKCIMMC" ~ "Cimarron, OK",
detention_facility_code == "OKDBACK" ~ "Diamondback, OK",
detention_facility_code == "STFRCTX" ~ "Dilley, TX",
TRUE ~ "All other facilities"
)
) |>
group_by(facility_label) |>
summarize(
person_days = n(),
.groups = "drop"
) |>
arrange(desc(person_days))
rm(plot_df)
```
::: {.content-visible when-format="html"}
::: {#tbl-facility-persondays .column-margin}
```{r}
new_facilities_total <- facility_persondays_df |>
filter(facility_label != "All other facilities") |>
summarize(person_days = sum(person_days)) |>
mutate(facility_label = "Total (new/reopened facilities)")
all_other <- facility_persondays_df |>
filter(facility_label == "All other facilities")
tbl_df <- facility_persondays_df |>
filter(facility_label != "All other facilities") |>
bind_rows(new_facilities_total) |>
bind_rows(all_other) |>
mutate(person_days = scales::label_comma()(person_days))
summary_rows <- which(
tbl_df$facility_label %in%
c("Total (new/reopened facilities)", "All other facilities")
)
kableExtra::kbl(
tbl_df,
col.names = c("Facility", "Person-Days"),
align = c("l", "r"),
booktabs = TRUE
) |>
kableExtra::kable_styling(
font_size = 11,
bootstrap_options = c("condensed"),
latex_options = c("hold_position", "scale_down")
) |>
kableExtra::row_spec(summary_rows, bold = TRUE) |>
kableExtra::row_spec(
summary_rows[1] - 1,
extra_css = "border-bottom: 2px solid #3C4D61;",
hline_after = TRUE
)
```
Detentions from interior arrests in new or reopened facilities.
:::
:::
The huge increase in detention was also possible because arrests at the border declined. @fig-border-interior shows these countervailing trends. As arrests at the border fell -- both because fewer people were trying to cross the border and because the new administration began expelling nearly everyone who did -- there was more space in detention for people arrested within the United States. ICE rapidly filled that space. A key note here is that the decrease in arrivals at the border mostly took place during the last year of the Biden administration. (As one of us has [explained](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=6124006) in other work, it is incorrect to suggest that the Trump administration is mostly responsible for the decline in arrivals, and there are reasons to believe that deterrence efforts at the border are less effective than commonly believed.)
```{r}
#| label: fig-border-interior
#| fig-cap: "Arrestees from the Interior Filled ICE Detention Bed Space Opened by Decreasing Border Arrests"
plot_df <-
detention_stints |>
left_join(
detentions_with_arrests |> select(stay_ID, has_arrest, border_arrest),
by = "stay_ID"
) |>
as_tibble() |>
mutate(
interior = case_when(
has_arrest == TRUE & border_arrest == FALSE ~ "Arrest in the interior",
TRUE ~ "Arrest near border"
)
) |>
filter(!is.na(interior)) |>
mutate(
start_date = as_date(book_in_date_time),
end_date = as_date(book_out_date_time),
end_date = if_else(is.na(end_date), as_date("2026-03-10"), end_date)
) |>
# ensure end_date is not before start_date (recode to start_date if so)
mutate(end_date = pmax(start_date, end_date)) |>
rowwise() |>
mutate(date = list(seq(start_date, end_date, by = "day"))) |>
unnest(date) |>
ungroup() |>
mutate(
unique_identifier_nona = if_else(
is.na(unique_identifier),
str_c("noid_", row_number()),
unique_identifier
)
) |>
group_by(date, interior) |>
summarize(
n = n_distinct(unique_identifier_nona),
.groups = "drop"
)
plot_df |>
mutate(
week = lubridate::round_date(date, "week")
) |>
group_by(week, interior) |>
summarize(
n = mean(n),
.groups = "drop"
) |>
filter(week >= as.Date("2024-07-01"), week < as.Date("2026-03-08")) |>
ggplot(aes(week, n, color = interior)) +
geom_vline(
color = "grey30",
xintercept = as.Date("2025-01-20"),
lty = "dashed"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-07"),
lty = "dotted"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-24"),
lty = "dotted"
) +
geom_line(size = 0.8) +
annotate(
color = "grey30",
"text",
x = as.Date("2025-01-10"),
y = Inf,
label = "Biden",
hjust = 1,
vjust = 1.5,
size = 4
) +
annotate(
color = "grey30",
"text",
x = as.Date("2025-01-30"),
y = Inf,
label = "Trump II",
hjust = 0,
vjust = 1.5,
size = 4
) +
scale_y_continuous(
breaks = c(0, 10000, 20000, 30000, 40000, 50000),
labels = scales::label_comma(),
limits = c(0, NA)
) +
scale_x_date(
breaks = function(x) {
seq(
floor_date(min(x, na.rm = TRUE), "quarter"),
ceiling_date(max(x, na.rm = TRUE), "quarter"),
by = "quarter"
)
},
labels = label_months
) +
scale_color_manual(values = cb_palette, name = NULL) +
labs(
x = "Week of detentions",
y = "Average overnight detainees\nby week"
) +
theme_minimal_transparent()
# rm(plot_df)
```
Finally, the administration did not just increase the number of people in detention, it also made it harder for them to be released. Before this administration, ICE often released people from detention who were reasonably likely to win their cases, and if ICE did not release them itself, an immigration judge often granted them release on bond. Both decisions were in the hands of the Executive Branch.^[Immigration judges, unlike federal district court judges, are administrative judges who are employed by the Department of Justice and are bound by guidance from the Attorney General and the Board of Immigration Appeals, also a part of the Department of Justice.]
ICE now releases virtually no one before a final deportation decision, and the administration has also made it far more difficult for immigration judges to release people by granting bond. On July 8, ICE [issued](https://www.aila.org/library/ice-memo-interim-guidance-regarding-detention-authority-for-applications-for-admission) guidance suggesting that anyone who had entered the country without inspection (i.e. between ports of entry) was ineligible for bond, regardless of how long they had lived in the United States. On September 5, the Board of Immigration Appeals [reached](https://www.justice.gov/eoir/media/1413311/dl?inline) the same conclusion in a decision that bound immigration judges. A flood of habeas corpus petitions -- cases filed in federal district court to seek noncitizens’ release from unlawful detention -- followed, and many succeeded.However, the habeas releases represent a small number on the scale of current detention.[^13] Despite the [hundreds](https://www.politico.com/news/2026/01/05/trump-administration-immigrants-mandatory-detention-00709494) of judicial opinions finding the policy illegal in these habeas cases, ICE and immigration courts continue to apply it.
As fewer people have been released from detention, more have been swiftly deported. The chance of release within 60 days of book-in has gone down from 16% to 5% (a relative decrease of 68%), and the chance of deportation within 60 days has increased from 56% to 63% (a relative increase of over 12%).[^14] But these numbers understate the degree of the change, because at the same times as releases declined, the percentage of people in detention who would have been released under previous administrations increased.
```{r}
#| label: release-reasons-calc
#| include: false
#| eval: false
plot_df <-
detentions_with_arrests |>
filter(has_arrest == TRUE, border_arrest == FALSE) |>
filter(
!(stay_release_reason %in% c("Died", "Escaped", "Transferred")),
is.na(stay_release_reason) |
!str_detect(stay_release_reason, "Marshal|ORR|Disposition|Withdraw")
) |>
mutate(
week = lubridate::round_date(stay_book_in_date_time, "week"),
stay_duration = as.numeric(
as.Date(stay_book_out_date_time) -
as.Date(stay_book_in_date_time)
),
any_departed_within_60 = !is.na(departed_date_removal) &
as.numeric(
as.Date(departed_date_removal) - as.Date(stay_book_in_date_time)
) >=
0 &
as.numeric(
as.Date(departed_date_removal) - as.Date(stay_book_in_date_time)
) <=
60,
class = case_when(
(stay_release_reason %in%
c("Voluntary Return", "Voluntary departure", "Removed") |
any_departed_within_60) &
stay_duration < 60 ~ "Removed within\n60 days",
stay_release_reason %in%
c(
"Bonded Out - Field Office",
"Order of Recognizance - Humanitarian",
"Order of Supervision - Humanitarian",
"Order of Supervision - No SLRRFF",
"Order of Supervision - Re-Release",
"Order of recognizance",
"Order of supervision",
"Paroled",
"Paroled - Fear Found",
"Paroled - Humanitarian",
"Paroled - Public Benefit",
"Bonded Out - IJ",
"Proceedings Terminated",
"Relief Granted by IJ"
) &
stay_duration < 60 ~ "Released within\n60 days",
is.na(stay_release_reason) |
stay_duration >= 60 ~ "Not released within\n60 days"
),
class = factor(
class,
levels = c(
"Not released within\n60 days",
"Released within\n60 days",
"Removed within\n60 days"
)
)
) |>
filter(
!is.na(class)
) |>
filter(
stay_book_in_date_time >= as.Date("2023-10-01"),
stay_book_in_date_time <= as.Date("2026-03-10") - 60
)
ratio_df <-
plot_df |>
mutate(
time_period = case_when(
as.Date(stay_book_in_date_time) >= as.Date("2024-05-01") &
as.Date(stay_book_in_date_time) <= as.Date("2024-10-31") ~ "before",
as.Date(stay_book_in_date_time) >= as.Date("2025-11-01") &
as.Date(stay_book_in_date_time) <= as.Date("2025-11-30") ~ "current"
)
) |>
filter(!is.na(time_period)) |>
group_by(class, time_period) |>
summarize(total = n(), .groups = "drop") |>
mutate(prop = total / sum(total), .by = time_period) |>
arrange(time_period, class) |>
select(-total) |>
pivot_wider(names_from = time_period, values_from = prop) |>
group_by(class) |>
summarize(current / before)
plot_df |>
count(week, class) |>
filter(week >= as.Date("2024-07-01"), week < as.Date("2026-03-08")) |>
mutate(prop = n / sum(n), .by = week) |>
ggplot(aes(week, prop, fill = class)) +
geom_area(position = "stack", alpha = 0.9) +
geom_vline(
xintercept = as.Date("2025-01-20"),
lty = "dashed",
col = "white"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-07"),
lty = "dotted"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-24"),
lty = "dotted"
) +
annotate(
"text",
x = as.Date("2025-01-10"),
y = 0.95,
label = "Biden",
hjust = 1,
size = 4,
col = "white"
) +
annotate(
"text",
x = as.Date("2025-01-30"),
y = 0.95,
label = "Trump II",
hjust = 0,
size = 4,
col = "white"
) +
scale_x_date(
breaks = function(x) {
seq(
floor_date(min(x, na.rm = TRUE), "quarter"),
ceiling_date(max(x, na.rm = TRUE), "quarter"),
by = "quarter"
)
},
labels = label_months
) +
scale_y_continuous(labels = scales::label_percent()) +
scale_fill_manual(values = cb_palette, name = NULL) +
labs(
x = "Week of book-in to detention",
y = "Percent of detainees\nbooked into detention"
) +
theme_minimal_transparent()
```
The decline in the release rate was therefore even more dramatic for the people who most often were released in the past as illustrated in @fig-release-reasons: those who were not convicted of any crime and who lacked a preexisting final order of removal. The release rate for this group fell from 35% to 7%, a reduction of more than three quarters, and the removal rate within 60 days doubled (from 26% to 52%). In the past, bond acted as a release valve in the system, impeding deportation cases against people -- like those with no convictions -- who were released and who often won the right to stay in the United States. Bond no longer plays that role.
```{r}
#| label: fig-release-reasons
#| fig-cap: "Chance of Release Among Those with No Criminal Convictions"
plot_df <-
detentions_with_arrests |>
filter(has_arrest == TRUE, border_arrest == FALSE) |>
mutate(
msc_code = as.character(most_serious_conviction_code),
# keep only pure 4-digit numeric NCIC-style codes for the UCR logic
msc4 = if_else(str_detect(msc_code, "^[0-9]{4}$"), msc_code, NA_character_),
# Homicide (09xx) EXCLUDING negligent manslaughter (0909, 0910)
ucr_violent = (str_detect(msc4, "^09") & !msc4 %in% c("0909", "0910")) |
# Rape / Sexual Assault (11xx) EXCLUDING statutory rape - no force (1116)
(str_detect(msc4, "^11") & msc4 != "1116") |
# Robbery (12xx)
str_detect(msc4, "^12") |
# Aggravated assault ONLY: 1301–1312 plus 1314–1315
msc4 %in%
c(
sprintf("13%02d", 1:12),
"1314",
"1315"
),
conviction = case_when(
ucr_violent ~ "Violent crime",
!is.na(most_serious_conviction_code) ~ "Nonviolent crime",
TRUE ~ "None"
)
) |>
filter(
conviction == "None",
is.na(final_order_date) |
as.Date(stay_book_in_date_time) < as.Date(final_order_date)
) |>
filter(
!(stay_release_reason %in% c("Died", "Escaped", "Transferred")),
is.na(stay_release_reason) |
!str_detect(stay_release_reason, "Marshal|ORR|Disposition|Withdraw")
) |>
mutate(
week = lubridate::round_date(stay_book_in_date_time, "week"),
stay_duration = as.numeric(
as.Date(stay_book_out_date_time) -
as.Date(stay_book_in_date_time)
),
any_departed_within_60 = !is.na(departed_date_removal) &
as.numeric(
as.Date(departed_date_removal) - as.Date(stay_book_in_date_time)
) >=
0 &
as.numeric(
as.Date(departed_date_removal) - as.Date(stay_book_in_date_time)
) <=
60,
class = case_when(
(stay_release_reason %in%
c("Voluntary Return", "Voluntary departure", "Removed") |
any_departed_within_60) &
stay_duration < 60 ~ "Removed within\n60 days",
stay_release_reason %in%
c(
"Bonded Out - Field Office",
"Order of Recognizance - Humanitarian",
"Order of Supervision - Humanitarian",
"Order of Supervision - No SLRRFF",
"Order of Supervision - Re-Release",
"Order of recognizance",
"Order of supervision",
"Paroled",
"Paroled - Fear Found",
"Paroled - Humanitarian",
"Paroled - Public Benefit",
"Bonded Out - IJ",
"Proceedings Terminated",
"Relief Granted by IJ"
) &
stay_duration < 60 ~ "Released within\n60 days",
is.na(stay_release_reason) |
stay_duration >= 60 ~ "Not released within\n60 days"
),
class = factor(
class,
levels = c(
"Not released within\n60 days",
"Released within\n60 days",
"Removed within\n60 days"
)
)
) |>
filter(
!is.na(class)
) |>
filter(
stay_book_in_date_time >= as.Date("2023-10-01"),
stay_book_in_date_time <= as.Date("2026-03-10") - 60
)
ratio_df <-
plot_df |>
mutate(
time_period = case_when(
as.Date(stay_book_in_date_time) >= as.Date("2024-05-01") &
as.Date(stay_book_in_date_time) <= as.Date("2024-10-31") ~ "before",
as.Date(stay_book_in_date_time) >= as.Date("2025-11-01") &
as.Date(stay_book_in_date_time) <= as.Date("2025-11-30") ~ "current"
)
) |>
filter(!is.na(time_period)) |>
group_by(class, time_period) |>
summarize(total = n(), .groups = "drop") |>
mutate(prop = total / sum(total), .by = time_period) |>
arrange(time_period, class) |>
select(-total) |>
pivot_wider(names_from = time_period, values_from = prop) |>
group_by(class) |>
summarize(current / before)
plot_df |>
count(week, class) |>
filter(week >= as.Date("2024-07-01"), week < as.Date("2026-03-08")) |>
mutate(prop = n / sum(n), .by = week) |>
ggplot(aes(week, prop, fill = class)) +
geom_area(position = "stack", alpha = 0.9) +
geom_vline(
xintercept = as.Date("2025-01-20"),
lty = "dashed",
col = "white"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-07"),
lty = "dotted"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-24"),
lty = "dotted"
) +
annotate(
"text",
x = as.Date("2025-01-10"),
y = 0.95,
label = "Biden",
hjust = 1,
size = 4,
col = "white"
) +
annotate(
"text",
x = as.Date("2025-01-30"),
y = 0.95,
label = "Trump II",
hjust = 0,
size = 4,
col = "white"
) +
scale_x_date(
breaks = function(x) {
seq(
floor_date(min(x, na.rm = TRUE), "quarter"),
ceiling_date(max(x, na.rm = TRUE), "quarter"),
by = "quarter"
)
},
labels = label_months
) +
scale_y_continuous(labels = scales::label_percent()) +
scale_fill_manual(values = cb_palette, name = NULL) +
labs(
x = "Week of book-in to detention",
y = "Percent of detainees\nbooked into detention"
) +
theme_minimal_transparent()
```
Now that bond is largely unavailable, more people appear to be giving up on their cases and accepting removal. One specific way to accept deportation from detention is to agree to so-called voluntary departure or voluntary return, which @fig-voluntary-departures shows increased by a factor of 28 in 2025. Voluntary departure and voluntary return allow a noncitizen to leave the country without being subject to a lengthy bar on applying for readmission. Not everyone who gives up on their case receives voluntary departure or voluntary return; many simply accept a removal order. That means that either voluntary departure or removal may follow from the pressure created by immigration detention. Of course, many other factors, including the [purge](https://www.npr.org/2026/01/10/nx-s1-5672386/the-trump-administration-fired-nearly-100-immigration-judges-in-2025-whats-next) of immigration judges, may also have contributed to these trends. There is no way to know from the data how many removal orders are the result of people giving up, but the combination of the increase in voluntary departures/returns and the decline in releases suggests that giving up to avoid more time in detention is an important part of the story.
```{r}
#| label: fig-voluntary-departures
#| fig-cap: "Voluntary Departures Increased by 28x"
plot_df <-
detentions_with_arrests |>
filter(has_arrest == TRUE, border_arrest == FALSE) |>
mutate(
week = lubridate::round_date(stay_book_out_date_time, "week"),
stay_duration = as.numeric(
difftime(
as.Date(stay_book_out_date_time),
as.Date(stay_book_in_date_time),
units = "days"
)
),
voluntary_departure = (str_detect(
case_status |> str_to_lower(),
"olunt|vd|vr"
) |
str_detect(case_category |> str_to_lower(), "olunt|vd|vr"))
) |>
filter(
voluntary_departure == TRUE
)
ratio_df <-
plot_df |>
mutate(
time_period = case_when(
as.Date(stay_book_out_date_time) >= as.Date("2024-07-01") &
as.Date(stay_book_out_date_time) <= as.Date("2024-12-31") ~ "before",
as.Date(stay_book_out_date_time) >= as.Date("2026-01-01") &
as.Date(stay_book_out_date_time) <= as.Date("2026-01-31") ~ "current"
),
month = case_when(
time_period == "before" ~ month(stay_book_out_date_time),
time_period == "current" ~ 1
)
) |>
filter(!is.na(time_period)) |>
group_by(time_period, month) |>
summarize(total = n()) |>
summarize(avg_monthly = mean(total)) |>
pivot_wider(names_from = time_period, values_from = avg_monthly) |>
summarize(current / before)
plot_df |>
count(week) |>
filter(week >= as.Date("2024-07-01"), week < as.Date("2026-03-08")) |>
ggplot(aes(week, n)) +
geom_line(size = 0.8) +
geom_vline(
color = "grey30",
xintercept = as.Date("2025-01-20"),
lty = "dashed"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-07"),
lty = "dotted"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-24"),
lty = "dotted"
) +
annotate(
color = "grey30",
"text",
x = as.Date("2025-01-10"),
y = Inf,
label = "Biden",
hjust = 1,
vjust = 1.5,
size = 4
) +
annotate(
color = "grey30",
"text",
x = as.Date("2025-01-30"),
y = Inf,
label = "Trump II",
hjust = 0,
vjust = 1.5,
size = 4
) +
scale_y_continuous(labels = scales::label_comma()) +
scale_x_date(
breaks = function(x) {
seq(
floor_date(min(x, na.rm = TRUE), "quarter"),
ceiling_date(max(x, na.rm = TRUE), "quarter"),
by = "quarter"
)
},
labels = label_months
) +
scale_color_manual(
values = c(
`TRUE` = "#D55E00",
`FALSE` = "#0072B2"
),
labels = c(
`TRUE` = "Violent conviction",
`FALSE` = "No violent conviction"
),
name = NULL # removes legend title
) +
labs(
x = "Date of book-out from detention",
y = "Weekly voluntary departures"
) +
theme_minimal_transparent()
```
## A Recent, Small Decline in Enforcement
After the murders of Renée Good and Alex Pretti, the Trump administration partially reversed course, first changing its leadership of the enforcement surge in Minneapolis and then winding that surge down (see @fig-arrests-city). Enforcement nationwide fell meaningfully at the same time, with street arrests declining to roughly their September 2025 levels (see @fig-arrests). This decline in arrests did not translate into a decline in removals before March 10, the last day in this dataset, but it may have prevented removals from rising even more. It is unclear whether this decline in enforcement will persist; appropriations from the One Big Beautiful Bill will allow ICE to continue to hire more officers and expand detention capacity.
## Conclusion
Focusing on immigration enforcement *within* the United States reveals the extent of the Trump administration’s crackdown: deportations following ICE arrests quintupled. ICE expanded street arrests so swiftly that they seem like an entirely new phenomenon and began arresting vastly more people who had no criminal convictions. ICE detained more people by expanding detention capacity and taking advantage of a decline in arrivals at the border to make more use of existing detention capacity for people arrested in the interior of the United States. Detention more often led to removal because ICE largely eliminated the possibility of release before a final decision. Finally, it is hard to know how to interpret the recent and relatively small decline in arrests.
## Methodology {.appendix}
Every step we took, beginning with the raw data, is documented in the code that we have posted. This section offers a less technical description of our approach.
To identify a set of deportations following ICE arrest and detention, we use two datasets from our latest (March 10, 2026) ICE data release.
We start with the arrests data and remove duplicates as well as rows that lack the unique identifiers that allow matching of the arrests and the detentions data.
We then join the arrests data with the detention stays data, treating any arrest that occurred up to ten days before a book-in to detention, as well as any arrest that took place less than five days after a book-in to detention, as linked to that book-in to detention. We adopt this approach because we believe that in some cases, arrest and/or book-in dates may be approximate, and we do not want to incorrectly exclude observations.
We do not include any detention book-ins that lack a corresponding arrest record. We recognize that this choice may lead us to exclude some interior detentions that started, for example, with an arrest by Homeland Security Investigations (our arrest data only includes arrests by ICE Enforcement and Removal Operations) or simply where the arrest was not recorded. We make this choice in order to build a comparable set of detentions over time, for which we think it is unlikely that the detentions started at the border. We believe these arrests include those carried out by ICE itself and those by CBP and other federal law enforcement officers in the major operations in LA, Washington, D.C., Chicago, and Minneapolis.
We count deportations using the stay release reason in the detentions data, rather than using the removals table. We do this for two reasons. First, the latest removals dataset that we have published only covers the period through July 28, 2025. But second, since we are focused on individuals who were arrested and detained by ICE, we believe that the stay release reason offers a good measure of the number of removals.
To identify custodial and noncustodial arrests, we use the apprehension method field in the arrests table, and we count arrests as custodial if they are categorized as “CAP Federal Incarceration,” “CAP State Incarceration,” “CAP Local Incarceration,” “Criminal Alien Program,” and “Custodial Arrest,” and as noncustodial or street arrests if they are categorized as “Non-Custodial Arrest,” “Located,” “Worksite Enforcement,” and “Probation and Parole.”
To classify convictions as violent or nonviolent, we use the most serious criminal charge field in the detentions table, and we cross-reference the named conviction with the FBI [list of violent offenses](https://cde.ucr.cjis.gov/LATEST/webapp), which include "murder and nonnegligent manslaughter, rape, robbery, and aggravated assault." Where no charge of any kind is listed, we treat the individual as having no conviction.
To identify voluntary departures and returns, we use a combination of the case category, case status, and stay release reason fields; if any of those fields indicates a voluntary departure or return, we categorize the case as a voluntary departure or return.
To identify newly-built or reopened large facilities, we examined both detention population figures and public reporting on the construction and reopening of large facilities. The list we use includes the following facilities: California City Corrections (CA), Camp East Montana (TX), Cimarron (OK), Delaney Hall (NJ), Diamondback (OK), Dilley (TX), El Paso soft-sided facility (TX), Florida soft-sided facility (FL), Folkston Annex (GA), Guantánamo Bay (Cuba), Irwin County (GA), Baker County (FL), Miami Speedway (IN), Nebraska/Cornhusker (NE), North Lake (MI), and Angola/Camp 57 (LA).
To estimate how frequently detention results in release and removal, we look only at each individual's first sixty days of detention, and we ignore the last sixty days of book-ins so that we consider a consistent window over time and avoid truncation bias.
\setlength{\parindent}{0em}
## Acknowledgments {.appendix}
We thank Dorothy Kronick, Elora Mukherjee, Phil Neff, and Amber Qureshi for input and comments and Elora Mukherjee for collaboration in obtaining the data.
## Disclosures {.appendix}
Blair consults for immigrants' rights organizations as an expert, including on litigation challenging ICE arrest and detention policies. Hausman worked for the American Civil Liberties Union Immigrants' Rights Project as an attorney from 2016 to 2019 and briefly as a volunteer attorney in early 2025. He continues to consult occasionally for the ACLU and other immigrants' rights organizations, including on litigation challenging the no-release policy described in this report. He is also a member of the academic advisory board for the Acacia Center.
## About the authors {.appendix}
[Graeme Blair](https://graemeblair.com) is a professor of political science at UCLA. He studies state violence and how to make social science more credible, ethical, and useful. His book, *Research Design in the Social Sciences* was published by Princeton University Press, and his book *Crime, insecurity, and community policing* was published by Cambridge University Press. He received a Ph.D. in politics from Princeton University and a B.A. in political science from Reed College. He received the Leamer-Rosenthal Prize in Open Social Science.
[David Hausman](https://david-hausman.com) is an assistant professor of law at Berkeley. He studies immigration enforcement, drawing on a background in quantitative methods and litigation experience at the ACLU. He has conducted data analysis as a consulting expert in numerous immigration cases. He has also published [scholarly](https://www.david-hausman.com/_files/ugd/a3c925_b5f5dcc8168644d4ab942c5264138230.pdf) [research](https://academic.oup.com/jleo/advance-article-abstract/doi/10.1093/jleo/ewac012/6767771?redirectedFrom=fulltext) [using](https://scholarship.law.upenn.edu/cgi/viewcontent.cgi?article=9525&context=penn_law_review) [immigration](https://www.pnas.org/content/early/2020/10/13/2014673117) enforcement datasets. He received his J.D. and Ph.D. in political science from Stanford University and clerked for Judge Stephen Williams on the D.C. Circuit Court of Appeals.
[^1]: Many of the findings here are mirrored in journalists' accounts. We have posted an incomplete list of them at [https://deportationdata.org/news.html](https://deportationdata.org/news.html).
[^2]: ICE refers to these transfers from jails and prisons as arrests.
[^3]: A recent [report](https://www.americanimmigrationcouncil.org/wp-content/uploads/2026/01/immigration-detention-report.pdf) from the American Immigration Council documents similar trends with different emphases (and often more detail).
[^5]: ICE and Customs and Border Protection (CBP) both produce datasets on removals, but they likely overlap to some degree, and since they are not connected, we have not developed a reliable way to estimate the size of the overlap. Austin Kocher has offered a detailed [discussion](https://austinkocher.substack.com/p/dhs-claims-603000-deportations-but) of these issues.
[^6]: To be more precise, we include only individuals who had an ICE arrest around the date of their book-in to ICE detention, and who were booked out of ICE detention for removal. We do not use ICE’s removals dataset both because we have doubts about its reliability and because we believe this is a meaningful measure of interior deportations over time.
[^7]: We include removals, voluntary departures, and (a small number of) voluntary returns as deportations here. We consider voluntary departures separately below.
[^8]: This does **not** mean that the annual total number of deportations in 2025 is five times larger than in 2024\. Instead, we are comparing January 2026 to the six months in the second half of 2024\.
[^9]: We focus on arrests that actually resulted in detention; it is possible for ICE to arrest a person and then decide not to book that person into detention.
[^10]: We exclude a small number of arrests that we could not categorize as custodial or street arrests.
[^11]: The memo laying out these enforcement priorities is available [here](https://www.ice.gov/doclib/news/guidelines-civilimmigrationlaw.pdf).
[^12]: ICE Detention Management spreadsheets.
[^13]: According to one [count](https://www.politico.com/news/2026/01/05/trump-administration-immigrants-mandatory-detention-00709494), there are now more than 100 lawsuits filed daily–but there are close to 1000 ICE arrests per day.
[^14]: A small number of these releases are actually victories in immigration court while detained, but that number does not contribute meaningfully to the trend.
::: {.content-visible when-format="pdf"}
\clearpage
## Appendix
\setcounter{figure}{0}
\renewcommand{\thefigure}{A}
```{r}
#| fig-cap: "Border Patrol Arrests"
#| label: fig-bp-arrests-pdf
#| fig-width: 5.5
#| fig-height: 3
bp_df <-
tribble(
~fiscal_year , ~OCT , ~NOV , ~DEC , ~JAN , ~FEB , ~MAR , ~APR , ~MAY , ~JUN , ~JUL , ~AUG , ~SEP ,
"2026" , 9846 , 9213 , NA , NA , NA , NA , NA , NA , NA , NA , NA , NA ,
"2025" , 58115 , 47534 , 48137 , 30127 , 9241 , 8193 , 10007 , 10359 , 8014 , 6168 , 8085 , 10203 ,
"2024" , 190454 , 192357 , 251178 , 125440 , 142104 , 139124 , 131078 , 121646 , 87606 , 59655 , 60684 , 55993 ,
"2023" , 206902 , 209496 , 224017 , 131720 , 131547 , 165068 , 185149 , 172423 , 100606 , 134064 , 182377 , 220323 ,
) |>
pivot_longer(-fiscal_year, names_to = "month", values_to = "bp_arrests") |>
filter(!is.na(bp_arrests)) |>
mutate(
fiscal_year = as.numeric(fiscal_year),
year = if_else(
month %in% c("OCT", "NOV", "DEC"),
fiscal_year - 1,
fiscal_year
)
) |>
mutate(date = ymd(str_c(year, "-", month, "-1")))
bp_df |>
filter(year >= 2023) |>
ggplot(aes(date, bp_arrests)) +
geom_vline(
color = "grey30",
xintercept = as.Date("2025-01-20"),
lty = "dashed"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-07"),
lty = "dotted"
) +
geom_vline(
color = "#CC9999",
xintercept = as.Date("2026-01-24"),
lty = "dotted"
) +
geom_line(size = 1) +
annotate(
color = "grey30",
"text",
x = as.Date("2025-01-10"),
y = Inf,
label = "Biden",
hjust = 1,
vjust = 1.5,
size = 4
) +
annotate(
color = "grey30",
"text",
x = as.Date("2025-01-30"),
y = Inf,
label = "Trump II",
hjust = 0,
vjust = 1.5,
size = 4
) +
scale_y_continuous(labels = scales::label_comma(), limits = c(0, NA)) +
scale_x_date(
breaks = function(x) {
seq(
floor_date(min(x, na.rm = TRUE), "quarter"),
ceiling_date(max(x, na.rm = TRUE), "quarter"),
by = "quarter"
)
},
labels = label_months
) +
scale_x_date() +
scale_color_manual(values = cb_palette, name = NULL) +
labs(x = "Date of arrest", y = "Monthly arrests") +
theme_minimal_transparent()
```
\setcounter{table}{0}
\renewcommand{\thetable}{A}
```{r}
#| label: tbl-facility-persondays-pdf
#| tbl-cap: "Total Person-Days in Detention by Facility, Jan. 31, 2025 -- Jan. 31, 2026 (Interior Arrests Only)"
new_facilities_total <- facility_persondays_df |>
filter(facility_label != "All other facilities") |>
summarize(person_days = sum(person_days)) |>
mutate(facility_label = "Total (new/reopened facilities)")
all_other <- facility_persondays_df |>
filter(facility_label == "All other facilities")
tbl_df <- facility_persondays_df |>
filter(facility_label != "All other facilities") |>
bind_rows(new_facilities_total) |>
bind_rows(all_other) |>
mutate(person_days = scales::label_comma()(person_days))
summary_rows <- which(
tbl_df$facility_label %in%
c("Total (new/reopened facilities)", "All other facilities")
)
kableExtra::kbl(
tbl_df,
col.names = c("Facility", "Person-Days"),
align = c("l", "r"),
booktabs = TRUE
) |>
kableExtra::kable_styling(
latex_options = c("hold_position")
) |>
kableExtra::row_spec(summary_rows, bold = TRUE) |>
kableExtra::row_spec(summary_rows[1] - 1, hline_after = TRUE)
```
:::