Introduction

This interactive graphic shows the weekly volume of Media Cloud stories mentioning the topics “artificial intelligence” and “climate change” from January 4, 2026 through September 5, 2026. The data were collected using the Media Cloud API and then grouped by week to compare how much coverage each topic received over time. The graphic allows the two topics to be compared by hovering over individual weeks, which displays the topic, number of stories and week. Looking at the trends can show when coverage of either topic increased or decreased and how the two topics compared with each other throughout the period.

Interactive Plotly Graphic

Plotly_Line

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