National Park Service API in Python#
By Nathaniel Pedigo, Michael T. Moen, and Avery Fernandez
This API is managed by the National Park Service (NPS) of the United States of America. It contains pertinent information about the national parks, monuments, and other sites managed by the NPS. This API is free to use with an API key.
“All sample data and information is attributed to National Park Service via the API service, no protection is claimed in original U.S. Government works.”
Please see the following resources for more information on API usage:
Documentation
Terms
Data Reuse
NOTE: The National Park Service API limits requests to a maximum of 1000 requests per hour.
These recipe examples were tested on May 7, 2025.
Setup#
Import Libraries#
The following external libraries need to be installed into your enviornment to run the code examples in this tutorial:
We import the libraries used in this tutorial below:
import requests
import matplotlib.pyplot as plt
from time import sleep
from pprint import pprint
from dotenv import load_dotenv
import os
Import API Key#
An API key is required to access the National Park Service API. You can sign up for one at the National Park Service API Get Started Page.
We keep our API key in a separate file, a .env
file, and use the dotenv
library to access it. If you use this method, create a file named .env
in the same directory as this notebook and add the following line to it:
NPS_API_KEY=PUT_YOUR_API_KEY_HERE
load_dotenv()
try:
API_KEY = os.environ["NPS_API_KEY"]
except KeyError:
print("API key not found. Please set 'NPS_API_KEY' in your .env file.")
else:
print("Environment and API key successfully loaded.")
Environment and API key successfully loaded.
1. Find the NPS Parks in a State#
Count the number of National Park Service parks in Alabama.
We use the following parameters in our API query:
stateCode
specifies the two letter code of the state being used as a filter. Note that the codes for U.S. territories can also be used (e.g.DC
for the District of Columbia andMP
for the Northern Mariana Islands).limit
specifies the maximum number of results to return in a request. It is set to 50 by default.
The NPS API allows for a number of parameters to be specified, including the state, the type of site, and the number of results returned. For this example, we will be using the state of Alabama, the type of site as national parks, and the maximum number of results returned as 200.
BASE_URL = "https://developer.nps.gov/api/v1/"
state = "AL"
limit = 200
endpoint = "parks"
params = {
"stateCode": state,
"limit": limit,
"api_key": API_KEY
}
Next, we use requests.get()
to make the API call.
Then, we must check that the request was performed successfully. The request returns a JSON object, which we can parse using the json()
function.
try:
response = requests.get(f"{BASE_URL}{endpoint}", params=params)
# Raise an error for bad responses
response.raise_for_status()
data = response.json()
pprint(data, depth=1)
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}")
data = None
{'data': [...], 'limit': '200', 'start': '0', 'total': '11'}
Finally, we can print our result:
print(f"The number of NPS managed lands in {state} is {data["total"]}.")
The number of NPS managed lands in AL is 11.
Note that none of the parks returned are National Parks, but other designations. See the National Park Service website to learn more about these designations.
print(f"{"Park Name":<45} Designation\n")
for park in data["data"]:
print(f"{park["fullName"]:<45} {park["designation"]}")
Park Name Designation
Birmingham Civil Rights National Monument National Monument
Freedom Riders National Monument National Monument
Horseshoe Bend National Military Park National Military Park
Little River Canyon National Preserve National Preserve
Natchez Trace National Scenic Trail National Scenic Trail
Natchez Trace Parkway Parkway
Russell Cave National Monument National Monument
Selma To Montgomery National Historic Trail National Historic Trail
Trail Of Tears National Historic Trail National Historic Trail
Tuskegee Airmen National Historic Site National Historic Site
Tuskegee Institute National Historic Site National Historic Site
2. Finding the Number of Parking Lots Created by NPS Entities by State#
In this example, we look at the number of “parking lots created by national parks and other NPS entities” by state using the parkinglots
endpoint:
# Parameters for finding parking lot data for Alabama
endpoint = "parkinglots"
params = {
"stateCode": "AL",
"limit": 200,
"api_key": API_KEY
}
try:
response = requests.get(f"{BASE_URL}{endpoint}", params=params)
# Raise an error for bad responses
response.raise_for_status()
data = response.json()
pprint(data, depth=1)
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}")
data = None
{'data': [...], 'limit': '200', 'start': '0', 'total': '17'}
Next, we’ll loop through each state to find the total number of parking lots for each:
# Create a list of all state abbreviations in the United States
states = ["AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DE", "FL", "GA",
"HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MD",
"MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ",
"NM", "NY", "NC", "ND", "OH", "OK", "OR", "PA", "RI", "SC",
"SD", "TN", "TX", "UT", "VT", "VA", "WA", "WV", "WI", "WY"]
# Create an empty dictionary to store the total number of parking lots per state
parking_lots = {}
for state in states:
params = {
"stateCode": state,
"limit": 200,
"api_key": API_KEY
}
try:
response = requests.get(f"{BASE_URL}{endpoint}", params=params)
# Raise an error for bad responses
response.raise_for_status()
data = response.json()
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}")
data = None
if data:
# Extract the total number of NPS parking lots in the state
total = int(data["total"])
# Add the total to the parking_lots list
parking_lots[state] = total
else:
# If the data is None, set the total to -1
parking_lots[state] = -1
# Delay for some period between between API calls
sleep(0.75)
# Create a list of the top 5 states with the most parking lots using
states_sorted = sorted(parking_lots.items(), key=lambda item: item[1], reverse=True)
print("Top 5 States by NPS Parking Lots:")
for state in states_sorted[:5]:
print(f"{state[0]}: {state[1]}")
Top 5 States by NPS Parking Lots:
CA: 64
GA: 51
PA: 49
AZ: 44
VA: 42
# Extract keys and values for plotting
states_top25 = [state[0] for state in states_sorted[:25]]
values_top25 = [state[1] for state in states_sorted[:25]]
# Create a bar chart
plt.bar(states_top25, values_top25, color='limegreen')
plt.title("NPS Parking Lots by State")
plt.xlabel("State")
plt.ylabel("Number of Parking Lots")
plt.xticks(fontsize=7)
plt.yticks(fontsize=7)
plt.show()

3. Finding Campground Addresses#
Prompt the user for a state, then a selection of the parks in that state. Return the addresses of all campgrounds in the specified park.
state = ""
endpoint = "parks"
# Prompt user for a state code using the states list from Section 2
while state not in states:
state = input("Enter a valid state code: ")
params = {
"stateCode": state,
"limit": 150,
"api_key": API_KEY
}
# Perform the API call
try:
response = requests.get(f"{BASE_URL}{endpoint}", params=params)
# Raise an error for bad responses
response.raise_for_status()
data = response.json()
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}")
data = None
# Offer user a selection from available parks within specified state
parks = data["data"]
park_names = [park["fullName"] for park in parks]
park_codes = [park["parkCode"] for park in parks]
print(f"Available NPS managed lands in {state}:")
for i, park_name in enumerate(park_names):
print(f"{i+1}. {park_name}")
Available NPS managed lands in WY:
1. Bighorn Canyon National Recreation Area
2. California National Historic Trail
3. Devils Tower National Monument
4. Fort Laramie National Historic Site
5. Fossil Butte National Monument
6. Grand Teton National Park
7. Mormon Pioneer National Historic Trail
8. Oregon National Historic Trail
9. Pony Express National Historic Trail
10. Yellowstone National Park
# Prompt to pick a park
park_choice = 0
while (park_choice < 1) or (park_choice > len(park_names)):
park_choice = int(input(f"Enter the number of the park you would \
like to explore (1-{len(park_names)}): "))
# Get the park code for the selected park
park_code = park_codes[park_choice-1]
park_name = park_names[park_choice-1]
# Create the endpoint URL with the park code
params = {
"parkCode": park_code,
"api_key": API_KEY
}
endpoint = "campgrounds"
# Perform the API call
try:
response = requests.get(f"{BASE_URL}{endpoint}", params=params)
# Raise an error for bad responses
response.raise_for_status()
data = response.json()
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}")
data = None
# Extract the total number of campgrounds in the park
if data:
campgrounds = data["data"]
total = data["total"]
print(f"There are {total} campgrounds in {park_name}.")
# Print the names and addresses of the campgrounds
if int(total) > 0:
for campground in campgrounds:
print(f"\nName: {campground['name']}")
for address in campground["addresses"]:
if address["type"] == "Physical":
print(f"Address: {address['line1']} {address['city']}, \
{address['stateCode']} {address['postalCode']}")
There are 12 campgrounds in Yellowstone National Park.
Name: Bridge Bay Campground
Address: GPS Coordinates: N 44 32.070 W 110 26.218 Yellowstone National Park, WY 82190
Name: Canyon Campground
Address: GPS Coordinates: N 44 44.118 W110 29 17 Yellowstone National Park, WY 82190
Name: Fishing Bridge RV Park
Address: GPS Coordinates: N 44 33.820 W 110 22.167 Yellowstone National Park, WY 82190
Name: Grant Village Campground
Address: GPS Coordinates-- N 44 23.610 W 110 33.769 Yellowstone National Park, WY 82190
Name: Indian Creek Campground
Address: GPS Coordinates--N: 44 53.22138 W: 110 44.16414 Yellowstone National Park, WY 82190
Name: Lewis Lake Campground
Address: GPS Coordinates-- N 44.2822056, W -110.6279873 Yellowstone National Park, WY 82190
Name: Madison Campground
Address: N 44 38.725 W 110 51.687 Yellowstone National Park, WY 82190
Name: Mammoth Campground
Address: GPS Coordinates N 44 58.4166 W 110.41.59392 Yellowstone National Park, WY 82190
Name: Norris Campground
Address: GPS Coordinates: N 44 44.27088 W 110 41.6169 Yellowstone National Park, WY 82190
Name: Pebble Creek Campground
Address: GPS Coordinates -- N 44 55.01886 W 110 6.82848 Yellowstone National Park, WY 82190
Name: Slough Creek Campground
Address: GPS Coordinates -- N: 44.9488466 W:-110.3068792 Yellowstone National Park, WY 82190
Name: Tower Fall Campground
Address: GPS Coordinates N: 44 53.373 W 110 23.36856 Yellowstone National Park, WY 82190
4. Finding Locations with Restrooms within a Park#
Prompt the user for a state, then a selection of the parks in that state. Return the names of all locations in the park that have restrooms listed as an amenity.
state = ""
# Prompt user for a state code
while state not in states:
state = input("Enter a valid state code: ")
# Create the endpoint URL with the state code
endpoint = "parks"
params = {
"stateCode": state,
"limit": 50,
"api_key": API_KEY
}
# Perform the API call
try:
response = requests.get(f"{BASE_URL}{endpoint}", params=params)
# Raise an error for bad responses
response.raise_for_status()
data = response.json()
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}")
data = None
# Offer user a selection from available parks within specified state
if data:
parks = data["data"]
park_names = [park["fullName"] for park in parks]
park_codes = [park["parkCode"] for park in parks]
print("Parks in the specified state:")
for i, park_name in enumerate(park_names):
print(f"{i+1}. {park_name}")
Parks in the specified state:
1. Butterfield Overland National Historic Trail
2. California National Historic Trail
3. Gateway Arch National Park
4. George Washington Carver National Monument
5. Harry S Truman National Historic Site
6. Lewis & Clark National Historic Trail
7. Oregon National Historic Trail
8. Ozark National Scenic Riverways
9. Pony Express National Historic Trail
10. Santa Fe National Historic Trail
11. Ste. Geneviève National Historical Park
12. Trail Of Tears National Historic Trail
13. Ulysses S Grant National Historic Site
14. Wilson's Creek National Battlefield
# Prompt to pick a park
park_choice = 0
while (park_choice < 1) or (park_choice > len(park_names)):
park_choice = int(input(f"Enter the number of the park you would like \
to explore (1-{len(park_names)}): "))
# Get the park code for the selected park
park_code = park_codes[park_choice-1]
park_name = park_names[park_choice-1]
# Create the endpoint URL with the park code
endpoint = "places"
params = {
"parkCode": park_code,
"api_key": API_KEY
}
# Perform the API call
try:
response = requests.get(f"{BASE_URL}{endpoint}", params=params)
# Raise an error for bad responses
response.raise_for_status()
data = response.json()
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}")
data = None
# Extract all locations in the park
if data:
if(data["total"] == 0):
print(f"There are no locations in {park_name}.")
else:
cell_locations = []
for location in data["data"]:
if "Restroom" in location["amenities"]:
cell_locations.append(location["title"])
if cell_locations:
print(f"The following locations in {park_name} have restrooms:\n")
for location in cell_locations:
print(location)
else:
print(f"There are no locations in {park_name} with restrooms.")
The following locations in Ulysses S Grant National Historic Site have restrooms:
Grant's Farm
Jefferson Barracks
Thomas Sappington House Museum
White Haven