Accessing Data Through APIs in R

Alex Manos, M.S.

Pinellas County Division of Environmental Management

August 26, 2025

Finding Data Online

  • Data is everywhere online
  • Data can be found in many formats (excel, csv, text)
  • Usually data can be downloaded manually from a data providers webpage
  • This may not be the most efficient method though if the data is updated regularly and if the providers offers an API

What is an API?

  • An API (Application Programming Interface) is when one computer (client) interacts with another (server).
  • The server receives a request, processes it, and returns a response.
  • We can use R to interact with APIs and retrieve data from providers.
  • R can be used to parse the data and return it in a tidy data format.

Why use an API?

  • APIs allow us to access data and services from other applications or platforms
  • Removes the need to manually data download from the source
  • Can be automated to run on a schedule
  • Provides access to up-to-date data

API Terms

  • REST API: Software architecture that uses HTTP requests to access and use data
  • HTTP Request: Protocol for sending requests from client to server
  • Endpoint: The URL where the API can be accessed
  • Parameters: Filters or options you include in the request (dates, locations, etc.)
  • Authentication: Some APIs require a key or token to access data
  • Response: The data returned by the API, often in JSON or XML format
  • JSON: JavaScript Object Notation, a lightweight data format

Typical API Request Workflow

Identify the API endpoint
Build the query (URL + parameters)
Make the request
Receive raw response
Use R to parse data

R Packages for APIs

  • httr2: Send HTTP requests to an API
  • xml2: Parse XML responses into data frames
  • jsonlite: Parse JSON responses into data frames
  • tidyverse: Clean and manipulate the data
install.packages(c('httr2','jsonlite','dplyr'))

API Request Format

Making a request to an API usually involves building a URL with the endpoint and parameters so the server knows what data to return.

  • protocol: Tells the browser how to communicate with the server
  • host: The domain name of the server hosting the resource
  • path: The location/endpoint of the API for the resource
  • query: Parameters to filter or specify the data you want

Every API is Different

  • Each API has its own endpoints, parameters, and authentication methods
  • Always refer to the API documentation for details on how to use it
  • Documentation will provide examples of requests and responses
  • Some APIs require authentication (API keys, tokens) to access data

Example API: Advice Slip

  • The Advice Slip API provides random pieces of advice
  • No authentication required
  • Simple endpoint to retrieve advice

https://api.adviceslip.com

Example API: Advice Slip (contd.)

library(httr2)
library(jsonlite)
# Define the API endpoint
advice_url <- "https://api.adviceslip.com"
# Create a request to the API
req <- httr2::request(advice_url)
# Show what will be sent to the server without actually sending it
req |>
  httr2::req_dry_run()
GET / HTTP/1.1
accept: */*
accept-encoding: deflate, gzip
host: api.adviceslip.com
user-agent: httr2/1.2.1 r-curl/6.4.0 libcurl/8.14.1
# Add the query to the path and send the request
resp <- req |>
  httr2::req_url_path_append("advice") |>
  httr2::req_perform()
resp
<httr2_response>
GET https://api.adviceslip.com/advice
Status: 200 OK
Content-Type: text/html
Body: In memory (61 bytes)

Example API: Advice Slip (contd.)

class(resp)
[1] "httr2_response"
names(resp)
[1] "method"      "url"         "status_code" "headers"     "body"       
[6] "timing"      "request"     "cache"      
resp$body
 [1] 7b 22 73 6c 69 70 22 3a 20 7b 20 22 69 64 22 3a 20 31 39 32 2c 20 22 61 64
[26] 76 69 63 65 22 3a 20 22 44 6f 6e 27 74 20 74 61 6b 65 20 69 74 20 70 65 72
[51] 73 6f 6e 61 6c 6c 79 2e 22 7d 7d
class(resp$body)
[1] "raw"
# Convert the raw characters into
body_json <- rawToChar(resp$body)
body_json
[1] "{\"slip\": { \"id\": 192, \"advice\": \"Don't take it personally.\"}}"

Example API: Advice Slip (contd.)

# Convert the raw vector into text
slip_advice <- jsonlite::fromJSON(body_json)
slip_advice
$slip
$slip$id
[1] 192

$slip$advice
[1] "Don't take it personally."
class(slip_advice)
[1] "list"
# Extract the advice string from the list
slip_advice$slip$advice
[1] "Don't take it personally."
# We can also extract the response as a string, which is a more straightforward
# way of getting the data
resp |>
  httr2::resp_body_string()
[1] "{\"slip\": { \"id\": 192, \"advice\": \"Don't take it personally.\"}}"

Example API: Advice Slip (contd.)

# Now parse the JSON text from the string
resp |>
  httr2::resp_body_string() |>
  jsonlite::fromJSON()
$slip
$slip$id
[1] 192

$slip$advice
[1] "Don't take it personally."
resp_advice_id5 <- req |>
  httr2::req_url_path_append("advice/5") |>
  httr2::req_perform()
resp_advice_id5
<httr2_response>
GET https://api.adviceslip.com/advice/5
Status: 200 OK
Content-Type: text/html
Body: In memory (66 bytes)
advice_id5 <- resp_advice_id5 |>
  httr2::resp_body_string() |>
  jsonlite::fromJSON()
advice_id5$slip$advice
[1] "If you have the chance, take it!"

USGS NAS API

  • USGS provides an API for nonindigenous aquatic species (NAS) data.
  • Spatially referenced data on NAS species precense.
  • Species, lat/long, obs. date

https://nas.er.usgs.gov/api/documentation.aspx

USGS NAS API (contd.)

# load packages
library(dplyr)

# Define our endpoint
nasAPI <- 'http://nas.er.usgs.gov/api/v2'
nasReq <- request(nasAPI) |>
  req_url_path_append("occurrence/search") |>
  req_url_query(
    state = "FL",
    county = "Pinellas"
  ) |>
  req_perform()
nasData <- nasReq$body |>
  rawToChar() |>
  fromJSON()
names(nasData)
[1] "endOfRecords" "count"        "offset"       "limit"        "results"     
dplyr::glimpse(nasData$results)
Rows: 2,321
Columns: 34
$ key              <int> 139, 204, 426, 645, 646, 647, 648, 649, 756, 757, 312…
$ speciesID        <int> 61, 57, 61, 61, 61, 61, 61, 61, 48, 48, 463, 762, 966…
$ group            <chr> "Amphibians-Frogs", "Amphibians-Frogs", "Amphibians-F…
$ family           <chr> "Eleutherodactylidae", "Hylidae", "Eleutherodactylida…
$ genus            <chr> "Eleutherodactylus", "Osteopilus", "Eleutherodactylus…
$ species          <chr> "planirostris", "septentrionalis", "planirostris", "p…
$ scientificName   <chr> "Eleutherodactylus planirostris", "Osteopilus septent…
$ commonName       <chr> "Greenhouse Frog", "Cuban Treefrog", "Greenhouse Frog…
$ state            <chr> "Florida", "Florida", "Florida", "Florida", "Florida"…
$ county           <chr> "Pinellas", "Pinellas", "Pinellas", "Pinellas", "Pine…
$ locality         <chr> "Pinellas County", "Largo, jct of Highland and 5th av…
$ decimalLatitude  <dbl> 27.88965, 27.92130, 27.96556, 28.04183, 28.03418, 27.…
$ decimalLongitude <dbl> -82.72777, -82.77490, -82.80028, -82.67178, -82.66510…
$ latLongSource    <chr> "US Census Bureau", "Map derived", "Map derived", "Ma…
$ latLongAccuracy  <chr> "Centroid", "Accurate", "Approximate", "Approximate",…
$ `Centroid Type`  <chr> "County", "", "", "", "", "", "", "", "", "", "", "",…
$ huc8Name         <chr> "Tampa Bay", "Crystal-Pithlachascotee", "Crystal-Pith…
$ huc8             <chr> "03100200", "03100207", "03100207", "03100206", "0310…
$ huc10Name        <chr> "", "Anclote River", "", "", "", "", "", "", "", "", …
$ huc10            <chr> "", "0310020705", "", "", "", "", "", "", "", "", "03…
$ huc12Name        <chr> "", "Lake Seminole", "", "", "", "", "", "", "", "", …
$ huc12            <chr> "", "031002070505", "", "", "", "", "", "", "", "", "…
$ date             <chr> "1988-8", "1993-7-8", "1940-7-8", "1991-1-28", "1991-…
$ year             <int> 1988, 1993, 1940, 1991, 1991, 1957, 1993, 1938, 1975,…
$ month            <int> 8, 7, 7, 1, 5, 9, 1, 12, 12, 12, 3, 3, 11, 3, 3, 3, 1…
$ day              <int> NA, 8, 8, 28, 1, 21, 27, 31, 31, 31, 1, 1, 8, 1, 1, 1…
$ status           <chr> "established", "established", "collected", "establish…
$ comments         <chr> "", "collected from trees bordering a pond and from w…
$ recordType       <chr> "Literature", "Specimen", "Literature", "Specimen", "…
$ disposal         <chr> "", "Florida Museum of Natural History herpetology co…
$ museumCatNumber  <chr> "", "UF 87694-701", "", "UF 81717", "UF 81718", "UF 9…
$ freshMarineIntro <chr> "Freshwater", "Freshwater", "Freshwater", "Freshwater…
$ UUID             <chr> "", "", "", "", "", "", "", "", "", "", "AD80BEE9-EA7…
$ references       <list> [<data.frame[2 x 7]>], [<data.frame[1 x 7]>], [<data…

Your Turn

  1. Install and load the packages httr2, jsonlite, using the install.packages() and library() functions if not done already

  2. Use the USGS NAS API to request data for your county

  3. Read the documentation to see the available options for the “group” parameter and set the query to one of those values

  4. Send the request and inspect the results

10:00

FDEP WIN API

  • Florida Department of Environmental Protection (FDEP) provides an API for accessing water quality data.
  • DEP API webpage has parameters options that you can set
  • Need to know organization ID used when submitting WIN reports

DEP API Documentation https://prodapps.dep.state.fl.us/dear-watershed/swagger-ui/index.html#/

FDEP WIN API - orgID

  • If you dont know your organization ID or you want to find another organizations ID go to the FDEP WIN webpage

https://prodenv.dep.state.fl.us/DearWin/public/welcomeGeneralPublic?calledBy=GENERALPUBLIC

Reprts & Extracts > WAVES > Organization and Contact > Organization Name > Deselect all > Choose your org and hit the “>>” button > Done > use the code that populates the Organization ID box

FDEP WIN API

library(httr2)
library(jsonlite)
library(dplyr)

# Define the API url
url <- 'https://prodapps.dep.state.fl.us/dear-watershed'
# Make the request to the API with the specified parameters
resp <- httr2::request(url) |>
  httr2::req_url_path_append('result-activities') |>
  httr2::req_url_query(
    `ActivityStartDateFrom (>=)` = '2024-03-11',
    `ActivityStartDateTo (<=)` = '2024-03-15',
    `Organization ID` = '21FLPDEM',
    page = 0,
    size = 500,
    sort = 'resultKey,ASC'
  ) |>
  httr2::req_perform()
resp
<httr2_response>
GET https://prodapps.dep.state.fl.us/dear-watershed/result-activities?ActivityStartDateFrom%20%28%3E%3D%29=2024-03-11&ActivityStartDateTo%20%28%3C%3D%29=2024-03-15&Organization%20ID=21FLPDEM&page=0&size=500&sort=resultKey%2CASC
Status: 200 OK
Content-Type: application/json
Body: In memory (1400053 bytes)

FDEP WIN API

# Parse the response
depResp <- resp$body |>
  rawToChar() |>
  jsonlite::fromJSON()
names(depResp)
 [1] "content"          "pageable"         "last"             "totalPages"      
 [5] "totalElements"    "size"             "number"           "sort"            
 [9] "first"            "numberOfElements" "empty"           
glimpse(depResp$content)
Rows: 500
Columns: 75
$ organizationId            <chr> "21FLPDEM", "21FLPDEM", "21FLPDEM", "21FLPDE…
$ organizationName          <chr> "PINELLAS COUNTY ENVIRONMENTAL MANAGEMENT", …
$ monitoringLocId           <chr> "W8-C-24-02", "W8-C-24-02", "W8-C-24-02", "W…
$ monitoringLocationName    <chr> "Boca Ciega Bay", "Boca Ciega Bay", "Boca Ci…
$ primaryType               <chr> "Surface Water", "Surface Water", "Surface W…
$ secondaryType             <chr> "Estuary", "Estuary", "Estuary", "Estuary", …
$ latitude                  <dbl> 27.68893, 27.68893, 27.68893, 27.68893, 27.6…
$ longitude                 <dbl> -82.70168, -82.70168, -82.70168, -82.70168, …
$ depDatum                  <chr> "North American Datum Of 1983", "North Ameri…
$ storetMonitoringLocId     <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ county                    <chr> "PINELLAS", "PINELLAS", "PINELLAS", "PINELLA…
$ gisBmapId                 <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ gisBmapSubLocation        <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ wbid                      <chr> "1558N", "1558N", "1558N", "1558N", "1558N",…
$ basin                     <chr> "TAMPA BAY", "TAMPA BAY", "TAMPA BAY", "TAMP…
$ gisHucTwelveCodeId        <chr> "031002060700", "031002060700", "03100206070…
$ gisHucTwelveCodeName      <chr> "TAMPA BAY", "TAMPA BAY", "TAMPA BAY", "TAMP…
$ gisHucEightCodeId         <chr> "03100206", "03100206", "03100206", "0310020…
$ gisHucEightCodeName       <chr> "TAMPA BAY", "TAMPA BAY", "TAMPA BAY", "TAMP…
$ gisStateName              <chr> "FLORIDA", "FLORIDA", "FLORIDA", "FLORIDA", …
$ gisWaterbodyTypeName      <chr> "ESTUARY", "ESTUARY", "ESTUARY", "ESTUARY", …
$ gisWaterbodyClassId       <chr> "2", "2", "2", "2", "2", "2", "2", "2", "2",…
$ wellFloridaUniqueId       <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ wellTypeName              <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ aquiferConfinementName    <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ gwWaterbodySubname        <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ projectId                 <chr> "WQMP", "WQMP", "WQMP", "WQMP", "WQMP", "WQM…
$ activityId                <chr> "W8C2421016F", "W8C2421016F", "W8C2421016F",…
$ activityType              <chr> "Field", "Field", "Field", "Field", "Field",…
$ activityStartDate         <chr> "03/13/2024 10:16:00", "03/13/2024 10:16:00"…
$ activityTimeZoneShortName <chr> "EDT", "EDT", "EDT", "EDT", "EDT", "EDT", "E…
$ media                     <chr> "Water", "Water", "Water", "Water", "Water",…
$ matrix                    <chr> "AQUEOUS-Surface Water", "AQUEOUS-Surface Wa…
$ sampleCollectionTypeName  <chr> "Field Testing-Discrete", "Field Testing-Dis…
$ samplingAgencyName        <chr> "PINELLAS COUNTY ENVIRONMENTAL MANAGEMENT", …
$ sampleCollectEquipName    <chr> "Misc Field Device", "Misc Field Device", "M…
$ activityDepth             <dbl> 0.303, 0.303, 0.303, 0.303, 0.303, 0.303, 3.…
$ depthUnit                 <chr> "m", "m", "m", "m", "m", "m", "m", "m", "m",…
$ activityTopDepth          <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ activityBottomDepth       <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ topBottomDepthUnit        <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ activityTotalDepthCnt     <dbl> 4.131, 4.131, 4.131, 4.131, 4.131, 4.131, NA…
$ totalDepthUnitName        <chr> "m", "m", "m", "m", "m", "m", NA, NA, NA, NA…
$ activityRepresentativeInd <chr> "Representative", "Representative", "Represe…
$ activityCommentText       <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ activitySamplerName       <chr> "R. Ryczek", "R. Ryczek", "R. Ryczek", "R. R…
$ masterActivityId          <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ activityCreateTs          <chr> "09/17/2024 10:35:10", "09/17/2024 10:35:10"…
$ activityModifyTs          <chr> "09/17/2024 10:40:02", "09/17/2024 10:40:02"…
$ resultKey                 <int> 16264604, 16264605, 16264606, 16264607, 1626…
$ orgNativePkId             <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ depAnalyteGroupName       <chr> "General Physical-Chemical", "General Physic…
$ depAnalytePrimaryName     <chr> "Dissolved Oxygen", "Dissolved Oxygen Satura…
$ adaptAnalyteId            <chr> "1880", "WIN-011", "1900", "1975", "1610", "…
$ depResultValue            <dbl> 6.89, 96.00, 7.96, 33.64, 51160.80, 22.14, 6…
$ depResultUnit             <chr> "mg/L", "%", "SU", "PSU", "umho/cm", "deg C"…
$ depMdl                    <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ depPql                    <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ valueQualifier            <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ sampleFraction            <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ preparationMethodName     <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ preparationTs             <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ preparatTimeZoneShortName <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ analysisMethodName        <chr> "FDEP FT1500", "FDEP FT1500", "FDEP FT1100",…
$ analysisTs                <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ analysisTimeZoneShortName <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ laboratoryId              <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ laboratoryLongName        <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ labAccreditationAuthName  <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ resultCommentText         <chr> "Open water, teal green color, visible surfa…
$ labSampleId               <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ dilutionCnt               <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ auditCensoredDecisions    <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ resultCreateTs            <chr> "09/17/2024 10:35:14", "09/17/2024 10:35:14"…
$ resultModifyTs            <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …

FDEP WIN API

  • Check API webpage for more parameters that can be set
  • Need to contact WIN administrator to schedule large dataset extractions
  • API calls limited to 500 results returned per page, but we can access more using loops

FDEP WIN API

# Initialize an empty list to fill and set the initial page number
allResults <- list()
page <- 0

# Start while loop so the page number will increase until no data is returned 
while (TRUE) {
  # Start the request
  resp <- httr2::request(url) |>
    httr2::req_url_path_append('result-activities') |>
    httr2::req_url_query(
      `ActivityStartDateFrom (>=)` = '2024-03-11',
      `ActivityStartDateTo (<=)` = '2024-03-15',
      `Organization ID` = '21FLPDEM',
      page = page,
      size = 500,
      sort = 'resultKey,ASC'
    ) |>
    httr2::req_perform()
  
  # Parse the response
  depResp <- resp$body |>
    rawToChar() |>
    jsonlite::fromJSON() 
  
  # Check if data is returned from the request and if not, stop the loop,
  # otherwise add the results to the empty list
  if (is.null(nrow(depResp$content))) {
    break
  } else {
    allResults <- append(allResults, list(depResp$content))
  }
  
  # Increase the page number
  page <- page + 1
}

FDEP WIN API

# Combine all the reults
df <- bind_rows(allResults)
glimpse(df)
Rows: 809
Columns: 75
$ organizationId            <chr> "21FLPDEM", "21FLPDEM", "21FLPDEM", "21FLPDE…
$ organizationName          <chr> "PINELLAS COUNTY ENVIRONMENTAL MANAGEMENT", …
$ monitoringLocId           <chr> "W8-C-24-02", "W8-C-24-02", "W8-C-24-02", "W…
$ monitoringLocationName    <chr> "Boca Ciega Bay", "Boca Ciega Bay", "Boca Ci…
$ primaryType               <chr> "Surface Water", "Surface Water", "Surface W…
$ secondaryType             <chr> "Estuary", "Estuary", "Estuary", "Estuary", …
$ latitude                  <dbl> 27.68893, 27.68893, 27.68893, 27.68893, 27.6…
$ longitude                 <dbl> -82.70168, -82.70168, -82.70168, -82.70168, …
$ depDatum                  <chr> "North American Datum Of 1983", "North Ameri…
$ storetMonitoringLocId     <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ county                    <chr> "PINELLAS", "PINELLAS", "PINELLAS", "PINELLA…
$ gisBmapId                 <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ gisBmapSubLocation        <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ wbid                      <chr> "1558N", "1558N", "1558N", "1558N", "1558N",…
$ basin                     <chr> "TAMPA BAY", "TAMPA BAY", "TAMPA BAY", "TAMP…
$ gisHucTwelveCodeId        <chr> "031002060700", "031002060700", "03100206070…
$ gisHucTwelveCodeName      <chr> "TAMPA BAY", "TAMPA BAY", "TAMPA BAY", "TAMP…
$ gisHucEightCodeId         <chr> "03100206", "03100206", "03100206", "0310020…
$ gisHucEightCodeName       <chr> "TAMPA BAY", "TAMPA BAY", "TAMPA BAY", "TAMP…
$ gisStateName              <chr> "FLORIDA", "FLORIDA", "FLORIDA", "FLORIDA", …
$ gisWaterbodyTypeName      <chr> "ESTUARY", "ESTUARY", "ESTUARY", "ESTUARY", …
$ gisWaterbodyClassId       <chr> "2", "2", "2", "2", "2", "2", "2", "2", "2",…
$ wellFloridaUniqueId       <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ wellTypeName              <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ aquiferConfinementName    <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ gwWaterbodySubname        <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ projectId                 <chr> "WQMP", "WQMP", "WQMP", "WQMP", "WQMP", "WQM…
$ activityId                <chr> "W8C2421016F", "W8C2421016F", "W8C2421016F",…
$ activityType              <chr> "Field", "Field", "Field", "Field", "Field",…
$ activityStartDate         <chr> "03/13/2024 10:16:00", "03/13/2024 10:16:00"…
$ activityTimeZoneShortName <chr> "EDT", "EDT", "EDT", "EDT", "EDT", "EDT", "E…
$ media                     <chr> "Water", "Water", "Water", "Water", "Water",…
$ matrix                    <chr> "AQUEOUS-Surface Water", "AQUEOUS-Surface Wa…
$ sampleCollectionTypeName  <chr> "Field Testing-Discrete", "Field Testing-Dis…
$ samplingAgencyName        <chr> "PINELLAS COUNTY ENVIRONMENTAL MANAGEMENT", …
$ sampleCollectEquipName    <chr> "Misc Field Device", "Misc Field Device", "M…
$ activityDepth             <dbl> 0.303, 0.303, 0.303, 0.303, 0.303, 0.303, 3.…
$ depthUnit                 <chr> "m", "m", "m", "m", "m", "m", "m", "m", "m",…
$ activityTopDepth          <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ activityBottomDepth       <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ topBottomDepthUnit        <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ activityTotalDepthCnt     <dbl> 4.131, 4.131, 4.131, 4.131, 4.131, 4.131, NA…
$ totalDepthUnitName        <chr> "m", "m", "m", "m", "m", "m", NA, NA, NA, NA…
$ activityRepresentativeInd <chr> "Representative", "Representative", "Represe…
$ activityCommentText       <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ activitySamplerName       <chr> "R. Ryczek", "R. Ryczek", "R. Ryczek", "R. R…
$ masterActivityId          <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ activityCreateTs          <chr> "09/17/2024 10:35:10", "09/17/2024 10:35:10"…
$ activityModifyTs          <chr> "09/17/2024 10:40:02", "09/17/2024 10:40:02"…
$ resultKey                 <int> 16264604, 16264605, 16264606, 16264607, 1626…
$ orgNativePkId             <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ depAnalyteGroupName       <chr> "General Physical-Chemical", "General Physic…
$ depAnalytePrimaryName     <chr> "Dissolved Oxygen", "Dissolved Oxygen Satura…
$ adaptAnalyteId            <chr> "1880", "WIN-011", "1900", "1975", "1610", "…
$ depResultValue            <dbl> 6.89, 96.00, 7.96, 33.64, 51160.80, 22.14, 6…
$ depResultUnit             <chr> "mg/L", "%", "SU", "PSU", "umho/cm", "deg C"…
$ depMdl                    <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ depPql                    <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ valueQualifier            <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ sampleFraction            <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ preparationMethodName     <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ preparationTs             <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ preparatTimeZoneShortName <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ analysisMethodName        <chr> "FDEP FT1500", "FDEP FT1500", "FDEP FT1100",…
$ analysisTs                <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ analysisTimeZoneShortName <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ laboratoryId              <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ laboratoryLongName        <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ labAccreditationAuthName  <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ resultCommentText         <chr> "Open water, teal green color, visible surfa…
$ labSampleId               <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ dilutionCnt               <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ auditCensoredDecisions    <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ resultCreateTs            <chr> "09/17/2024 10:35:14", "09/17/2024 10:35:14"…
$ resultModifyTs            <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …

Notice that we used the same time frame from when we called the API without our loop and got 500 obs., now we have 809!

Recap

  • APIs allow users to access provider data programatically
  • httr2 and jsonlite packages let you access and parse data
  • Inspect API documentation to find parameters names
  • Be aware of API keys and keep yours secure

Thank you!