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.
- 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.
- 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>
<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-Afterheader 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
/api/v1/observations.phpThis single endpoint supports three distinct modes for retrieving data:
- **History:** Retrieve a time series of observations over a date range. (Requires `start_time` & `end_time` - default mode if `mode` is omitted).
- **Latest:** Retrieve the most recent single observation. (Requires `mode=latest`).
- **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.
|
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_code | string | Identifier of the reporting station. |
date | string | The local trading day the high was calculated for (e.g., `YYYY-MM-DD`). |
wethr_high | integer / null | The 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_utc | string (datetime) / null | The UTC timestamp of the observation that established the wethr_high value. Null if no high is set. |
calculation_logic | string | Indicates 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 |
|---|---|---|
id | integer | Unique identifier for the observation record. |
station_code | string | Identifier of the reporting station. |
temperature | number (decimal) | Temperature in Celsius. |
six_hour_high | number (decimal) | Highest temperature in the previous 6 hours (Celsius). |
twentyfour_hour_high | number (decimal) | Highest temperature in the previous 24 hours (Celsius). |
cli_high | number (decimal) | Climate day high temperature (calendar day, Celsius). |
dsm_high | number (decimal) | Midnight-to-midnight high temperature (Celsius). |
precision_level | integer | Indicator of data precision/source type. |
observation_time | string (datetime) | Timestamp of the observation (UTC, e.g., YYYY-MM-DD HH:MM:SS). |
dew_point | number (decimal) | Dew point temperature (Celsius). |
wind_direction | string | Wind direction (e.g., degrees "180", "VRB"). |
wind_speed | number (decimal) | Wind speed (knots). |
wind_gust | number (decimal) | Wind gust (knots), if any. |
lowest_probable_f | integer | The calculated minimum probable temperature in Fahrenheit, based on the precision of the source Celsius reading. Null if source temperature is null. |
highest_probable_f | integer | The calculated maximum probable temperature in Fahrenheit, based on the precision of the source Celsius reading. Null if source temperature is null. |
relative_humidity | number (decimal) | Calculated relative humidity in percent (%). Null if source temperature or dew point is null. |
heat_index_f | number (decimal) | Calculated heat index in Fahrenheit (°F). Null if temperature is below 80°F, humidity is below 40%, or source data is missing. |
visibility | number (decimal) | Visibility in statute miles. |
altimeter | number (decimal) | Altimeter setting in inches of mercury (inHg). |
sea_level_pressure | number (decimal) | Sea level pressure in hectopascals (hPa), also known as millibars (mb). |
pressure_tendency | number (decimal) | The pressure change in the last 3 hours, in hectopascals (hPa/mb). Can be positive, negative, or zero. |
precipitation | number (decimal) | Liquid-equivalent precipitation in the **last hour**, in inches. |
precipitation_3hr | number (decimal) | Total liquid-equivalent precipitation over the **last 3 hours**, in inches. |
precipitation_6hr | number (decimal) | Total liquid-equivalent precipitation over the **last 6 hours**, in inches. |
precipitation_24hr | number (decimal) | Total liquid-equivalent precipitation over the **last 24 hours**, in inches. |
snow | number (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
/api/v1/forecasts.phpRetrieves 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 |
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 |
|---|---|---|
id | integer | Unique identifier for the forecast record. |
model | string | Identifier of the forecast model (e.g., "HRRR"). |
location_name | string | Identifier of the forecast location. |
latitude | number (decimal) | Latitude of the forecast point. |
longitude | number (decimal) | Longitude of the forecast point. |
run_time | string (datetime) | Timestamp when the model forecast was generated (UTC). |
valid_time | string (datetime) | Timestamp the forecast is valid for (UTC). |
forecast_hour | integer | The forecast lead time in hours from the run_time. |
temperature_k | number (decimal) | Temperature in Kelvin (K). |
temperature_f | number (decimal) | Temperature in Fahrenheit (°F). |
temperature_c | number (decimal) | Temperature in Celsius (°C). |
inserted_at | string (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}")