In preparation for the final project in Science of Communication, I pulled data from Media Cloud to compare the pervasiveness of coverage focused on two of the hottest topics in American media — immigration in the United States versus the prevalence of stories following the investigative records pertaining to convicted pedophile Jeffrey Epstein, more commonly referred to as “the Epstein files.”
Agenda-setting theory purports that media coverage frequency shapes issue salience. The more attention a topic receives, the more audiences deem it important, supporting the theory that the media heavily influences what we focus on and how we interpret those issues.
This theory applies to this analysis because it focuses on the amount of news coverage given to two different issues that are often spotlighted by the media. By counting stories about immigration and the Epstein files, this analysis examines which topic receives more attention (as documented by Media Cloud data).
If one topic receives substantially more coverage than the other, that could suggest that it has a more prominent place than the other. However, the number of stories does not necessarily show what the public thinks about an issue.
There are, of course, many other media theories that could be used to examine the psyche of the American people as the media outlets grapple for attention. The results could be an example of priming theory — exposure to a topic can temporarily shape how we feel and act. Elaboration Likelihood Model could explain how media may flood the news with simple messaging (peripheral route) instead of prompting people to plunge deeper into an issue in order to form an opinion (central route).
I predict that immigration will receive more news coverage than the Epstein files during the period studied. I also expect that coverage of both topics will change from week to week as news events and other factors influence media attention. (As of Oct. 24, we are in the throes of election coverage, which could skew the results at this point.)
The independent variable is the news topic, which has two categories: immigration and the Epstein files. The dependent variable is the number of online news stories returned for each topic. The analysis also considers how the number of stories changes over time.
Data has been collected using Media Cloud between Jan. 1 through Oct. 17. 2026. Search terms were created for each topic:
• Immigration, deportation, asylum, border security, border patrol, illegal immigration, immigration policy;
• Jeffrey Epstein, Epstein files, Epstein list, Epstein investigation
R Studio was used to organize the daily story counts by topic and group them into weeks. Summary statistics were calculated to show the total number of stories, the lowest and highest weekly counts, the median, mean and standard deviation for each topic. An interactive graph was also created to show changes in story counts over time. Results show the stories returned by these specific searches but does not compile a complete measure of all news coverage on each topic.
The initial results support the hypothesis that immigration received more coverage than the Epstein files during the period studied. The Media Cloud search returned 186,594 stories for immigration, compared with 30,554 stories for the Epstein files. In some weeks, the number of immigration-related stories were nearly 10 times more prevalent than the stories focused on a convicted pedophile.
The weekly summary statistics also show a major difference in coverage. Immigration had a weekly mean of approximately 4,551 stories, while the Epstein files had a weekly mean of 745 stories. The median weekly counts were 4,301 for immigration and 603 for the Epstein files.
The maximum weekly count for immigration was 9,663 stories, compared with 3,634 for the Epstein files. The minimum weekly counts were 3,183 and 150, respectively. These differences suggest that both topics experienced changes in coverage, but immigration consistently had higher weekly counts in the results.
The Plotly graph compares weekly story counts for immigration and the Epstein files from January through October 2026, providing an interactive visual comparison for each topic.
Here is the code I used to generate the data.
############################################################
# STEP 1: Install and Load Required Packages
############################################################
required_packages <- c(
"tidyverse",
"httr2",
"lubridate",
"plotly",
"knitr",
"kableExtra"
)
for (pkg in required_packages) {
if (!requireNamespace(pkg, quietly = TRUE)) {
install.packages(pkg)
}
}
library(tidyverse)
library(httr2)
library(lubridate)
library(plotly)
library(knitr)
library(kableExtra)
############################################################
# STEP 2: Verify Media Cloud API Key
############################################################
if (!nzchar(Sys.getenv("MEDIACLOUD_KEY"))) {
stop(
"No Media Cloud API key was found.\n",
"Run the setup section once,\n",
"then restart R and try again."
)
}
mc_key <- Sys.getenv("MEDIACLOUD_KEY")
############################################################
# STEP 3: Define the Date Range
############################################################
start_date <- "2026-01-04"
end_date <- "2026-10-17"
############################################################
# STEP 4: Define Search Queries
############################################################
immigration_query <-
'(
immigration OR immigrant* OR migrant* OR deportation OR
deport* OR asylum OR "border security" OR "border patrol" OR
"illegal immigration" OR "immigration policy"
)'
epstein_query <-
'(
"Jeffrey Epstein" OR "Epstein files" OR
"Epstein documents" OR "Epstein records" OR
"Epstein investigation" OR "Epstein list"
)'
############################################################
# STEP 5: Create Topic Definitions
############################################################
topics <- c(
"immigration" = immigration_query,
"Epstein files" = epstein_query
)
############################################################
# STEP 6: Create a Function to Retrieve Daily
# Story Counts
############################################################
mc_counts_over_time <- function(
query,
start_date,
end_date,
collection_id = 34412234
) {
resp <-
request(
"https://search.mediacloud.org/api/search/count-over-time"
) |>
req_headers(
Authorization = paste("Token", mc_key)
) |>
req_url_query(
q = query,
start = start_date,
end = end_date,
platform = "onlinenews-mediacloud",
cs = collection_id
) |>
req_perform()
results <- resp |>
resp_body_json()
map_dfr(
results$count_over_time$counts,
as_tibble
) |>
mutate(
date = as.Date(date)
)
}
############################################################
# STEP 7: Download Daily Counts for All Topics
############################################################
topic_data <-
imap_dfr(
topics,
function(search_string, topic_name) {
message(
"Downloading: ",
topic_name
)
results <-
mc_counts_over_time(
query = search_string,
start_date = start_date,
end_date = end_date
)
Sys.sleep(35)
results |>
mutate(
topic = topic_name
)
}
)
############################################################
# STEP 8: Examine the Downloaded Daily Data
############################################################
head(topic_data)
glimpse(topic_data)
############################################################
# STEP 9: Convert Daily Counts to Weekly Counts
############################################################
weekly_counts <-
topic_data |>
mutate(
week = floor_date(
date,
unit = "week"
)
) |>
group_by(
topic,
week
) |>
summarise(
stories = sum(count),
.groups = "drop"
)
############################################################
# STEP 10: Examine Weekly Counts
############################################################
weekly_counts
############################################################
# STEP 11: Summarize Coverage by Topic
############################################################
topic_summary <-
weekly_counts |>
group_by(topic) |>
summarise(
Total_Stories = sum(stories),
Weekly_Minimum = min(stories),
Weekly_Maximum = max(stories),
Weekly_Median = median(stories),
Weekly_Mean = round(
mean(stories),
2
),
Weekly_SD = round(
sd(stories),
2
),
.groups = "drop"
) |>
arrange(
desc(Total_Stories)
)
Table <-
topic_summary |>
kbl(
caption =
"Media Cloud Topic Summary Statistics",
align = "lrrrrrr"
) |>
kable_styling(
full_width = FALSE,
bootstrap_options = c(
"striped",
"hover",
"condensed"
)
)
Table
############################################################
# STEP 12: Create an Interactive Plotly Graph
############################################################
weekly_counts <- weekly_counts |>
mutate(
stories_formatted = formatC(
stories,
format = "d",
big.mark = ","
)
)
Graph <- plot_ly(
data = weekly_counts,
x = ~week,
y = ~stories,
color = ~topic,
colors = "Set1",
type = "scatter",
mode = "lines+markers",
customdata = ~topic,
text = ~stories_formatted,
hovertemplate =
paste(
"%{customdata}
",
"Week: %{x|%Y-%m-%d}
",
"Stories: %{text}
",
""
)
) |>
layout(
title =
"Weekly News Coverage: Immigration vs. Epstein Files (Jan. 1–Oct. 17, 2026)",
hovermode = "x unified",
xaxis = list(
title = "Week"
),
yaxis = list(
title = "Number of Stories",
separatethousands = TRUE
)
)
Graph