Week 9 API

Author

Carol Campbell

Published

November 2, 2026

Introduction

An API (Application Programming Interface) enables different software applications to communicate, exchange information, and interact with one another efficiently.

This assignment requires us to retrieve data from a specific New York Times API endpoint, which is a digital location where software applications can send requests to programmatically access information stored in a database. To access data from a New York Times API endpoint, it is necessary to create an account and obtain an API key from them. The API Key is a randomly generated, unique and secure code that authorizes a user to connect to the server and retrieve data.

In this assignment, I will load the New York Times Top Stories in Travel API list using my API key. Then, I will use the data to answer the following question: Which category in the top stories travel section is ranked highest?

Load necessary packages:

library (httr2)
library(jsonlite)
library(purrr)

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

    flatten
library(rlang)

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

    flatten, flatten_chr, flatten_dbl, flatten_int, flatten_lgl,
    flatten_raw, invoke, splice
The following objects are masked from 'package:jsonlite':

    flatten, unbox
library(tibble)
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(cli)

Using an API key to access data

I stored my API key in the R system environment to prevent sharing it with the public. Before I begin any processing, I will load the API key, validate it and the connection to the endpoint.

#load API Key into R
nyt_key <- Sys.getenv("NYT_API_KEY")

As previously stated, the endpoint that I’ll be using for this assignment is Top Stories in Travel

# ==========================================
# ENDPOINT 1: Top Stories API (e.g., Travel)
# ==========================================
# url from NYT developers portal for Top Stories 

travel_url <- paste0("https://api.nytimes.com/svc/topstories/v2/travel.json?api-key=", nyt_key)


req <- request(travel_url) |>
  req_headers(
    Accept = "application/json",
    `User-Agent` = "Data-607-week9-cchomework"
  ) |>
  req_url_query(`api-key` = nyt_key)

req
<httr2_request>
GET https://api.nytimes.com/svc/topstories/v2/travel.json?api-key=zJ23b1bEaIzfCir91VpA5PpSAPLPsAJafLxyGKgR5HfLAxwi
Headers:
* Accept    : "application/json"
* User-Agent: "Data-607-week9-cchomework"
Body: empty
req_dry_run(req)    # preview request before sending it
GET /svc/topstories/v2/travel.json HTTP/1.1
accept: application/json
accept-encoding: deflate, gzip
host: api.nytimes.com
user-agent: Data-607-week9-cchomework

This code chunk validates the connection and returns the status. A status of “200” indicates success; “401” means invalid API key; “403” means “Access forbidden”; “404” means Endpoint not found.

response <- request("https://api.nytimes.com/svc/topstories/v2/travel.json") |>
  req_url_query('api-key' = nyt_key) |>
  req_error(is_error = function(resp) FALSE) |>
  req_perform()

status <- resp_status(response)

if (status == 200) {
  cli_alert_success("200")
} else if (status == 401) {
  cli_alert_danger("Invalid API key.")
} else if (status == 403) {
  cli_alert_danger("Access forbidden.")
} else if (status == 404) {
  cli_alert_danger("Endpoint not found.")
} else {
  cli_alert_warning("Unexpected status: {status}")
}
✔ 200
resp_content_type(response)
[1] "application/json"
con <- url(travel_url)
travel <- jsonlite::fromJSON(readLines(con, warn = FALSE))
close(con)


names(travel)
[1] "status"       "copyright"    "section"      "last_updated" "num_results" 
[6] "results"     

The API call returned a list that I named travel which contains 6 elements. Our data is likely loaded in the “results” element.

names(travel$results)
 [1] "section"             "subsection"          "title"              
 [4] "abstract"            "url"                 "uri"                
 [7] "byline"              "item_type"           "updated_date"       
[10] "created_date"        "published_date"      "material_type_facet"
[13] "kicker"              "des_facet"           "org_facet"          
[16] "per_facet"           "geo_facet"           "multimedia"         
[19] "short_url"          

Unnesting the list

# Unnesting Data 

toptravel <- as.tibble(travel$results)
Warning: `as.tibble()` was deprecated in tibble 2.0.0.
ℹ Please use `as_tibble()` instead.
ℹ The signature and semantics have changed, see `?as_tibble`.
head(toptravel)
# A tibble: 6 × 19
  section    subsection title abstract url   uri   byline item_type updated_date
  <chr>      <chr>      <chr> <chr>    <chr> <chr> <chr>  <chr>     <chr>       
1 admin      ""         ""    ""       ""    nyt:… ""     Embedded… 2015-07-20T…
2 world      "europe"   "Up … "Unlike… "htt… nyt:… "By M… Article   2026-11-02T…
3 t-magazine ""         "Tai… "Plus, … "htt… nyt:… "By L… Article   2026-10-30T…
4 world      "europe"   "Aft… "Karl B… "htt… nyt:… "By J… Article   2026-11-04T…
5 technology "personal… "Hit… "The re… "htt… nyt:… "By J… Article   2026-10-29T…
6 world      "europe"   "Is … "After … "htt… nyt:… "By M… Article   2026-10-29T…
# ℹ 10 more variables: created_date <chr>, published_date <chr>,
#   material_type_facet <chr>, kicker <chr>, des_facet <list>,
#   org_facet <list>, per_facet <list>, geo_facet <list>, multimedia <list>,
#   short_url <chr>

Examining the data

Our toptravel data frame contains 25 rows and 19 variables or columns,many of which are identified by the term “facet.” According to the New York Times (NYT) Developer’s website, facets are arrays of strings that contain descriptive subject terms or topical keywords assigned to articles by Times indexers and used for retrieval based on various criteria. This is for the standardization of its metadata.

There are four categories of “facet”:

  • des_facet: Descriptive subjects, concepts, and themes.

  • per_facet: Names of specific people mentioned (e.g., [“Soto, Juan”]).

  • org_facet: Organizations, companies, or institutions (e.g., [“Google LLC”]).

  • geo_facet: Geographic locations, countries, or cities (e.g., [“Paris (France)”]).

For this assignment, I will focus on the “des_facet” column to assist in answering my question.

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     ✔ tidyr     1.3.2
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter()      masks stats::filter()
✖ rlang::flatten()     masks purrr::flatten(), jsonlite::flatten()
✖ rlang::flatten_chr() masks purrr::flatten_chr()
✖ rlang::flatten_dbl() masks purrr::flatten_dbl()
✖ rlang::flatten_int() masks purrr::flatten_int()
✖ rlang::flatten_lgl() masks purrr::flatten_lgl()
✖ rlang::flatten_raw() masks purrr::flatten_raw()
✖ rlang::invoke()      masks purrr::invoke()
✖ dplyr::lag()         masks stats::lag()
✖ rlang::splice()      masks purrr::splice()
✖ rlang::unbox()       masks jsonlite::unbox()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(dplyr)

traveldf <- toptravel|>
  tidyr::unnest_wider(des_facet, names_sep = "_") |>
  dplyr::select(title, section, dplyr::starts_with("des_facet_")) |>
  as.data.frame()

traveldf
                                                                                   title
1                                                                                       
2                                  Up in the Clouds With the Pope, It’s Still Commercial
3                  Taipei Is One of the World’s Great Food Capitals. Here’s Where to Go.
4                              After 28 Years Walking Across the Globe, He’s Almost Home
5                            Hit the Road This Season With These Useful New Map Features
6             Is There a ‘Safest Seat’ in a Train Crash? Experts Say It’s Not So Simple.
7            Text Message Referencing a ‘Hijack’ Forces United Flight to Return to Vegas
8                                   An ‘Endless Cruise’ Goes Broke, Stranding Passengers
9                The Ocean ‘Was the Color of Chocolate.’ Mexico Declares War on Seaweed.
10                                     Holiday Travel Costs Are Soaring. 5 Ways to Save.
11          A Song in Your Heart and a Pillow Under Your Head: 5 Hotels for Music Lovers
12                 Europe’s Toughest Trek? We Put a Notorious Corsican Hike to the Test.
13 How to Spend a Bookish Afternoon in Chinatown, the Lower East Side & the East Village
14                                            Why Tofu Is the Perfect Dessert Ingredient
15                                  6 Horse-Centered Retreats That Go Beyond Trail Rides
16                            Airbnb Says Guests Shouldn’t Have to Do the Dishes Anymore
17                                    A New Art-Filled Hotel Adds to Cape Town’s Skyline
18                                                          36 Hours in Kansas City, Mo.
19                       Help! Why Won’t Travel Insurance Pay for My $17,500 Broken Leg?
20                 America Mints a New National Park, Elevating an Arizonan ‘Sky Island’
21                         Rugby Star Ilona Maher’s 5 Favorite Places in Burlington, Vt.
22                        Driving Inn to Inn to Discover the Hidden Charm of the Baltics
23                                      Boeing’s Large 737 Max Jet Secures U.S. Approval
24                           Heading to France? Amid Recent Unrest, Here’s What to Know.
25            How Do Airports Prepare for Disaster? With Fake Screams and Cloth Dummies.
      section                              des_facet_1
1       admin                                     <NA>
2       world                   Airlines and Airplanes
3  t-magazine                     Travel and Vacations
4       world             internal-open-access-from-nl
5  technology                   iOS (Operating System)
6       world     Spain Train Crash (October 28, 2026)
7      travel Aviation Accidents, Safety and Disasters
8      travel                                  Cruises
9       world                                  Seaweed
10     travel                            Budget Travel
11     travel                     Travel and Vacations
12     travel                         Hikes and Hiking
13      books                     internal-disable-fab
14 t-magazine                                     Tofu
15     travel                     Travel and Vacations
16     travel                     Travel and Vacations
17 t-magazine               Hotels and Travel Lodgings
18     travel                     Travel and Vacations
19     travel                     Travel and Vacations
20     travel  National Parks, Monuments and Seashores
21     travel                     Travel and Vacations
22     travel                     Travel and Vacations
23   business                   Airlines and Airplanes
24     travel                     Travel and Vacations
25     travel                   Airlines and Airplanes
                                des_facet_2
1                                      <NA>
2                      Travel and Vacations
3                Hotels and Travel Lodgings
4                                   Walking
5                Android (Operating System)
6             Railroad Accidents and Safety
7                    Airlines and Airplanes
8              Oil (Petroleum) and Gasoline
9                                     Algae
10                         Thanksgiving Day
11               Hotels and Travel Lodgings
12                        Camps and Camping
13                     Books and Literature
14                                 Desserts
15                                   Horses
16               Hotels and Travel Lodgings
17                                     <NA>
18                                     <NA>
19                         Travel Insurance
20                                     <NA>
21                                    Rugby
22                              Restaurants
23 Aviation Accidents, Safety and Disasters
24       Demonstrations, Protests and Riots
25                                 Airports
                                des_facet_3
1                                      <NA>
2                                     Popes
3                               Restaurants
4                      Travel and Vacations
5                                      Maps
6                                 Railroads
7                            Text Messaging
8            Prices (Fares, Fees and Rates)
9                                   Beaches
10                                Christmas
11                            Country Music
12                     Travel and Vacations
13                      Shopping and Retail
14          Interior Design and Furnishings
15               Hotels and Travel Lodgings
16                       Customer Relations
17                                     <NA>
18                                     <NA>
19                    Content Type: Service
20                                     <NA>
21                              Restaurants
22               Hotels and Travel Lodgings
23              Factories and Manufacturing
24                                     <NA>
25 Aviation Accidents, Safety and Disasters
                               des_facet_4
1                                     <NA>
2                                     <NA>
3                      Shopping and Retail
4           Content Type: Personal Profile
5                                   iPhone
6                                     <NA>
7         Threats and Threatening Messages
8                     Travel and Vacations
9                     Travel and Vacations
10                    Travel and Vacations
11                      Pop and Rock Music
12                                    <NA>
13                   Content Type: Service
14                               Furniture
15                                    Yoga
16                       Cleaning Services
17                                    <NA>
18                                    <NA>
19                                    <NA>
20                                    <NA>
21                                 Beaches
22                              Road Trips
23 Regulation and Deregulation of Industry
24                                    <NA>
25                  Fires and Firefighters
                                            des_facet_5
1                                                  <NA>
2                                                  <NA>
3                                                   Tea
4                              Records and Achievements
5                                   Mobile Applications
6                                                  <NA>
7                                             Hijacking
8                  US and Israeli Attack on Iran (2026)
9                               Politics and Government
10                       Prices (Fares, Fees and Rates)
11                                       Culture (Arts)
12                                                 <NA>
13                                           vis-design
14                                               Pearls
15                       Post-Traumatic Stress Disorder
16                                                 <NA>
17                                                 <NA>
18                                                 <NA>
19                                                 <NA>
20                                                 <NA>
21                        Ice Cream and Frozen Desserts
22                                                 <NA>
23 Boeing 737 Max Groundings and Safety Concerns (2019)
24                                                 <NA>
25                                              Rescues
                             des_facet_6                      des_facet_7
1                                   <NA>                             <NA>
2                                   <NA>                             <NA>
3                       Hikes and Hiking                             <NA>
4                          internal-eafs                             <NA>
5                            Automobiles             Travel and Vacations
6                                   <NA>                             <NA>
7                      Flight Attendants                             <NA>
8                                   <NA>                             <NA>
9                              vis-photo                             <NA>
10 Reservations (Airlines, Hotels, Etc.)       Hotels and Travel Lodgings
11                   Bars and Nightclubs                            Music
12                                  <NA>                             <NA>
13             Book Trade and Publishing                             <NA>
14                    Jewels and Jewelry                             <NA>
15            Therapy and Rehabilitation Volunteers and Community Service
16                                  <NA>                             <NA>
17                                  <NA>                             <NA>
18                                  <NA>                             <NA>
19                                  <NA>                             <NA>
20                                  <NA>                             <NA>
21                   Bars and Nightclubs                 Hikes and Hiking
22                                  <NA>                             <NA>
23                                  <NA>                             <NA>
24                                  <NA>                             <NA>
25           Emergency Medical Treatment        Disasters and Emergencies
             des_facet_8
1                   <NA>
2                   <NA>
3                   <NA>
4                   <NA>
5            Smartphones
6                   <NA>
7                   <NA>
8                   <NA>
9                   <NA>
10 Content Type: Service
11                  <NA>
12                  <NA>
13                  <NA>
14                  <NA>
15                  <NA>
16                  <NA>
17                  <NA>
18                  <NA>
19                  <NA>
20                  <NA>
21                  <NA>
22                  <NA>
23                  <NA>
24                  <NA>
25                Police

What does this mean?

We now have the “traveldf” data frame which contains the two original columns, title and section, plus the eight unnested “des_facet” columns labeled “des_facet_1, des_facet_2…des_facet_8). I searched NYT website for more detailed information on typical travel taxonomy layout, only to find that each”des_facet” column provides more granular information and multiple topic tags, all related to the original article.

A google.com query asking “what do the des_facet_1, des_facet_2,des_facet_3, des_facet_4… columns represent” as related to top travel stories” revealed the following:

• Core Identifier: Travel and Vacations almost always occupies des_facet_1 for every article in this section, serving as the top-level classification.

• Accommodations & Dining: Hotels and Motels, Resorts, Restaurants, Bars and Wine Bars found in des_facet_2.

• Modes of Transit: Cruises, Airlines and Airplanes, Walking and Hiking, Automobiles found in des_facet_3.

• Niche Styles: Ecotourism, Luxury Goods and Services, Budgets and Budgeting, Culture found in des_facet 4.

Answering the question

Therefore, to answer my original query, “Which category in the top stories travel section is ranked highest?” I decided to rank the results of des_facet_1 and then plot them.

library(ggplot2)

# 1. Calculate frequencies and rank them from highest to lowest
ranked_primary_topics <- traveldf |>
  filter(!is.na(des_facet_1)) |>  # Filter out any missing/NA
  count(des_facet_1, name = "article_count") |> # group by the primary topic and count occurrences
  arrange(desc(article_count))

# 2. View the resulting ranked table
print(ranked_primary_topics)
                                des_facet_1 article_count
1                      Travel and Vacations             9
2                    Airlines and Airplanes             3
3  Aviation Accidents, Safety and Disasters             1
4                             Budget Travel             1
5                                   Cruises             1
6                          Hikes and Hiking             1
7                Hotels and Travel Lodgings             1
8   National Parks, Monuments and Seashores             1
9                                   Seaweed             1
10     Spain Train Crash (October 28, 2026)             1
11                                     Tofu             1
12                   iOS (Operating System)             1
13                     internal-disable-fab             1
14             internal-open-access-from-nl             1
# Take the top 10 primary topics for a cleaner chart
ranked_primary_topics |>
  slice_max(article_count, n = 10) |>
  
  ggplot(aes(x = reorder(des_facet_1, article_count), y = article_count)) +
  geom_col(fill = "steelblue") +
  coord_flip() + # Flips axes so long text categories are easy to read
  labs(
    title = "Top Primary Themes in NYT Travel Stories",
    x = "Primary Topic (des_facet_1)",
    y = "Number of Articles"
  ) +
  theme_minimal()

Here we see that “Travel and Vacations” and “Hotels and Travel Lodging” dominate the ratings While the remaining topics are negligible. This is a very unimpressive graphic and not very useful.

Second time around

So let’s rephrase the question: “How will our data look if we count all secondary and tertiary elements, (i.e. include des_facets_2 through 8)? What can we glean from this?” To accomplish this I ranked the combined topics across all columns to get a more diverse look at the travel data.

# 1. Reshape, combine, and count frequencies
ranked_combined_topics <- traveldf |>
  pivot_longer(    # Pivot columns des_facet_1 through des_facet_4 into a single long column
    cols = starts_with("des_facet"),
    names_to = "facet_position", # Tracks if it was 1, 2, 3---8
    values_to = "topic",
    values_drop_na = TRUE        # Drops empty/null tags automatically
  ) |>
  count(topic, name = "total_appearances") |>  #group by topic name/calculate total occurances
  arrange(desc(total_appearances))


# 2. View the complete ranked list
print(ranked_combined_topics)
# A tibble: 80 × 2
   topic                                    total_appearances
   <chr>                                                <int>
 1 Travel and Vacations                                    16
 2 Hotels and Travel Lodgings                               7
 3 Airlines and Airplanes                                   4
 4 Aviation Accidents, Safety and Disasters                 3
 5 Content Type: Service                                    3
 6 Hikes and Hiking                                         3
 7 Restaurants                                              3
 8 Bars and Nightclubs                                      2
 9 Beaches                                                  2
10 Prices (Fares, Fees and Rates)                           2
# ℹ 70 more rows

Plotting new results

ranked_combined_topics |>
  slice_head(n = 15) |> # slice_head to get exactly the top 15 rows without breaking on ties
  
  # Plot, ensuring the items are reordered strictly by their total count
  ggplot(aes(x = reorder(topic, total_appearances), y = total_appearances)) +
  geom_col(fill = "purple", width = 0.8) +
  coord_flip() + 
  labs(
    title = "Most Frequent Overall Topics in NYT Travel Stories",
    subtitle = "Combined count across des_facet_1 through des_facet_8",
    x = NULL, 
    y = "Total Times Tagged"
  ) +
  theme_minimal(base_size = 10) +
  theme(
    panel.grid.major.y = element_blank(), 
    plot.title = element_text(face = "bold")
  )

Conclusion

This is a better visualization of the data culled from the NYT Top Travel Stories API, and helps answer my second question, How will our data look if we count all secondary and tertiary elements, (i.e. include des_facets_2 through 8)? What can we glean from this?

ANS: Even though “Travel and Vacations” and “Hotels and Travel Lodging” still dominate the ratings, a user can readily see a count of other top travel related articles that could be of interest to them. For instance, if their passion is hiking, there are three articles that may interest them; if their looking for restaurants while traveling, there are also three articles that they could read that may answer their questions.

Adding reproducibility of metadata.

The last step in our analysis is to save the reproducibility of the data.1

Since data is ever-changing, it’s important to memorialize the date/time of the data pull that is the basis of this analysis.

# Use UTC so each timestamp has one unambiguous time zone.
retrieved_at <- format(Sys.time(), tz = "UTC", usetz = TRUE)

travel_snapshot <- toptravel |>
  mutate(
    retrieved_at = retrieved_at,
    request_url = travel_url,
    .before = 1
  )

travel_snapshot
# A tibble: 25 × 21
   retrieved_at request_url section subsection title abstract url   uri   byline
   <chr>        <chr>       <chr>   <chr>      <chr> <chr>    <chr> <chr> <chr> 
 1 2026-11-05 … https://ap… admin   ""         ""    ""       ""    nyt:… ""    
 2 2026-11-05 … https://ap… world   "europe"   "Up … "Unlike… "htt… nyt:… "By M…
 3 2026-11-05 … https://ap… t-maga… ""         "Tai… "Plus, … "htt… nyt:… "By L…
 4 2026-11-05 … https://ap… world   "europe"   "Aft… "Karl B… "htt… nyt:… "By J…
 5 2026-11-05 … https://ap… techno… "personal… "Hit… "The re… "htt… nyt:… "By J…
 6 2026-11-05 … https://ap… world   "europe"   "Is … "After … "htt… nyt:… "By M…
 7 2026-11-05 … https://ap… travel  ""         "Tex… "Now ch… "htt… nyt:… "By R…
 8 2026-11-05 … https://ap… travel  ""         "An … "The cr… "htt… nyt:… "By C…
 9 2026-11-05 … https://ap… world   "americas" "The… "To pro… "htt… nyt:… "By D…
10 2026-11-05 … https://ap… travel  ""         "Hol… "As air… "htt… nyt:… "By E…
# ℹ 15 more rows
# ℹ 12 more variables: item_type <chr>, updated_date <chr>, created_date <chr>,
#   published_date <chr>, material_type_facet <chr>, kicker <chr>,
#   des_facet <list>, org_facet <list>, per_facet <list>, geo_facet <list>,
#   multimedia <list>, short_url <chr>

Footnotes

  1. Courtesy of Prof. Darwhin Gomez, week 9 code base.↩︎