API Documentation

Introduction

Welcome to the Wethr.net Weather Data API! This API provides programmatic access to historical weather observation data and model forecast data. To use this API, you need an API key associated with your Wethr.net account.

Base URL: https://wethr.net (The specific endpoint path is listed below)


Authentication

All requests to the Wethr.net API must be authenticated using an API key associated with your account.

  1. Obtaining an API Key: Log in to your Wethr.net account and navigate to the My Account page. You can generate and manage your API keys in the "API Keys" section. Please copy your key immediately upon generation, as it will not be shown again.
  2. Using the API Key: Include your API key in the HTTP request header. Two methods are supported:
    • Bearer Token (Recommended):

      Authorization: Bearer <YOUR_API_KEY>
    • Custom Header:

      X-API-Key: <YOUR_API_KEY>
    Replace <YOUR_API_KEY> with the actual key you generated.

Failure to provide a valid, enabled API key will result in a 401 Unauthorized error.


Rate Limiting

To ensure fair usage and server stability, API requests are rate-limited based on the subscription tier associated with your API key. Limits are applied per API key.

Tier Requests per Minute Requests per Day
Professional 60 5,000
Enterprise 300 50,000

Note: Daily limits reset based on a fixed time window (e.g., UTC midnight). Per-minute limits use a sliding window where possible.

If you exceed the rate limit for your key's tier, the API will respond with an HTTP 429 Too Many Requests error. You may receive a Retry-After header indicating when you can make another request.


Error Responses

All endpoints use the following error response formats.

  • 400 Bad Request: Problem with the request parameters.

    Example Body:

    {
        "error": "Missing required parameters.",
        "details": [
            "start_time is required."
        ]
    }
  • 401 Unauthorized: API key is missing, invalid, or disabled.

    Example Body:

    {
        "error": "Invalid or disabled API key."
    }
  • 429 Too Many Requests: Rate limit exceeded for the API key's tier.

    Example Body:

    {
        "error": "Rate limit exceeded",
        "limit_per_minute": 60,
        "retry_after_seconds": 58
    }

    Includes a Retry-After header indicating seconds to wait.

  • 500 Internal Server Error: An unexpected error occurred on the server.

    Example Body:

    {
        "error": "An unexpected server error occurred."
    }

Weather Observations API

Endpoint: Get Observations

GET /api/v1/observations.php

This single endpoint supports three distinct modes for retrieving data:

  1. **History:** Retrieve a time series of observations over a date range. (Requires `start_time` & `end_time` - default mode if `mode` is omitted).
  2. **Latest:** Retrieve the most recent single observation. (Requires `mode=latest`).
  3. **Wethr High:** Retrieve the current calculated "Wethr High" for the trading day. (Requires `mode=wethr_high`).

Parameters (Query String)

Parameter Type Required Mode(s) Description Example
station_code string Yes All The unique identifier for the weather station (3-10 alphanumeric characters). KDTO
mode string No Latest, Wethr High Controls the type of response. If omitted, defaults to history mode.
  • history: Requires start_time/end_time.
  • latest: Returns single most recent observation.
  • wethr_high: Returns the calculated trading-day high.
wethr_high
start_time string Conditional History only The beginning of the observation time range (inclusive). ISO 8601 format recommended (UTC 'Z' preferred). 2025-04-18T00:00:00Z
end_time string Conditional History only The end of the observation time range (inclusive). ISO 8601 format recommended. Must be after start_time. 2025-04-18T06:00:00Z

Request Headers (Same for all modes)

Header Required Description Example
Authorization Yes (or X-API-Key) Contains the API key as a Bearer token. Bearer <YOUR_API_KEY>
X-API-Key Yes (or Authorization) Contains the API key directly. <YOUR_API_KEY>
Accept No (recommended) Informs the server the client expects JSON. application/json

Mode: Latest Observation (`mode=latest`)

Returns a single JSON object representing the most recently available observation from the station. The structure follows the standard **Observation Data Model**.

Example Request: /api/v1/observations.php?station_code=KDTO&mode=latest

Example Success Body (Single Observation Object):

{
    "id": 12345,
    "station_code": "KDTO",
    "temperature": 30.50,
    // ... all other observation fields ...
    "lowest_probable_f": 87,
    "highest_probable_f": 87,
    "relative_humidity": 60.33,
    "heat_index_f": 92.55
}

Mode: Wethr High Calculation (`mode=wethr_high`)

Calculates and returns the current "Wethr High" for the station's active trading day. This value is determined by iterating through the current day's available data, prioritizing official reports (6-hour high, CLI, DSM) and current observations according to market-specific rules (including DST adjustments).

Successful Response Model (200 OK)

Field Type Description
station_codestringIdentifier of the reporting station.
datestringThe local trading day the high was calculated for (e.g., `YYYY-MM-DD`).
wethr_highinteger / nullThe highest confirmed temperature in Fahrenheit (°F) for the current trading day, calculated to the nearest integer. Null if no data is available for the current day.
time_of_high_utcstring (datetime) / nullThe UTC timestamp of the observation that established the wethr_high value. Null if no high is set.
calculation_logicstringIndicates the rule set used: standard (Kalshi) or polymarket (KLGA, EGLC).

Example Request: /api/v1/observations.php?station_code=KMDW&mode=wethr_high

Example Success Body:

{
    "station_code": "KMDW",
    "date": "2025-11-18",
    "wethr_high": 75,
    "time_of_high_utc": "2025-11-18 20:00:00",
    "calculation_logic": "standard"
}

Observation Data Model (History / Latest Modes)

Represents a single weather observation record returned by the API.

Field Type Description
idintegerUnique identifier for the observation record.
station_codestringIdentifier of the reporting station.
temperaturenumber (decimal)Temperature in Celsius.
six_hour_highnumber (decimal)Highest temperature in the previous 6 hours (Celsius).
twentyfour_hour_highnumber (decimal)Highest temperature in the previous 24 hours (Celsius).
cli_highnumber (decimal)Climate day high temperature (calendar day, Celsius).
dsm_highnumber (decimal)Midnight-to-midnight high temperature (Celsius).
precision_levelintegerIndicator of data precision/source type.
observation_timestring (datetime)Timestamp of the observation (UTC, e.g., YYYY-MM-DD HH:MM:SS).
dew_pointnumber (decimal)Dew point temperature (Celsius).
wind_directionstringWind direction (e.g., degrees "180", "VRB").
wind_speednumber (decimal)Wind speed (knots).
wind_gustnumber (decimal)Wind gust (knots), if any.
lowest_probable_fintegerThe calculated minimum probable temperature in Fahrenheit, based on the precision of the source Celsius reading. Null if source temperature is null.
highest_probable_fintegerThe calculated maximum probable temperature in Fahrenheit, based on the precision of the source Celsius reading. Null if source temperature is null.
relative_humiditynumber (decimal)Calculated relative humidity in percent (%). Null if source temperature or dew point is null.
heat_index_fnumber (decimal)Calculated heat index in Fahrenheit (°F). Null if temperature is below 80°F, humidity is below 40%, or source data is missing.
visibilitynumber (decimal)Visibility in statute miles.
altimeternumber (decimal)Altimeter setting in inches of mercury (inHg).
sea_level_pressurenumber (decimal)Sea level pressure in hectopascals (hPa), also known as millibars (mb).
pressure_tendencynumber (decimal)The pressure change in the last 3 hours, in hectopascals (hPa/mb). Can be positive, negative, or zero.
precipitationnumber (decimal)Liquid-equivalent precipitation in the **last hour**, in inches.
precipitation_3hrnumber (decimal)Total liquid-equivalent precipitation over the **last 3 hours**, in inches.
precipitation_6hrnumber (decimal)Total liquid-equivalent precipitation over the **last 6 hours**, in inches.
precipitation_24hrnumber (decimal)Total liquid-equivalent precipitation over the **last 24 hours**, in inches.
snownumber (decimal)Snow depth on the ground, in inches.

Note: Unless specified otherwise, temperature units are in Celsius and wind speed units are in knots.

Observation Client Examples

Replace <YOUR_API_KEY> with your actual API key and adjust parameters as needed.

cURL (History Mode - Original)

# Get Observations (History Mode - Requires start_time and end_time)
curl -G "https://wethr.net/api/v1/observations.php" \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  --data-urlencode "station_code=KDTO" \
  --data-urlencode "start_time=2025-04-18T00:00:00Z" \
  --data-urlencode "end_time=2025-04-18T06:00:00Z"

cURL (Latest Observation Mode)

# Get Latest Single Observation
curl -G "https://wethr.net/api/v1/observations.php" \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  --data-urlencode "station_code=KDTO" \
  --data-urlencode "mode=latest"

cURL (Wethr High Mode)

# Get Current Wethr High Calculation
curl -G "https://wethr.net/api/v1/observations.php" \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  --data-urlencode "station_code=KDTO" \
  --data-urlencode "mode=wethr_high"

JavaScript (fetch - Wethr High Example)

const apiKey = '<YOUR_API_KEY>';
const station = 'KMDW';

const params = new URLSearchParams({
    station_code: station,
    mode: 'wethr_high'
});

const url = `/api/v1/observations.php?${params.toString()}`;

fetch(url, {
    method: 'GET',
    headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Accept': 'application/json'
    }
})
.then(response => {
    if (!response.ok) {
        return response.json().catch(() => response.text()).then(errData => Promise.reject(errData));
    }
    return response.json();
})
.then(data => {
    console.log(`Current Wethr High for ${data.station_code}: ${data.wethr_high}°F`);
})
.catch(error => {
    console.error("API Request Failed:", error);
});

PHP (cURL - Latest Observation Example)

<?php

$apiKey = '<YOUR_API_KEY>';
$station = 'KDTO';

$queryParams = http_build_query([
    'station_code' => $station,
    'mode' => 'latest'
]);

$url = "https://wethr.net/api/v1/observations.php?" . $queryParams;

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer " . $apiKey,
    "Accept: application/json"
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($httpCode === 200) {
    $observation = json_decode($response, true);
    if (json_last_error() === JSON_ERROR_NONE) {
        echo "Latest observation time: " . ($observation['observation_time'] ?? 'N/A') . "\n";
        echo "Current Temp Range: {$observation['lowest_probable_f']} - {$observation['highest_probable_f']} F\n";
    }
} else {
    echo "Request failed with status code: {$httpCode}\n";
}

?>

Weather Forecasts API

Get Weather Forecasts

GET /api/v1/forecasts.php

Retrieves a list of model forecast data for a specific location within a defined time range.

Parameters (Query String)

Parameter Type Required Description Example
location_name string Yes The unique identifier for the forecast location (3-10 alphanumeric characters). KMDW
start_valid_time string Yes The beginning of the forecast valid time range (inclusive). ISO 8601 format recommended (UTC 'Z' preferred). 2025-04-09T18:00:00Z
end_valid_time string Yes The end of the forecast valid time range (inclusive). ISO 8601 format recommended. Must be after start_valid_time. 2025-04-09T20:00:00Z
model string No The forecast model to query. If omitted, results from all available models are returned.
Available options (Run Interval UTC):
  • HRRR: Hourly
  • RAP: Hourly
  • LAV-MOS: Hourly
  • GFS: 6 Hours (00, 06, 12, 18Z)
  • GFS-MOS: 6 Hours (00, 06, 12, 18Z)
  • NAM: 6 Hours (00, 06, 12, 18Z)
  • NAM4KM: 6 Hours (00, 06, 12, 18Z)
  • NAM-MOS: 12 Hours (00, 12Z)
  • NBS-MOS: Varies
HRRR

Successful Response (200 OK)

Returns a JSON array containing forecast objects matching the query. If no forecasts match, returns an empty array [].

  • Content-Type: application/json
  • Body: Array<Forecast>

Example Success Body:

[
    {
        "id": 1,
        "model": "HRRR",
        "location_name": "KMDW",
        "latitude": "41.784100",
        "longitude": "-87.755100",
        "run_time": "2025-04-09 18:00:00",
        "valid_time": "2025-04-09 18:00:00",
        "forecast_hour": 0,
        "temperature_k": "277.77",
        "temperature_f": "40.31",
        "temperature_c": "4.62",
        "inserted_at": "2025-04-09 15:14:31"
    },
    {
        "id": 10,
        "model": "HRRR",
        "location_name": "KMDW",
        "latitude": "41.784100",
        "longitude": "-87.755100",
        "run_time": "2025-04-09 18:00:00",
        "valid_time": "2025-04-09 18:15:00",
        "forecast_hour": 1,
        "temperature_k": "276.98",
        "temperature_f": "38.90",
        "temperature_c": "3.83",
        "inserted_at": "2025-04-09 15:14:31"
    }
]

Forecast Data Model

Represents a single model forecast record returned by the API.

Field Type Description
idintegerUnique identifier for the forecast record.
modelstringIdentifier of the forecast model (e.g., "HRRR").
location_namestringIdentifier of the forecast location.
latitudenumber (decimal)Latitude of the forecast point.
longitudenumber (decimal)Longitude of the forecast point.
run_timestring (datetime)Timestamp when the model forecast was generated (UTC).
valid_timestring (datetime)Timestamp the forecast is valid for (UTC).
forecast_hourintegerThe forecast lead time in hours from the run_time.
temperature_knumber (decimal)Temperature in Kelvin (K).
temperature_fnumber (decimal)Temperature in Fahrenheit (°F).
temperature_cnumber (decimal)Temperature in Celsius (°C).
inserted_atstring (datetime)Timestamp when the record was inserted into the database.

Forecast Client Examples

Replace <YOUR_API_KEY> with your actual API key and adjust parameters as needed.

cURL

# Get Forecasts (for HRRR model)
curl -G "https://wethr.net/api/v1/forecasts.php" \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  --data-urlencode "location_name=KMDW" \
  --data-urlencode "start_valid_time=2025-04-09T18:00:00Z" \
  --data-urlencode "end_valid_time=2025-04-09T20:00:00Z" \
  --data-urlencode "model=HRRR"

JavaScript (fetch)

const apiKey = '<YOUR_API_KEY>';
const location = 'KMDW';
const startTime = '2025-04-09T18:00:00Z';
const endTime = '2025-04-09T20:00:00Z';

const params = new URLSearchParams({
    location_name: location,
    start_valid_time: startTime,
    end_valid_time: endTime,
    model: 'HRRR' // Optional: remove this line for all models
});

const url = `/api/v1/forecasts.php?${params.toString()}`;

fetch(url, {
    method: 'GET',
    headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Accept': 'application/json'
    }
})
.then(response => {
    if (!response.ok) {
        return response.json().catch(() => response.text()).then(errData => Promise.reject(errData));
    }
    return response.json();
})
.then(data => {
    console.log("Forecasts:", data);
})
.catch(error => {
    console.error("API Request Failed:", error);
});

PHP (cURL)

<?php

$apiKey = '<YOUR_API_KEY>';

$queryParams = http_build_query([
    'location_name' => 'KMDW',
    'start_valid_time' => '2025-04-09T18:00:00Z',
    'end_valid_time' => '2025-04-09T20:00:00Z',
    'model' => 'HRRR' // Optional: remove this line for all models
]);

$url = "https://wethr.net/api/v1/forecasts.php?" . $queryParams;

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer " . $apiKey,
    "Accept: application/json",
    "User-Agent: WethrNet-PHPScript/1.0"
]);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlErrorNum = curl_errno($ch);

curl_close($ch);

if ($curlErrorNum > 0) {
    echo "cURL Error ({$curlErrorNum}): " . curl_error($ch);
} elseif ($httpCode >= 400) {
    echo "HTTP Error: [{$httpCode}]\n";
    echo "Response Body: [" . $response . "]\n";
} else {
    echo "Success! HTTP Code: [{$httpCode}]\n";
    $forecasts = json_decode($response, true);
    if (json_last_error() === JSON_ERROR_NONE) {
        print_r($forecasts);
    } else {
        echo "Failed to decode JSON response.";
    }
}

?>

Python (requests)

import requests
import json

api_key = '<YOUR_API_KEY>'
api_url = 'https://wethr.net/api/v1/forecasts.php'

params = {
    'location_name': 'KMDW',
    'start_valid_time': '2025-04-09T18:00:00Z',
    'end_valid_time': '2025-04-09T20:00:00Z',
    'model': 'HRRR' # Optional: remove this line for all models
}

headers = {
    'Authorization': f'Bearer {api_key}',
    'Accept': 'application/json',
    'User-Agent': 'WethrNet-PythonScript/1.0'
}

try:
    response = requests.get(
        api_url,
        headers=headers,
        params=params,
        timeout=15
    )
    response.raise_for_status() # Raise HTTPError for bad responses

    forecasts = response.json()
    print(f"Success! Found {len(forecasts)} forecast points.")
    if forecasts:
        print(json.dumps(forecasts[0], indent=2))

except requests.exceptions.HTTPError as e:
    print(f"HTTP Error: {e.response.status_code}")
    print(f"Response Body: {e.response.text}")
except requests.exceptions.RequestException as e:
    print(f"An error occurred: {e}")

Top