Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

This notebook demonstrates how to authenticate with the Spire Weather API and make your first requests.

Prerequisites

You’ll need:

  • A valid Spire Weather API key

  • Python 3.8+

  • The requests library

import os
import requests
from dotenv import load_dotenv

# Load environment variables from .env file if present
load_dotenv()

Setting Up Your API Key

Never commit API keys to version control!

Store your API key in an environment variable or .env file:

export SPIRE_API_KEY="your-api-key-here"

Or create a .env file:

SPIRE_API_KEY=your-api-key-here
# Get API key from environment
API_KEY = os.environ.get('SPIRE_API_KEY')

if not API_KEY:
    raise ValueError(
        "SPIRE_API_KEY environment variable not set. "
        "Please set it before running this notebook."
    )

print("API key loaded successfully!")

API Configuration

# Base URL for all API requests
BASE_URL = 'https://api.wx.spire.com'

# Headers for authentication
HEADERS = {
    'spire-api-key': API_KEY
}

Helper Function

Let’s create a helper function to make API requests:

def spire_request(endpoint, params=None, method='GET', json_data=None):
    """
    Make a request to the Spire Weather API.
    
    Parameters
    ----------
    endpoint : str
        API endpoint (e.g., '/forecast/point')
    params : dict, optional
        Query parameters
    method : str, optional
        HTTP method (GET or POST)
    json_data : dict, optional
        JSON body for POST requests
        
    Returns
    -------
    dict
        JSON response from the API
    """
    url = f"{BASE_URL}{endpoint}"
    
    if method == 'GET':
        response = requests.get(url, headers=HEADERS, params=params)
    elif method == 'POST':
        response = requests.post(url, headers=HEADERS, params=params, json=json_data)
    else:
        raise ValueError(f"Unsupported method: {method}")
    
    # Check for errors
    response.raise_for_status()
    
    return response.json()

Test API Connection

Let’s verify the API connection by requesting a point forecast:

# Test location: Boulder, Colorado
test_params = {
    'lat': 40.0,
    'lon': -105.0,
    'bundles': 'basic'
}

try:
    response = spire_request('/forecast/point', params=test_params)
    print("Connection successful!")
    print(f"Forecast product: {response['meta'].get('forecast', 'N/A')}")
    print(f"Unit system: {response['meta'].get('unit_system', 'N/A')}")
    print(f"Number of forecast times: {len(response['data'])}")
except requests.exceptions.HTTPError as e:
    print(f"API Error: {e}")
    print(f"Response: {e.response.text}")

View Sample Data

# Look at the first forecast record
if response and response.get('data'):
    first_record = response['data'][0]
    
    print("Location:")
    print(f"  Lat: {first_record['location']['coordinates']['lat']}")
    print(f"  Lon: {first_record['location']['coordinates']['lon']}")
    print()
    print("Times:")
    print(f"  Issuance: {first_record['times']['issuance_time']}")
    print(f"  Valid: {first_record['times']['valid_time']}")
    print()
    print("Values:")
    for key, value in first_record['values'].items():
        print(f"  {key}: {value}")

Check Available Files

# List available forecast files
files_response = spire_request('/forecast/file', params={'bundles': 'basic'})

print(f"Available files: {files_response['meta']['count']}")
print()
print("First 5 files:")
for f in files_response['files'][:5]:
    print(f"  {f}")

Error Handling Example

# Example: Handle invalid parameters gracefully
try:
    bad_params = {
        'lat': 999,  # Invalid latitude
        'lon': -105.0
    }
    spire_request('/forecast/point', params=bad_params)
except requests.exceptions.HTTPError as e:
    print(f"Expected error for invalid latitude:")
    print(f"Status code: {e.response.status_code}")
    print(f"Response: {e.response.text}")

Next Steps

Now that you’re authenticated, continue with: