Code
############################################################
# STEP 1: Install and Load Packages
############################################################
if (!require("tidyverse")) install.packages("tidyverse")
if (!require("httr2")) install.packages("httr2")
if (!require("lubridate")) install.packages("lubridate")
if (!require("kableExtra")) install.packages("kableExtra")
if (!require("plotly")) install.packages("plotly")
library(tidyverse)
library(httr2)
library(lubridate)
library(kableExtra)
library(plotly)
############################################################
# STEP 2: Verify Media Cloud API Key
############################################################
api_key <- Sys.getenv("MEDIACLOUD_API_KEY")
if (!nzchar(api_key)) {
stop(
"No Media Cloud API key was found.\n",
"Please save your key to .Renviron first."
)
}
############################################################
# STEP 3: Define Two Topics
############################################################
topics <- c(
"Artificial Intelligence" = "\"artificial intelligence\"",
"Climate Change" = "\"climate change\""
)
############################################################
# STEP 4: Define the Date Range
############################################################
start_date <- "2026-01-01"
end_date <- "2026-09-01"
############################################################
# STEP 5: Create a Function to Download Daily Story Counts
############################################################
get_counts <- function(query) {
Sys.sleep(35)
response <-
request("https://search.mediacloud.org/api/search/count-over-time") |>
req_headers(
Authorization = paste("Token", api_key),
Accept = "application/json"
) |>
req_url_query(
q = query,
start = start_date,
end = end_date,
platform = "onlinenews-mediacloud",
cs = 34412234
) |>
req_perform()
results <- response |> resp_body_json()
map_dfr(
results$count_over_time$counts,
as_tibble
) |>
mutate(date = as.Date(date))
}
############################################################
# STEP 6: Download Data for Both Topics
############################################################
topic_data <-
imap_dfr(
topics,
function(search_query, topic_name) {
message("Downloading: ", topic_name)
get_counts(search_query) |>
mutate(topic = topic_name)
}
)
## Downloading: Artificial Intelligence
## Downloading: Climate Change
############################################################
# STEP 7: Examine the Downloaded Data
############################################################
head(topic_data)
## # A tibble: 6 × 5
## date total_count count ratio topic
## <date> <int> <int> <dbl> <chr>
## 1 2026-01-01 6212 73 0.0118 Artificial Intelligence
## 2 2026-01-02 8595 190 0.0221 Artificial Intelligence
## 3 2026-01-03 6425 76 0.0118 Artificial Intelligence
## 4 2026-01-04 6649 75 0.0113 Artificial Intelligence
## 5 2026-01-05 11656 254 0.0218 Artificial Intelligence
## 6 2026-01-06 12777 278 0.0218 Artificial Intelligence
glimpse(topic_data)
## Rows: 488
## Columns: 5
## $ date <date> 2026-01-01, 2026-01-02, 2026-01-03, 2026-01-04, 2026-01-0…
## $ total_count <int> 6212, 8595, 6425, 6649, 11656, 12777, 13479, 13684, 12525,…
## $ count <int> 73, 190, 76, 75, 254, 278, 254, 325, 304, 66, 69, 286, 296…
## $ ratio <dbl> 0.011751449, 0.022105876, 0.011828794, 0.011279892, 0.0217…
## $ topic <chr> "Artificial Intelligence", "Artificial Intelligence", "Art…
############################################################
# STEP 8: Calculate Weekly Story Counts
############################################################
weekly_counts <-
topic_data |>
mutate(
week = floor_date(date, unit = "week")
) |>
group_by(topic, week) |>
summarise(
stories = sum(count),
.groups = "drop"
)
############################################################
# STEP 9: Display Weekly Counts
############################################################
weekly_counts |>
arrange(topic, week) |>
kbl(
caption = "Weekly Media Cloud Story Counts"
) |>
kable_styling(
full_width = FALSE,
bootstrap_options = c("striped", "hover")
)
Weekly Media Cloud Story Counts
|
topic
|
week
|
stories
|
|
Artificial Intelligence
|
2025-12-28
|
339
|
|
Artificial Intelligence
|
2026-01-04
|
1556
|
|
Artificial Intelligence
|
2026-01-11
|
1601
|
|
Artificial Intelligence
|
2026-01-18
|
1366
|
|
Artificial Intelligence
|
2026-01-25
|
1687
|
|
Artificial Intelligence
|
2026-02-01
|
1617
|
|
Artificial Intelligence
|
2026-02-08
|
1587
|
|
Artificial Intelligence
|
2026-02-15
|
1295
|
|
Artificial Intelligence
|
2026-02-22
|
1966
|
|
Artificial Intelligence
|
2026-03-01
|
1498
|
|
Artificial Intelligence
|
2026-03-08
|
1308
|
|
Artificial Intelligence
|
2026-03-15
|
1583
|
|
Artificial Intelligence
|
2026-03-22
|
1478
|
|
Artificial Intelligence
|
2026-03-29
|
1246
|
|
Artificial Intelligence
|
2026-04-05
|
1411
|
|
Artificial Intelligence
|
2026-04-12
|
1582
|
|
Artificial Intelligence
|
2026-04-19
|
1901
|
|
Artificial Intelligence
|
2026-04-26
|
2171
|
|
Artificial Intelligence
|
2026-05-03
|
2043
|
|
Artificial Intelligence
|
2026-05-10
|
2342
|
|
Artificial Intelligence
|
2026-05-17
|
2213
|
|
Artificial Intelligence
|
2026-05-24
|
1781
|
|
Artificial Intelligence
|
2026-05-31
|
2323
|
|
Artificial Intelligence
|
2026-06-07
|
2140
|
|
Artificial Intelligence
|
2026-06-14
|
1773
|
|
Artificial Intelligence
|
2026-06-21
|
1715
|
|
Artificial Intelligence
|
2026-06-28
|
1393
|
|
Artificial Intelligence
|
2026-07-05
|
1306
|
|
Artificial Intelligence
|
2026-07-12
|
1481
|
|
Artificial Intelligence
|
2026-07-19
|
1389
|
|
Artificial Intelligence
|
2026-07-26
|
1491
|
|
Artificial Intelligence
|
2026-08-02
|
1481
|
|
Artificial Intelligence
|
2026-08-09
|
1258
|
|
Artificial Intelligence
|
2026-08-16
|
1392
|
|
Artificial Intelligence
|
2026-08-23
|
1371
|
|
Artificial Intelligence
|
2026-08-30
|
606
|
|
Climate Change
|
2025-12-28
|
170
|
|
Climate Change
|
2026-01-04
|
662
|
|
Climate Change
|
2026-01-11
|
824
|
|
Climate Change
|
2026-01-18
|
760
|
|
Climate Change
|
2026-01-25
|
815
|
|
Climate Change
|
2026-02-01
|
654
|
|
Climate Change
|
2026-02-08
|
1101
|
|
Climate Change
|
2026-02-15
|
857
|
|
Climate Change
|
2026-02-22
|
836
|
|
Climate Change
|
2026-03-01
|
750
|
|
Climate Change
|
2026-03-08
|
793
|
|
Climate Change
|
2026-03-15
|
921
|
|
Climate Change
|
2026-03-22
|
907
|
|
Climate Change
|
2026-03-29
|
849
|
|
Climate Change
|
2026-04-05
|
823
|
|
Climate Change
|
2026-04-12
|
915
|
|
Climate Change
|
2026-04-19
|
1038
|
|
Climate Change
|
2026-04-26
|
963
|
|
Climate Change
|
2026-05-03
|
1128
|
|
Climate Change
|
2026-05-10
|
817
|
|
Climate Change
|
2026-05-17
|
922
|
|
Climate Change
|
2026-05-24
|
745
|
|
Climate Change
|
2026-05-31
|
840
|
|
Climate Change
|
2026-06-07
|
755
|
|
Climate Change
|
2026-06-14
|
752
|
|
Climate Change
|
2026-06-21
|
968
|
|
Climate Change
|
2026-06-28
|
810
|
|
Climate Change
|
2026-07-05
|
770
|
|
Climate Change
|
2026-07-12
|
883
|
|
Climate Change
|
2026-07-19
|
875
|
|
Climate Change
|
2026-07-26
|
950
|
|
Climate Change
|
2026-08-02
|
834
|
|
Climate Change
|
2026-08-09
|
872
|
|
Climate Change
|
2026-08-16
|
722
|
|
Climate Change
|
2026-08-23
|
893
|
|
Climate Change
|
2026-08-30
|
466
|
############################################################
# STEP 10: Summarize Total Coverage
############################################################
topic_summary <-
weekly_counts |>
group_by(topic) |>
summarise(
Total_Stories = sum(stories),
Weekly_Minimum = min(stories),
Weekly_Maximum = max(stories),
Weekly_Mean = round(mean(stories), 2),
.groups = "drop"
) |>
arrange(desc(Total_Stories))
Summary_Table <-
topic_summary |>
kbl(
caption = "Coverage Summary by Topic"
) |>
kable_styling(
full_width = FALSE,
bootstrap_options = c("striped", "hover")
)
Summary_Table
Coverage Summary by Topic
|
topic
|
Total_Stories
|
Weekly_Minimum
|
Weekly_Maximum
|
Weekly_Mean
|
|
Artificial Intelligence
|
56690
|
339
|
2342
|
1574.72
|
|
Climate Change
|
29640
|
170
|
1128
|
823.33
|
############################################################
# STEP 11: Create a Line Graph
############################################################
Line_Graph <-
weekly_counts |>
ggplot(
aes(
x = week,
y = stories,
color = topic
)
) +
geom_line(linewidth = 1) +
geom_point(size = 2) +
labs(
title = "Weekly Media Coverage Volume",
subtitle = "Comparison of Two Topics",
x = "Week",
y = "Number of Stories",
color = "Topic"
) +
theme_minimal()
Line_Graph

############################################################
# STEP 12: Save Results as a CSV File
############################################################
write_csv(
weekly_counts,
"weekly_story_counts.csv"
)
############################################################
# STEP 13: Create an Interactive Plotly Line Graph
############################################################
Plotly_Line <-
weekly_counts |>
filter(
week >= as.Date(start_date),
week + 6 <= as.Date(end_date)
) |>
plot_ly(
x = ~week,
y = ~stories,
color = ~topic,
colors = "Set1",
type = "scatter",
mode = "lines+markers",
hovertemplate = paste0(
"<b>%{fullData.name}</b><br>",
"Stories: %{y:,}<br>",
"Week of: %{x|%b %d, %Y}",
"<extra></extra>"
)
) |>
layout(
title = "Weekly Media Coverage Volume",
hovermode = "x unified",
xaxis = list(
title = "Week"
),
yaxis = list(
title = "Number of Stories",
tickformat = ","
),
legend = list(
title = list(text = "Topic")
),
margin = list(t = 80)
)
Plotly_Line