Theta Meteorology
Free tier · Sign up in 30 seconds

Weather forecasts that state how wrong they might be.

A daily and seasonal forecast API for the contiguous US. Every value returns as a calibrated distribution — mean, sigma, and confidence bands we verify in public — with the source named on every number. Price a weather risk, size a position, or schedule a crew against the full distribution, not a point estimate.

1,000 free calls / month · No card required WFO-anchored, contiguous US
POST /v1/forecast/point
200 OK · 78 ms
{
  "succeeded": true,
  "data": {
    "station_id": "NYC",
    "network":    "NY_ASOS",
    "daily": [
      {
        "date": "2026-06-21", "kind": "high",
        "prediction_interval": {       // p50 is the point estimate
          "p10": 77.7, "p25": 80.2, "p50": 82.1,
          "p75": 84.0, "p90": 86.5, "sigma": 3.4
        },
        "provenance": {
          "source":      "nws+climo_sigma",
          "model_cycle": "12Z",
          "station_id":  "NYC", "network": "NY_ASOS"
        }
      }
    ],
    "generated_at": "2026-06-20T18:22:04Z"
  },
  "credits_remaining": 9847.0
}

Live forecast explorer Every forecast is a distribution. Hover any of the 19 calibration stations for the live high and low you'd plan against — sun, sky, and precipitation show typical climatology for now.

Hover or tap a station to read its forecast distribution

Calibration receipts

We back-tested 83,294 forecasts. Here's how the intervals did.

Walk-forward over six years × nineteen contiguous-US stations × daily high and low temperatures. No data leakage: for each evaluated day, σ was fit on records strictly before that day. Most weather APIs ship a number. We ship the number, the confidence range around it, and the proof the range is honest.

91.0%

Empirical coverage

on nominal 90% intervals — target 90%

80.9%

Empirical coverage

on nominal 80% intervals — target 80%

0.7 pp

Calibration error

mean |empirical − nominal| over both bands

19 stations · 2020–2025 · daily high & low · walk-forward σ fit on prior history only · backtest artifacts in the repo

What you get

A forecast with a confidence range, every time.

A single forecast number isn't enough to plan against. Theta Weather returns the full picture: a central estimate, a confidence range, and the source the value came from, in one consistent response shape across six endpoints.

Confidence ranges you can audit

Every value carries a mean, a standard deviation, and a p05–p95 confidence interval calibrated against decades of station observations. The verification endpoint lets you replay the model against historical windows so the bands stop being a black box.

Source named on every value

Every day in every response identifies the source, the Weather Forecast Office covering the location, and the model cycle that produced it. When the engine falls back to climatology, the response says so. No silent substitutions.

Anchored to NOAA Weather Forecast Offices

Forecasts pull from the same gridpoint guidance the NWS publishes, station by station, the same source emergency managers and broadcasters rely on. Climatology and a 30-year observational baseline cover what the forecast cycle doesn't.

Endpoints

Six endpoints. One response shape.

All endpoints return the same confidence-range envelope and the same source fields, so you write the integration once. Credit cost scales with how much work the request does. Coverage is the contiguous US for v0; Alaska, Hawaii, and US territories are on the roadmap.

POST /v1/climatology Daily-temperature climatology What's typical for this day of year at this station, with a calibrated p05–p95 range fit on 30 years of observations. 1 credit
POST /v1/forecast/point Point forecast with WFO source Daily high and low for a station or coordinate. Every day names the Weather Forecast Office, the model cycle, and the issue time it came from. 1 credit
POST /v1/history/observations Observed history with rolling statistics Quality-flagged daily observations for any date range, from the same ASOS record we calibrate against — returned alongside a rolling summary of the trailing window. Mean, σ, min and max on every plan; skew, kurtosis and IQR for tail risk from Basic up, where you can also move the 30-day window. 5 credits
POST /v1/forecast-vs-actual Forecast verification — our receipts, on demand Every past forecast for a station against what actually happened. Each row carries the interval we published, the observed value, the residual, and whether the actual landed inside our p80 and p90 bands. The response closes with rolling empirical coverage and calibration error for the window you asked for. 10 credits
POST /v1/window/compare · Pro+ Window comparison and regime detection Compare two date ranges at one station: Kolmogorov–Smirnov for a change in the whole distribution, Mann–Whitney for a shift in level, one-way ANOVA for the means. Add a second station for Granger causality, or ask for a Gaussian mixture fit to see whether a window is one population or several. 10 credits
POST /v1/seasonal Seasonal outlook Monthly mean with a tight confidence range over a 1–12 month horizon, from daily climatology with a 5-day autocorrelation correction on the uncertainty. Warming-trend and ENSO components are on the roadmap. 2 credits

Worked example

From distribution to probability.

A point forecast can't answer "what are the odds the high lands between 86 and 88°F?" A calibrated distribution can — integrate it over the bracket. That one line is how traders price temperature markets, how utilities weight load scenarios, and how parametric products set triggers. The calibration record is what makes the number usable: probability from an uncalibrated distribution is just formatting.

PY bracket_probability.py
stdlib only
# P(lo <= high_f < hi) from one /v1/forecast/point response
from math import erf, sqrt

def bracket_p(mu, sigma, lo, hi):
    cdf = lambda x: 0.5 * (1 + erf((x - mu) / (sigma * sqrt(2))))
    return cdf(hi) - cdf(lo)

# from the response above: prediction_interval.p50 and .sigma
pi = r["data"]["daily"][0]["prediction_interval"]
bracket_p(pi["p50"], pi["sigma"], 86.0, 88.0)  # => 0.084 — an 8.4% bracket

Compare that 8.4% to whatever the bracket is quoted at — a market price, an insurance trigger, a load-planning threshold — and you have a decision, not a vibe. Works identically for lows, seasonal windows, and every station we cover.

Docs

Everything you need, on one page.

Six POST endpoints, one auth header, one response envelope. No SDK required and no client library to wait on — if you can send JSON, you are done.

Base URL and authentication

Send your key as the X-Theta-Key header on every request. Keys are shown once, at mint time, in your account. There is no OAuth flow and no token exchange.

SH first-call.sh
copy-paste ready
# Base URL
https://api.weather.oasis-x.io

curl -X POST https://api.weather.oasis-x.io/v1/forecast/point \
  -H "X-Theta-Key: $THETA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"body": {"station_id": "NYC", "horizon_days": 7}}'

# Note the "body" wrapper — request fields go inside it, on every
# endpoint. Sending them at the top level returns 422 with
# {"loc": ["body","body"], "msg": "Field required"}.

Choosing a station

Every endpoint accepts either station_id (with optional network), or a lat/lon pair that we snap to the nearest covered station. Coverage is the contiguous US in v0. The station we actually used is echoed back in provenance, so a snapped coordinate is never ambiguous.

The six endpoints

POST /v1/climatology Body: kind ("high" or "low", default "high"), start_date, end_date Returns days[], each with climo_value_f, residual_sigma_f, a prediction_interval, provenance, and n_history_records. 1 credit
POST /v1/forecast/point Body: horizon_days (default 7) Returns daily[] with a calibrated prediction_interval per day, plus hourly[]. Hourly points carry a raw NWS value with prediction_interval: null and provenance.source: "nws_hourly_uncalibrated" — the calibration is daily, and we will not dress up an hourly number as calibrated. 1 credit
POST /v1/seasonal Body: kind, horizon_months (1–12, default 3) Returns monthly mean_f with a prediction_interval per period. 2 credits
POST /v1/history/observations Body: kind, start_date, end_date, window_days (default 30) Returns observations[] with value_f, quality_flag and source — the same ASOS record we calibrate against — plus a rolling summary of the trailing window_days. Every plan gets n, mean, sigma, min, max. Basic and above also get skew, kurtosis, p25, p75, iqr, and may set window_days. On the free plan the window is served at 30 and rolling_window_days tells you so — we will not quietly summarise a different span than you asked for. 5 credits
POST /v1/window/compare · Pro+ Body: window_a_start/end, window_b_start/end, optional granger_station_id, granger_max_lag (default 7), include_mixture Returns tests with Kolmogorov–Smirnov, Mann–Whitney and one-way ANOVA — each carrying a statistic, a p-value and the question it answers. ANOVA also states its assumptions, so when it disagrees with Mann–Whitney you know which to trust.

Supply granger_station_id and you also get granger, tested in both directions. The series are deseasonalized first and the lag is chosen by AIC; both are reported. Each result carries measured_false_positive_rate — we measured our own method at 9% against a nominal 5%, and 100% without the deseasonalization step, so you can size how much to trust a p-value.

Set include_mixture for a 1–3 component Gaussian fit per window, chosen by AIC, with weights, means, sigmas, the AIC of every candidate, and a KS goodness-of-fit. Components are returned unlabelled on purpose: naming one "trend" or "ENSO" would be a claim about the atmosphere, not a property of the fit.
10 credits
POST /v1/forecast-vs-actual Body: kind, start_date, end_date Our receipts. Each row pairs the forecast interval with actual_value_f, residual_f, and the booleans in_p80 / in_p90. The response closes with rolling_coverage_p80, rolling_coverage_p90, and rolling_calibration_error. Check our numbers yourself, over any window you like. 10 credits

Response shape

Every success returns { succeeded, message, data, credits_remaining }. The endpoint-specific payload is always under data. Your running balance also comes back in the X-Theta-Credits-Remaining response header, so you can track burn without a side-channel counter. Every calibrated value is a prediction_interval: p10, p25, p50, p75, p90 always, with p05, p95, and sigma where available. p50 is the point estimate — if you want a single number, that is the one.

Errors

401 Missing or invalid keyCheck the X-Theta-Key header. Revoked keys also return 401. no charge
402 Out of credits, or a feature above your planTwo cases, told apart by error: insufficient_credits means top up, feature_not_in_tier means upgrade. Both carry current_tier and an upgrade URL; the second also names the feature and the required_tier. Nothing is debited on either — a request we refuse is a request you do not pay for. no charge

Credits debit only on success. A request that errors costs you nothing. If something here does not match what the API returns, that is a bug and we want to hear about it — tell us.

Pricing

Start free. Scale by call volume.

The calibrated intervals, the per-day provenance and the verification receipts are the same on every plan, free included — we would rather you evaluate the real thing than a crippled version of it. What the paid tiers add is call volume, more keys, and the statistical surface area on top: higher moments from Basic, window comparison and mixture fits from Pro. Credits debit per call, weighted by endpoint cost. Plans renew monthly; unused credits do not roll over. Full breakdown on the pricing page.

Free

$0 /mo

  • 1,000 credits / mo · 1 API key
  • Five core endpoints
  • Calibrated intervals + per-day provenance on every value
  • Rolling mean, σ, min & max on history
Start free

Basic

$29 /mo

  • 10,000 credits / mo · 1 API key
  • Everything in Free, plus:
  • Rolling higher moments — skew, kurtosis, IQR for tail risk
  • Move the 30-day rolling window to any span you need
Subscribe

Team

$499 /mo

  • 300,000 credits / mo · 25 API keys
  • Everything in Pro, plus:
  • 25 keys — one per client, service, or desk
  • Shared Slack channel
Subscribe

Enterprise

Custom

  • Custom credits · 100+ API keys
  • SLA + dedicated WFO-region routing
  • Custom priors & hierarchical models, scoped per engagement
  • Direct engineering contact
Contact sales

Compare all features across tiers →

Get a free API key.

Pick a sign-in email and password. You'll get your first key on the next screen, ready to call the five core endpoints right away — window comparison is on Pro and up.