nchar(Sys.getenv(“NYT_API_KEY”))— title: “Week 9 Assignment: NYT Most Popular API” author: “Supriya P.” date: today format: html: toc: true code-fold: true theme: flatly execute: echo: true warning: false message: false —

Introduction

For this assignment, I used the New York Times Most Popular API to find out which sections produced the most viewed articles on NYTimes.com over the past 30 days.

The API

Endpoint: https://api.nytimes.com/svc/mostpopular/v2/viewed/{period}.json

The Most Popular API returns the most emailed, most shared, or most viewed articles on NYTimes.com. I used the most viewed endpoint, which takes one setting, the time period, and accepts 1, 7, or 30 days.

Request parameters:

  • period: 30 (the past 30 days)
  • api-key: my NYT API key, read from an environment variable

Making the Request

library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.2.1     ✔ readr     2.2.0
✔ forcats   1.0.1     ✔ stringr   1.6.0
✔ ggplot2   4.0.3     ✔ tibble    3.3.1
✔ lubridate 1.9.5     ✔ tidyr     1.3.2
✔ purrr     1.2.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(httr)
library(jsonlite)

Attaching package: 'jsonlite'

The following object is masked from 'package:purrr':

    flatten
library(knitr)

api_key <- Sys.getenv("NYT_API_KEY")

if (api_key == "") {
  stop("NYT_API_KEY is not set. Add it to your .Renviron file and restart R.")
}

url <- "https://api.nytimes.com/svc/mostpopular/v2/viewed/30.json"

response <- GET(url, query = list(`api-key` = api_key))

status_code(response)
[1] 200

My API key is stored in my .Renviron file and read with Sys.getenv(), so it never appears in the code or on this page. GET() sends the request to the NYT, and a status code of 200 means the request worked. I only printed the status code rather than the full response, since the full response would show the request URL, which includes the key.

Parsing the JSON

raw <- fromJSON(content(response, as = "text", encoding = "UTF-8"))

raw$num_results
[1] 20
glimpse(raw$results)
Rows: 20
Columns: 22
$ uri            <chr> "nyt://interactive/42c5882e-d566-5a02-9920-26c576df1c0e…
$ url            <chr> "https://www.nytimes.com/interactive/polls/latest-polls…
$ id             <dbl> 1e+14, 1e+14, 1e+14, 1e+14, 1e+14, 1e+14, 1e+14, 1e+14,…
$ asset_id       <dbl> 1e+14, 1e+14, 1e+14, 1e+14, 1e+14, 1e+14, 1e+14, 1e+14,…
$ source         <chr> "New York Times", "New York Times", "New York Times", "…
$ published_date <chr> "2026-09-10", "2026-10-15", "2026-10-09", "2026-10-28",…
$ updated        <chr> "2026-11-01 20:47:06", "2026-10-18 23:46:24", "2026-10-…
$ section        <chr> "Polls", "U.S.", "Movies", "U.S.", "U.S.", "Opinion", "…
$ subsection     <chr> "", "Politics", "", "Politics", "", "", "", "Politics",…
$ nytdsection    <chr> "polls", "u.s.", "movies", "u.s.", "u.s.", "opinion", "…
$ adx_keywords   <chr> "Polls and Public Opinion;Midterm Elections (2026);inte…
$ column         <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ byline         <chr> "", "By Tyler Pager and Anatoly Kurmanaev", "By Coralie…
$ type           <chr> "Interactive", "Article", "Article", "Article", "Articl…
$ title          <chr> "Latest Polls", "Behind a White House Feud That Is Bitt…
$ abstract       <chr> "Track all the polls from today, including the latest d…
$ des_facet      <list> <"Polls and Public Opinion", "Midterm Elections (2026)…
$ org_facet      <list> <>, "Kennedy, John F, Center for the Performing Arts",…
$ per_facet      <list> <>, <"Grenell, Richard", "Habba, Alina", "Mildon, Turh…
$ geo_facet      <list> <>, "Venezuela", <>, "Alaska", "Erie (Pa)", <>, "Los A…
$ media          <list> [<data.frame[1 x 6]>], [<data.frame[1 x 6]>], [<data.f…
$ eta_id         <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…

content() pulls the body of the response out as text, and fromJSON() turns the JSON into an R list. The articles are stored under results, which fromJSON() converts into a data frame with one row per article. Several columns are nested, meaning each cell holds a list instead of a single value. These include the subject tags (des_facet, org_facet, per_facet, geo_facet) and media, which holds information about each article’s images.

Creating a Tidy Data Frame

articles <- raw$results %>%
  as_tibble() %>%
  select(title, section, published_date) %>%
  mutate(
    section = na_if(section, ""),
    published_date = as_date(published_date)
  )

kable(articles, col.names = c("Title", "Section", "Published"))
Title Section Published
Latest Polls Polls 2026-09-10
Behind a White House Feud That Is Bitter Even by Trump-Era Standards U.S. 2026-10-15
She Was Offered $40 Million for Silence on Elon Musk. She Spoke Out Instead. Movies 2026-10-09
Inside Democrats’ Secret Effort to Stack Alaska’s Ballot U.S. 2026-10-28
‘It Was Horrific’: 8 Killed in Shooting in Erie, Pa. U.S. 2026-10-10
In Six Decades of Public Life, I Never Thought I Would Write This Piece Opinion 2026-10-21
O.J. Simpson, a Secret Informant and a Decades-Old Mystery U.S. 2026-10-18
Trump’s New Press Secretary: Not-Quite Attorney at Law U.S. 2026-10-14
Trump’s Bubble Is About to Burst Opinion 2026-10-17
Obama Issues Searing Attacks on Trump and Ramaswamy in Return to Campaign Trail U.S. 2026-10-24
Trump Erupted After Learning of Alina Habba’s $50 Million Deal U.S. 2026-10-29
They Argued About Trump at a Dog Park. Then They Pulled Their Guns. U.S. 2026-10-05
I Teach at a Business School. It’s Time to Shut Them Down. Opinion 2026-10-18
It Can Keep Cancer in Remission. Why Is No One Talking About It? Well 2026-10-06
How Cornell Punished Each of the 7 Men Accused of Sexual Assault New York 2026-10-03
Patrick Stewart, Master of Shakespeare, ‘Star Trek’ and More, Has Died Arts 2026-10-29
This Photo Is an A.I. Fake. Can You Tell? Technology 2026-10-08
Obama’s ‘Cheesecake Factory’ Jab Rankles Trump on Mississippi Campaign Stop U.S. 2026-10-26
‘He’s Not the Same Baby’: Family of Child Found Alive in Morgue Recalls Ordeal U.S. 2026-10-24
Katie Zacharia Is Ousted After 5 Days as White House Press Secretary U.S. 2026-10-16
articles %>%
  summarize(across(everything(), ~ sum(is.na(.x)))) %>%
  kable(caption = "Missing values per column")
Missing values per column
title section published_date
0 0 0

Data Cleaning Decisions

  • Nested fields: I dropped the subject tags and the image data, since they aren’t needed to count articles by section. Keeping only the title, section, and publication date gives one simple row per article.
  • Blank values: The API uses an empty string instead of a missing value when a field is blank, so I converted any blank sections to NA before counting.
  • Dates: The publication date comes in as text, so I converted it to a date.

Analysis

section_counts <- articles %>%
  count(section, sort = TRUE, name = "articles")

kable(section_counts, col.names = c("Section", "Number of Articles"))
Section Number of Articles
U.S. 11
Opinion 3
Arts 1
Movies 1
New York 1
Polls 1
Technology 1
Well 1
ggplot(section_counts, aes(x = reorder(section, articles), y = articles)) +
  geom_col(fill = "steelblue") +
  coord_flip() +
  labs(
    title = "Most Viewed NYT Articles by Section, Past 30 Days",
    x = NULL, y = "Number of Articles"
  ) +
  theme_minimal()

[Write 2 or 3 sentences here after rendering: which section had the most articles on the list, how many, and anything that surprised you. For example, whether one section dominated or the list was spread across many sections.]

Conclusions

The Most Popular API made it easy to get a ranked list of articles in a single request, and the main cleaning step was pulling a few flat columns out of a response with several nested fields. [Add one sentence summarizing your main finding.]

Since the list only includes about 20 articles from one 30 day window, the results are a snapshot of what Times readers were viewing this month rather than a long term pattern. A next step would be to run the same request every month to see whether the leading sections change over time.

AI Citation

Anthropic. (2026). Claude Opus 5.5 [Large language model]. https://claude.ai. Accessed October 2026.