Working with JSON and APIs
Prerequisites: Lists, Tibbles
JSON is the format most web APIs use to send data. The jsonlite package converts between JSON and R objects.
JSON to R
library(jsonlite)
json <- '[
{"name": "Ada", "age": 36, "langs": ["R", "Python"]},
{"name": "Linus", "age": 28, "langs": ["C"]}
]'
people <- fromJSON(json)
class(people)
people[, c("name", "age")]
people$langs[[1]]
[1] "data.frame"
name age
1 Ada 36
2 Linus 28
[1] "R" "Python"
fromJSON simplifies an array of similar objects into a data frame
automatically. Nested arrays (like langs) become a list column,
with one vector per row.
Paste an API response into the JSON Formatter to see its structure before deciding how to flatten it.
R to JSON
library(jsonlite)
df <- data.frame(name = c("Ada", "Linus"), age = c(36, 28))
toJSON(df)
toJSON(list(status = "ok", count = 2), auto_unbox = TRUE)
[{"name":"Ada","age":36},{"name":"Linus","age":28}]
{"status":"ok","count":2}
auto_unbox = TRUE writes single values as 2 rather than [2] — R
values are always vectors, so without it every value becomes an array.
Add pretty = TRUE for indented output.
Calling an API
fromJSON can read a URL directly for simple public APIs. For anything
more — headers, authentication, error handling, retries — use the
httr2 package:
library(httr2)
resp <- request("https://api.github.com/repos/tidyverse/dplyr") |>
req_timeout(10) |>
req_perform()
repo <- resp_body_json(resp)
repo$stargazers_count
req_perform() raises an R error for HTTP error statuses (4xx and 5xx),
so failures aren't silently ignored.
Common mistakes
- Hard-coding API keys in scripts. Store them in environment variables
(in a
.Renvironfile that isn't committed to version control) and read them withSys.getenv("API_KEY").