NY Times API

Author

Tenzin Lhamo Thakuri

Task

Assignment: Using a New York Times API in R

The New York Times provides multiple public APIs via its developer portal (NYT Developer Network). To use any NYT API, you must first create an account and obtain an API key.

Your task:

Choose one New York Times API endpoint (e.g., Article Search, Most Popular, Books, etc.). You should use your data analysis to ask and attempt to answer an interesting question of your choosing. Examples: “What are the top five best selling hard cover books?”, “Which newspaper sections have produced the most popular articles, and how has this changed compared to X years ago?”, etc. In R, write code to: Authenticate using your API key. Never hard code an API key. There are a number of good ways to do this. For example: Store it in an environment variable (e.g., Sys.getenv(“NYT_API_KEY”)). Make a request to the endpoint Parse the JSON response Transform the result into a clean R data frame (tibble is fine) Deliverable

A single Quarto (.qmd) document that includes: The API you selected (endpoint + brief description) The request you made (parameters used) Code that returns a tidy data frame A short note describing any data-cleaning decisions (e.g., nested fields, missing values)

Approach

I’m going to use the following api endpoint to get the most popular email articles on NYTimes.com for the period of 30 days.

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

Step1: Store api_key in .Renviron file and use Sys.getenv to get api_key for security reason. step2: API request to the endpoint by sending api-key in the query parameter. step 3:Parse the JSON response step4:Transform response JSON data into tibble Step5:Clean dataset step6: I’ll try to answer What topics appear most often among articles? from the dataset.

Anticipated challenges

Converting json data to data frame in R Aalyzing article topic to answer What topics appear most often among emailed articles?

Codebase

library(httr2)
library(rlang)
library(dplyr)

Attaching package: 'dplyr'
The following objects are masked from 'package:stats':

    filter, lag
The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union
library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ forcats   1.0.1     ✔ readr     2.2.0
✔ ggplot2   4.0.3     ✔ stringr   1.6.0
✔ lubridate 1.9.5     ✔ tibble    3.3.1
✔ purrr     1.2.1     ✔ tidyr     1.3.2
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter()      masks stats::filter()
✖ purrr::flatten()     masks rlang::flatten()
✖ purrr::flatten_chr() masks rlang::flatten_chr()
✖ purrr::flatten_dbl() masks rlang::flatten_dbl()
✖ purrr::flatten_int() masks rlang::flatten_int()
✖ purrr::flatten_lgl() masks rlang::flatten_lgl()
✖ purrr::flatten_raw() masks rlang::flatten_raw()
✖ purrr::invoke()      masks rlang::invoke()
✖ dplyr::lag()         masks stats::lag()
✖ purrr::splice()      masks rlang::splice()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(jsonlite)

Attaching package: 'jsonlite'

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

    flatten

The following objects are masked from 'package:rlang':

    flatten, unbox
library(ggplot2)
library(lubridate)

The API that I have chosen will return the most popular articles on NYTimes.com for the period of 30 days. The data set has 20 rows and 22 columns.

url<-"https://api.nytimes.com/svc/mostpopular/v2/emailed/30.json"
api_key<-Sys.getenv("api_key")
# make API request
response<-request(url) %>%
  # Add custom headers
    req_headers("Accept"="application/json")%>%
    req_url_query(
    `api-key` = api_key
  ) %>%
  req_perform()

# Extract JSON response as text
json_data <- resp_body_string(response)
# Parse JSON 
data<-fromJSON(json_data)
# check object type
class(data)
[1] "list"
# Convert the list of elements into a clean tibble
articles <- bind_rows(data$results)
# clean dataset
clean_df<-articles%>%
  select(url,id,published_date,section,byline,title,des_facet)%>%
  unnest_longer(des_facet)
  unnest_df<-clean_df%>%
  count(des_facet,sort=TRUE)

For further data analysis, I retained only url,id,published_date,section,byline,title,des_facet columns by stripping out unnecessary columns. Next, the des_facet field contains nested list arrays of multiple topic tags per article, the unnest_longer() function was applied. This expanded the dataset vertically by extracting each individual string descriptor into its own distinct row

What topics appear most often among articles?

top_10_topics <- unnest_df %>%
  slice_max(order_by = n, n = 10)
ggplot(top_10_topics, aes(x = reorder(des_facet, n), y = n)) +
  geom_col(fill = "#2b5c8f", width = 0.7) +
  coord_flip() +
  labs(
    title = "Most Common NYT Article Topics 30 days",
    x = "Topic",
    y = "Number of Appearances"
  ) +
  theme_minimal()+
 theme(
    panel.grid.major.y = element_blank(),
    panel.grid.minor = element_blank(),
    plot.title = element_text(face = "bold", size = 14),
    plot.subtitle = element_text(color = "gray30")
  )

The chart illustrates the top 10 most frequent article topics appearing over a 30-day period.United States Politics and Government is the most frequent topic with 5 appearances in 30 days, followed by Midterm Elections(2026), Personal Profile, and AI are tied with 3 appearances each, while the rest of the articles topics are tied with 2 appearances each.

Which section of the article publish most

ggplot(articles, aes(x = fct_infreq(section))) +
  geom_bar(fill = "lightblue", width = 0.7) +
  geom_text(
    stat = "count",
    aes(label = after_stat(count)),
    vjust = -0.5,
    size = 3.5
  ) +
  labs(
    title = "Number of Articles by Section",
    x = "Section",
    y = "Count"
  ) +
  theme_minimal()+
   theme(
    panel.grid.major.y = element_blank(),
    panel.grid.minor = element_blank(),
    plot.title = element_text(face = "bold", size = 14),
    plot.subtitle = element_text(color = "gray30")
  )

During a 30-day period, the Opinion section published the maximum number of articles with 4, closely followed by the Science section with 3. Business, Magazine, U.S., and Well are tied with 2 articles each, while Books, Style, Technology, Travel, and World published the least with 1 article each.

# convert published_date char to Date datatype
articles <- articles %>%
  mutate(published_date = as.Date(published_date))

article_per_day<-articles%>%
  mutate(day = weekdays(published_date))%>%
  group_by(day)%>%
  summarise(
    total_articles=n()
  )%>%arrange(total_articles)
article_per_day
# A tibble: 7 × 2
  day       total_articles
  <chr>              <int>
1 Monday                 1
2 Saturday               2
3 Friday                 3
4 Sunday                 3
5 Thursday               3
6 Tuesday                4
7 Wednesday              4
glimpse(clean_df)
Rows: 106
Columns: 7
$ url            <chr> "https://www.nytimes.com/2026/10/18/opinion/business-sc…
$ id             <dbl> 1e+14, 1e+14, 1e+14, 1e+14, 1e+14, 1e+14, 1e+14, 1e+14,…
$ published_date <chr> "2026-10-18", "2026-10-18", "2026-10-18", "2026-10-18",…
$ section        <chr> "Opinion", "Opinion", "Opinion", "Opinion", "Well", "We…
$ byline         <chr> "By Chris Marquis", "By Chris Marquis", "By Chris Marqu…
$ title          <chr> "I Teach at a Business School. It’s Time to Shut Them D…
$ des_facet      <chr> "Business Schools", "Colleges and Universities", "Execu…
ggplot(article_per_day, aes(x = day, y = total_articles)) +
   geom_segment(
    aes(  
        color = "orange"
,xend = day, y = 0, yend = total_articles)
  ) +
   coord_flip() +
  geom_point(
        color = "darkgreen",
size = 4) +
  labs(
    title = 
      "Number of Articles Published in 30 days",
    x = "Day of the Week",
    y = "Number of Articles"
  ) +
  theme_minimal()

glimpse(clean_df)
Rows: 106
Columns: 7
$ url            <chr> "https://www.nytimes.com/2026/10/18/opinion/business-sc…
$ id             <dbl> 1e+14, 1e+14, 1e+14, 1e+14, 1e+14, 1e+14, 1e+14, 1e+14,…
$ published_date <chr> "2026-10-18", "2026-10-18", "2026-10-18", "2026-10-18",…
$ section        <chr> "Opinion", "Opinion", "Opinion", "Opinion", "Well", "We…
$ byline         <chr> "By Chris Marquis", "By Chris Marquis", "By Chris Marqu…
$ title          <chr> "I Teach at a Business School. It’s Time to Shut Them D…
$ des_facet      <chr> "Business Schools", "Colleges and Universities", "Execu…

The maximum number of articles was published on Tuesday and Wednesday with 4 each, followed by Thursday, Sunday, and Friday with 3 each, while the least number of articles was published on Monday with 1.

Conclusion

Based on the most popular articles published over a one-month period, the most common topic was United States Politics and Government, with the Opinion section publishing the highest volume of content. Additionally, the maximum number of articles was published on Tuesdays and Wednesdays.

Citation and Reference

https://chatgpt.com/c/6ae2b2ec-5140-83e9-baf1-cfb3d3bf0f5c

https://developer.nytimes.com/docs/most-popular-product/1/routes/emailed/%7Bperiod%7D.json/get