USGS National Map API in Python#

by Sebastian Shirk

Last updated: May 10, 2024

This notebook demonstrates how to use the USGS National Map API to get/download elevation data for a given latitude and longitude.

This data is mostly useful for gathering images of the terrain at a given location which is demonstrated at the end of this notebook.

USGS National Map Viewer: https://apps.nationalmap.gov/viewer/

USGS National Map Downloader: https://apps.nationalmap.gov/downloader/

USGS National Map Documentation: https://tnmaccess.nationalmap.gov/api/v1/docs

USGS Copyright an Credits: https://www.usgs.gov/information-policies-and-instructions/copyrights-and-credits

FAQs and Related Content: https://www.usgs.gov/faqs/what-are-terms-uselicensing-map-services-and-data-national-map

“Map services and data available from U.S. Geological Survey, National Geospatial Program.”

Note that this API is only for the United States and that if too many requests are being made nation wide, the API may not work as expected.

Import Libraries#

This tutorial will use the following libraries:

import requests
from pprint import pprint

1. Requesting the API#

We start by adding all the variables that we can use to specify what kind of data we want to retrieve:

url = "https://tnmaccess.nationalmap.gov/api/v1/products?"
bbox = ""
polygon = ""
datasets = ""
prodExtents = ""
prodFormats = ""
q = ""
dataType = ""
start = ""
end = ""
offset = "0"
max = "1"
outputFormat = "JSON"
polyType = ""
polyCode = ""
extentQuery = ""

This will start as an empty query that gives us the first sets of data on the API database. We can modify our output with the variables above

Then we add the response variable and print statement:

response = requests.get(url + bbox + "&" + polygon + "&" + datasets + "&" + prodExtents + "&" + prodFormats + "&" + q + "&" + dataType +\
                         "&" + start + "&" + end + "&" + offset + "&" + max + "&" + outputFormat + "&" + polyType + "&" + polyCode + "&" + extentQuery).json()
# This sends a query that looks like this: https://tnmaccess.nationalmap.gov/api/v1/products?&offset=0&max=1&outputFormat=JSON
# A full query with not null variables will follow a format such as this one:
# https://tnmaccess.nationalmap.gov/api/v1/products?bbox=-106,39,-106,39&datasets=National%20Elevation%20Dataset%20%28NED%29%201%20arc-second&prodExtents=&prodFormats=GeoTIFF&dateType=dateCreated&start=1996-01-22&end=2025-01-22&outputFormat=JSON

i = int(offset)
while i <= int(max) + int(offset) - 1:
    print("---------------------------------------------")
    pprint(response["items"][i], compact=False, sort_dicts=False)
    print("---------------------------------------------\n")
    i += 1
# This will print the first item in the response. In this case, with all the variables null, we will get back the first item in the database.
---------------------------------------------
{'title': '18TWK610820',
 'moreInfo': 'Lidar (Light detection and ranging) discrete-return point cloud '
             'data are available in the American Society for Photogrammetry '
             'and Remote Sensing (ASPRS) LAS format. The LAS format is a '
             'standardized binary format for storing 3-dimensional point cloud '
             'data and point attributes along with header information and '
             'variable length records specific to the data. Millions of data '
             'points are stored as a 3-dimensional data cloud as a series of x '
             '(longitude), y (latitude) and z (elevation) points. A few older '
             'projects in this collection are in ASCII format. Please refer to '
             'http://www.asprs.org/Committee-General/LASer-LAS-File-Format-Exchange-Activities.html '
             'for additional information. This data set is a LAZ (compressed '
             'LAS) format file containing [...]',
 'sourceId': '6338596ad34e900e86cdbfd3',
 'sourceName': 'ScienceBase',
 'sourceOriginId': None,
 'sourceOriginName': 'gda',
 'metaUrl': 'https://www.sciencebase.gov/catalog/item/6338596ad34e900e86cdbfd3',
 'vendorMetaUrl': 'https://prd-tnm.s3.amazonaws.com/index.html?prefix=StagedProducts/Elevation/metadata/NJ_New_Jersey_SANDY_LiDAR_15/NJ_SdL5_2014',
 'publicationDate': '2015-05-03',
 'lastUpdated': '2022-10-01T09:14:53.711-06:00',
 'dateCreated': '2022-10-01T09:14:50.910-06:00',
 'sizeInBytes': 31830199,
 'extent': 'Varies',
 'format': 'LAZ',
 'downloadURL': 'https://rockyweb.usgs.gov/vdelivery/Datasets/Staged/Elevation/LPC/Projects/USGS_Lidar_Point_Cloud_NJ_SdL5_2014_LAS_2015/laz/18TWK610820.laz',
 'downloadURLRaster': None,
 'previewGraphicURL': 'https://prd-tnm.s3.amazonaws.com/StagedProducts/Elevation/LPC/Projects/USGS_Lidar_Point_Cloud_NJ_SdL5_2014_LAS_2015/browse/18TWK610820.jpg',
 'downloadLazURL': 'https://rockyweb.usgs.gov/vdelivery/Datasets/Staged/Elevation/LPC/Projects/USGS_Lidar_Point_Cloud_NJ_SdL5_2014_LAS_2015/laz/18TWK610820.laz',
 'urls': {'LAZ': 'https://rockyweb.usgs.gov/vdelivery/Datasets/Staged/Elevation/LPC/Projects/USGS_Lidar_Point_Cloud_NJ_SdL5_2014_LAS_2015/laz/18TWK610820.laz'},
 'datasets': [],
 'boundingBox': {'minX': -74.2802466122605,
                 'maxX': -74.2624014212519,
                 'minY': 40.4863433374492,
                 'maxY': 40.4999672066248},
 'bestFitIndex': 0.0,
 'body': 'Lidar (Light detection and ranging) discrete-return point cloud data '
         'are available in the American Society for Photogrammetry and Remote '
         'Sensing (ASPRS) LAS format. The LAS format is a standardized binary '
         'format for storing 3-dimensional point cloud data and point '
         'attributes along with header information and variable length records '
         'specific to the data. Millions of data points are stored as a '
         '3-dimensional data cloud as a series of x (longitude), y (latitude) '
         'and z (elevation) points. A few older projects in this collection '
         'are in ASCII format. Please refer to '
         'http://www.asprs.org/Committee-General/LASer-LAS-File-Format-Exchange-Activities.html '
         'for additional information. This data set is a LAZ (compressed LAS) '
         'format file containing lidar point cloud data. Compression to an LAZ '
         "file was done with the LAStools 'laszip' program and can be unzipped "
         'with the same free program (laszip.org).',
 'processingUrl': 'processingUrl',
 'modificationInfo': '2022-10-01'}
---------------------------------------------

2. Specifying our Variables#

In this section I will explain what all the variables are and how to use them.

All variables (except for max and offset) should be strings that start with the variable name and end with the variable value.

The variable max and offset will be set as a string that represents the maximum number of results and pagination respectively. If max or offset are null, the API will not run. The variable max is defaulted to 1 and offset is defaulted to 0. These variables can have up to a combined total of 50.

Any variable that you do not want to specify should be left as an empty string. “”

For the most accurate ways to use the variables, please refer to the USGS National Map Documentation.

Example:

url = "https://tnmaccess.nationalmap.gov/api/v1/products?"
bbox = "bbox=-122.5,37.5,-122,38"
polygon = ""
datasets = "datasets=National%20Elevation%20Dataset%20%28NED%29%201%20arc-second"
prodExtents = ""
prodFormats = "prodFormats=GeoTIFF"
q = ""
dataType = "dateType=dateCreated"
start = "start=1996-01-22"
end = "end=2025-01-22"
offset = "0"
max = "1"
outputFormat = "outputFormat=JSON"
polyType = ""
polyCode = ""
extentQuery = ""

Note: These variables are must be in an encoded format in the URL. They are case sensitive and must be spelled correctly. Spaces are represented by %20. Parentheses are represented by %28 and %29 for left and right parentheses respectively. The API will not work if the variables are not spelled correctly or if spaces and parentheses are not encoded in the URL.

  • bbox: The bbox variable is used to specify the bounding box of the area you want to retrieve data from or a single point you would like to see around. The format is as follows:

# Box
# bbox = "bbox=minX,minY,maxX,maxY"
bbox = "bbox=-122.5,37.5,-122,38"
# Gives back in an area inside or around -122.5,37.5,-122,38

# Point
# bbox = "bbox=minX,minY,maxX,maxY"
bbox = "bbox=-106,39,-106,39"
# Gives back the area around the point -106,39
  • datatset: The dataset variable is used to specify what kind of data you want to retrieve. Here are all of the possible values you can use:

# National Boundary Dataset (NBD):
datasets="datasets=National%20Boundary%20Dataset%20%28NBD%29"
# National Elevation Dataset (NED) 1 arc-second:
datasets="datasets=National%20Elevation%20Dataset%20%28NED%29%201%20arc-second"
# Digital Elevation Model (DEM) 1 meter:
datasets="datasets=Digital%20Elevation%20Model%20%28DEM%29%201%20meter"
# etc.
# For a full list of datasets, see https://tnmaccess.nationalmap.gov/api/v1/docs
# Click on /GET /products and Try It Out to see the list of datasets
  • q: The q variable is used to specify a query string to search for data. This is useful if you want to search for a specific area or type of data. Here are all of the possible values you can use:

# 7.5:
q="q=7.5"
# Airports:
q="q=Airports"
# Bend:
q="q=Bend"
# etc.
# For a full list of queries, see https://tnmaccess.nationalmap.gov/api/v1/docs
# Click on /GET /products and Try It Out to see the list of queries

# Using q=Bridge will give back something like this:
# ---------------------------------------------
# {'title': 'US Topo 7.5-minute map for Apishapa Bridge, CO',
#  'format': 'Geospatial PDF, Geospatial PDF',
#  'downloadURL': 'https://prd-tnm.s3.amazonaws.com/StagedProducts/Maps/USTopo/PDF/CO/CO_Apishapa_Bridge_20220308_TM_geo.pdf',
# ---------------------------------------------
# As well as other information about the product

For the other variable types, see the API documentation

3. Querying and Parsing the API#

We can parse the API to get all the provided information on any given data piece. The most useful information in this data is the download URL to get the image, but there are other pieces of information that can be useful as well.

For example, we can get the title of the data, the download URL, and any other present information:

url = "https://tnmaccess.nationalmap.gov/api/v1/products?"
bbox = ""
polygon = ""
datasets = ""
prodExtents = ""
prodFormats = ""
q = ""
dataType = ""
start = ""
end = ""
offset = "0"
max = "1"
outputFormat = "JSON"
polyType = ""
polyCode = ""
extentQuery = ""

# Query:
response = requests.get(url + bbox + "&" + polygon + "&" + datasets + "&" + prodExtents + "&" + prodFormats + "&" + q + "&" + dataType + "&" + start + "&" + end + "&" + offset + "&" + max + "&" + outputFormat + "&" + polyType + "&" + polyCode + "&" + extentQuery).json()

# Add any variable to the end of response["items"][i]
# Title Example:
# Add ["title"] to the end of response["items"][i]
i = int(offset)
while i <= int(max) + int(offset) - 1:
    print("---------------------------------------------")
    pprint(response["items"][i]["title"], compact=False, sort_dicts=False)
    print("---------------------------------------------\n")
    i += 1

# DownloadURL Example: 
# Add ["downloadURL"] to the end of response["items"][i]
i = int(offset)
while i <= int(max) + int(offset) - 1:
    print("---------------------------------------------")
    pprint(response["items"][i]["downloadURL"], compact=False, sort_dicts=False)
    print("---------------------------------------------\n")
    i += 1

# Usable Variables include:
# (This is also the order that data will be printed when not specified)
["title"]
["moreInfo"]
["sourceId"]
["sourceName"]
["sourceOriginName"]
["metaUrl"]
["vendorMetaUrl"]
["publicationDate"]
["lastUpdated"]
["dateCreated"]
["sizeInBytes"]
["extent"]
["format"]
["downloadURL"]
["previewGraphicURL"]
["urls"]
["datasets"]
["boundingBox"]
["bestFitIndex"]
["body"]
["processingUrl"]
["modificationInfo"]

# To get multiple variables, duplicate the pprint line and change the variable
i = int(offset)
while i <= int(max) + int(offset) - 1:
    print("---------------------------------------------")
    pprint(response["items"][i]["title"], compact=False, sort_dicts=False)
    pprint(response["items"][i]["downloadURL"], compact=False, sort_dicts=False)
    pprint(response["items"][i]["sourceName"], compact=False, sort_dicts=False)
    print("---------------------------------------------\n")
    i += 1
    

# To go back and get all the variables, remove the variable from the pprint line
i = int(offset)
while i <= int(max) + int(offset) - 1:
    print("---------------------------------------------")
    pprint(response["items"][i], compact=False, sort_dicts=False)
    print("---------------------------------------------\n")
    i += 1
---------------------------------------------
'18TWK610820'
---------------------------------------------

---------------------------------------------
'https://rockyweb.usgs.gov/vdelivery/Datasets/Staged/Elevation/LPC/Projects/USGS_Lidar_Point_Cloud_NJ_SdL5_2014_LAS_2015/laz/18TWK610820.laz'
---------------------------------------------

---------------------------------------------
'18TWK610820'
'https://rockyweb.usgs.gov/vdelivery/Datasets/Staged/Elevation/LPC/Projects/USGS_Lidar_Point_Cloud_NJ_SdL5_2014_LAS_2015/laz/18TWK610820.laz'
'ScienceBase'
---------------------------------------------

---------------------------------------------
{'title': '18TWK610820',
 'moreInfo': 'Lidar (Light detection and ranging) discrete-return point cloud '
             'data are available in the American Society for Photogrammetry '
             'and Remote Sensing (ASPRS) LAS format. The LAS format is a '
             'standardized binary format for storing 3-dimensional point cloud '
             'data and point attributes along with header information and '
             'variable length records specific to the data. Millions of data '
             'points are stored as a 3-dimensional data cloud as a series of x '
             '(longitude), y (latitude) and z (elevation) points. A few older '
             'projects in this collection are in ASCII format. Please refer to '
             'http://www.asprs.org/Committee-General/LASer-LAS-File-Format-Exchange-Activities.html '
             'for additional information. This data set is a LAZ (compressed '
             'LAS) format file containing [...]',
 'sourceId': '6338596ad34e900e86cdbfd3',
 'sourceName': 'ScienceBase',
 'sourceOriginId': None,
 'sourceOriginName': 'gda',
 'metaUrl': 'https://www.sciencebase.gov/catalog/item/6338596ad34e900e86cdbfd3',
 'vendorMetaUrl': 'https://prd-tnm.s3.amazonaws.com/index.html?prefix=StagedProducts/Elevation/metadata/NJ_New_Jersey_SANDY_LiDAR_15/NJ_SdL5_2014',
 'publicationDate': '2015-05-03',
 'lastUpdated': '2022-10-01T09:14:53.711-06:00',
 'dateCreated': '2022-10-01T09:14:50.910-06:00',
 'sizeInBytes': 31830199,
 'extent': 'Varies',
 'format': 'LAZ',
 'downloadURL': 'https://rockyweb.usgs.gov/vdelivery/Datasets/Staged/Elevation/LPC/Projects/USGS_Lidar_Point_Cloud_NJ_SdL5_2014_LAS_2015/laz/18TWK610820.laz',
 'downloadURLRaster': None,
 'previewGraphicURL': 'https://prd-tnm.s3.amazonaws.com/StagedProducts/Elevation/LPC/Projects/USGS_Lidar_Point_Cloud_NJ_SdL5_2014_LAS_2015/browse/18TWK610820.jpg',
 'downloadLazURL': 'https://rockyweb.usgs.gov/vdelivery/Datasets/Staged/Elevation/LPC/Projects/USGS_Lidar_Point_Cloud_NJ_SdL5_2014_LAS_2015/laz/18TWK610820.laz',
 'urls': {'LAZ': 'https://rockyweb.usgs.gov/vdelivery/Datasets/Staged/Elevation/LPC/Projects/USGS_Lidar_Point_Cloud_NJ_SdL5_2014_LAS_2015/laz/18TWK610820.laz'},
 'datasets': [],
 'boundingBox': {'minX': -74.2802466122605,
                 'maxX': -74.2624014212519,
                 'minY': 40.4863433374492,
                 'maxY': 40.4999672066248},
 'bestFitIndex': 0.0,
 'body': 'Lidar (Light detection and ranging) discrete-return point cloud data '
         'are available in the American Society for Photogrammetry and Remote '
         'Sensing (ASPRS) LAS format. The LAS format is a standardized binary '
         'format for storing 3-dimensional point cloud data and point '
         'attributes along with header information and variable length records '
         'specific to the data. Millions of data points are stored as a '
         '3-dimensional data cloud as a series of x (longitude), y (latitude) '
         'and z (elevation) points. A few older projects in this collection '
         'are in ASCII format. Please refer to '
         'http://www.asprs.org/Committee-General/LASer-LAS-File-Format-Exchange-Activities.html '
         'for additional information. This data set is a LAZ (compressed LAS) '
         'format file containing lidar point cloud data. Compression to an LAZ '
         "file was done with the LAStools 'laszip' program and can be unzipped "
         'with the same free program (laszip.org).',
 'processingUrl': 'processingUrl',
 'modificationInfo': '2022-10-01'}
---------------------------------------------

4. Downloading any URLs/Images#

If you want to download any of the URLs, you can easily do this by copying the URL into any browser and it will download it.

If you want to do this strictly in the python script you can add and call this function:

def get_file(filepath):
    with open("my_file_name.extension", "wb") as f:
        file = requests.get(filepath)
        f.write(file.content)

This function will download the file and save it to the specified path.

Calling the function:

get_file(response["items"][int(offset)]["downloadURL"])

Make sure to change the "my_file_name.extension" to the name of the file you want and to you match the extension of the file you are downloading. (.tif, .jpg, .png, etc.)

I would recommend printing the response to make sure you are downloading the correct file and to see the file type.

Example:

i = int(offset)
while i <= int(max) + int(offset) - 1:
    print("---------------------------------------------")
    pprint(response["items"][i]["downloadURL"], compact=False, sort_dicts=False)
    print("---------------------------------------------\n")
    i += 1
# Output might be: "https://prd-tnm.s3.amazonaws.com/StagedProducts/Elevation/1/TIFF/historical/n19w156/USGS_1_n19w156_20230522.tif"
# The .tif is the important part here.

# Change the "my_file_name.extension" to the name of the file you want to save it as and the extension of the file you are downloading
# Here I'm changing the name to "my_tif_file.tif for the tif file I'm downloading"
def get_file(filepath):
    with open("my_tif_file.tif", "wb") as f:
        file = requests.get(filepath)
        f.write(file.content)

# Call the edited function
get_file(response["items"][int(offset)]["downloadURL"])
---------------------------------------------
'https://rockyweb.usgs.gov/vdelivery/Datasets/Staged/Elevation/LPC/Projects/USGS_Lidar_Point_Cloud_NJ_SdL5_2014_LAS_2015/laz/18TWK610820.laz'
---------------------------------------------