The graph below from MediaCloud.org shows the weekly media coverage volume for stories covering artificial intelligence and climate change from the week of January 4th, 2026, to the week of September 5th, 2026. The highest weekly peak for climate change stories happened in the week of May 3rd, 2026, totaling 1,128 stories. The highest weekly peak for artificial intelligence stories happened on the week of May 10th, 2026, totaling 2,342 stories. Overall, the average number of monthly artificial intelligence stories, around 1,635, is almost double the number of monthly climate change stories, around 856.

Code

Below is the code used to conduct the report.

############################################################
# 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
############################################################

if (!nzchar(Sys.getenv("MEDIACLOUD_KEY"))) {
  stop(
    "No Media Cloud API key was found.\n",
    "Please save your key to .Renviron first."
  )
}

############################################################
# STEP 3: Define Two Topics
############################################################

# Replace the topic labels and search queries below.
#
# Examples:
# "artificial intelligence"
# "climate change"
# "immigration"
# "inflation"

topics <- c(
  "Artificial Intelligence" = "\"artificial intelligence\"",
  "Climate Change" = "\"climate change\""
)

############################################################
# STEP 4: Define the Date Range
############################################################

start_date <- "2026-01-04"
end_date <- "2026-09-05"

############################################################
# 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",
        Sys.getenv("MEDIACLOUD_KEY")
      )
    ) |>
    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
        )
    }
  )

############################################################
# STEP 7: Examine the Downloaded Data
############################################################

head(topic_data)
glimpse(topic_data)

############################################################
# 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"
    )
  )

############################################################
# 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

############################################################
# 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 11B: Create an Interactive Plotly Line Graph
############################################################

# Create custom hover text with comma-formatted counts

plotly_data <-
  weekly_counts |>
  mutate(
    hover_text = paste0(
      "<b>Topic:</b> ", topic,
      "<br><b>Stories:</b> ", format(
        stories,
        big.mark = ",",
        scientific = FALSE
      ),
      "<br><b>Week:</b> ", format(
        week,
        "%Y-%m-%d"
      )
    )
  )

# Set1 palette colors
set1_colors <- c(
  "#E41A1C",  # red
  "#377EB8"   # blue
)

Interactive_Line_Graph <-
  plot_ly()

for (i in seq_along(unique(plotly_data$topic))) {
  
  current_topic <- unique(plotly_data$topic)[i]
  
  topic_subset <-
    plotly_data |>
    filter(topic == current_topic)
  
  Interactive_Line_Graph <-
    Interactive_Line_Graph |>
    add_trace(
      data = topic_subset,
      x = ~week,
      y = ~stories,
      type = "scatter",
      mode = "lines+markers",
      name = current_topic,
      line = list(
        color = set1_colors[i],
        width = 2
      ),
      marker = list(
        color = set1_colors[i],
        size = 8
      ),
      text = ~hover_text,
      hovertemplate = "%{text}<extra></extra>"
    )
}

Interactive_Line_Graph <-
  Interactive_Line_Graph |>
  layout(
    title = list(
      text = "Weekly Media Coverage Volume"
    ),
    xaxis = list(
      title = "Week"
    ),
    yaxis = list(
      title = "Number of Stories",
      separatethousands = TRUE
    ),
    hovermode = "x unified",
    legend = list(
      title = list(
        text = "Topic"
      )
    )
  )

Interactive_Line_Graph

############################################################
# STEP 12: Save Results as a CSV File
############################################################

write_csv(
  weekly_counts,
  "weekly_story_counts.csv"
)