Building a personal data project starts with gathering data. What better way to start than by exploring a free and well-documented API? In this article, we’ll dive into the National Weather Service API (api.weather.gov) to understand how it works, and we’ll write Python code to retrieve and explore weather data.

So… what are you going to be doing?

  • Let’s read through the docs and try to understand how to navigate the API.📜
  • I’m gonna write some Python code to fetch weather data.
  • I’m gonna analyze the data structure of the forecastHourly endpoint.

📍 Understanding the API

The National Weather Service API organizes data using a grid-point system. Each grid point covers an area of 2.5 km x 2.5 km. The /points endpoint is where you get information about retrieving data for a specific location.

Key Features of the /points Endpoint

When you pass a latitude and longitude to /points, you receive metadata about that location, including:

  • URLs for forecast data:
    • forecast: 12-hour forecast over seven days.
    • forecastHourly: Hourly forecast over seven days.
    • forecastGridData: Raw grid forecast data.
  • Observation stations near the point.
  • Associated zones (e.g., forecast zone, fire weather zone, county zone).

Here’s an example API call for Springfield, MO (latitude: 37.209, longitude: -93.2923):

1
https://api.weather.gov/points/37.209,-93.2923

🛠️ Constructing an API Request

Before making requests, it’s important to review the authentication requirements. The Weather API doesn’t require an API key (yet) but does require a User-Agent header for identification. This can be a string unique to your application, and including contact information is encouraged.

Here’s how to construct a Python request to the /points endpoint:

Python Code to Query the /points Endpoint

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import requests

def get_point_metadata(latitude, longitude):
    """
    Fetch metadata for a specific latitude and longitude using the Weather API's /points endpoint.
    """
    url = f"https://api.weather.gov/points/{latitude},{longitude}"
    headers = {"User-Agent": "WeatherDataPipeline (your_email@example.com)"}
    response = requests.get(url, headers=headers)
    response.raise_for_status()
    return response.json()

# Example: Fetching metadata for Springfield, MO
latitude = 37.209
longitude = -93.2923
point_metadata = get_point_metadata(latitude, longitude)
print(point_metadata)  # Inspect the metadata

Exploring the /points Response

Here’s a snippet of what you’ll see in the response:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
{
    "@context": [
        "https://geojson.org/geojson-ld/geojson-context.jsonld",
        {
            "@version": "1.1",
            "wx": "https://api.weather.gov/ontology#"
        }
    ],
    "type": "Feature",
    "geometry": {
        "type": "Point",
        "coordinates": [-93.2923, 37.209]
    },
    "properties": {
        "forecastHourly": "https://api.weather.gov/gridpoints/SGF/67,35/forecast/hourly",
        "observationStations": "https://api.weather.gov/gridpoints/SGF/67,35/stations",
        ...
    }
}

The most relevant field for our purposes is the forecastHourly URL. Let’s use it to fetch detailed hourly weather data.


Fetching Hourly Forecast Data

The forecastHourly endpoint provides an array of weather data for each hour over the next seven days. Each entry includes:

  • startTime and endTime: The hour’s time range.
  • temperature and temperatureUnit: The forecasted temperature.
  • relativeHumidity: The percentage of humidity. 💧
  • probabilityOfPrecipitation: Chance of rain. ☔
  • windSpeed and windDirection: Wind conditions. 🌬️
  • shortForecast and detailedForecast: Summary descriptions.
  • icon: A URL to an icon representing the forecast. 🖼️

Python Code to Query the forecastHourly Endpoint

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def get_hourly_forecast(forecast_url):
    """
    Fetch hourly forecast data from the given forecast URL.
    """
    headers = {"User-Agent": "WeatherDataPipeline (your_email@example.com)"}
    response = requests.get(forecast_url, headers=headers)
    response.raise_for_status()
    return response.json()

# Extract the forecastHourly URL from point metadata
forecast_hourly_url = point_metadata["properties"]["forecastHourly"]
hourly_forecast = get_hourly_forecast(forecast_hourly_url)
print(hourly_forecast["properties"]["periods"])  # Inspect the forecast periods

Inspecting the forecastHourly Data Structure

Here’s an example of a single period from the forecastHourly response:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
{
    "number": 1,
    "startTime": "2024-11-25T08:00:00-06:00",
    "endTime": "2024-11-25T09:00:00-06:00",
    "isDaytime": true,
    "temperature": 46,
    "temperatureUnit": "F",
    "probabilityOfPrecipitation": {
        "unitCode": "wmoUnit:percent",
        "value": 5
    },
    "relativeHumidity": {
        "unitCode": "wmoUnit:percent",
        "value": 96
    },
    "windSpeed": "13 mph",
    "windDirection": "NW",
    "icon": "https://api.weather.gov/icons/land/day/bkn?size=small",
    "shortForecast": "Mostly Cloudy",
    "detailedForecast": ""
}

Key fields:

  • startTime and endTime: Define the hourly time period.
  • temperature: The forecasted temperature, with the unit (F for Fahrenheit).
  • relativeHumidity: The percentage of humidity in the air.
  • probabilityOfPrecipitation: Chance of rain (in percentages).
  • windSpeed and windDirection: Provide details about wind conditions.
  • shortForecast: A concise summary of expected weather.
  • icon: A URL to an image representing the forecast.

The forecastHourly endpoint returns an array of up to 155 periods, representing a week of hourly forecasts.


Exploring Further

By running this code, you’ll be able to:

  • Extract and examine metadata about a location.
  • Fetch detailed hourly weather data.
  • Understand the structure of the API responses for further use.

This sets the stage for the next article, where we’ll build a FastAPI application to store this data in a PostgreSQL database and create an API to retrieve it. Stay tuned!


In the next article, we’ll transform this weather data into a full-fledged database-driven API!