Quickstart.

Every endpoint listed here is callable without an API key today — the service is in public preview while the billing stack is wired. Paid tiers will change the rate limit, not the shape.

HTTP (primary surface)

curl https://api.amanoki.com/v1/markets
curl https://api.amanoki.com/v1/status
curl https://api.amanoki.com/v1/power/jepx/tokyo
curl "https://api.amanoki.com/v1/power/jepx/tokyo/regime-forecast?horizon_min=120"
curl https://api.amanoki.com/v1/power/jepx/tokyo/spike-probability
curl "https://api.amanoki.com/v1/power/jepx/tokyo/transitions?limit=10"
curl "https://api.amanoki.com/v1/power/jepx/tokyo/spatial-influence?target_regime=high"

Python SDK

The amanoki package wraps the HTTP surface and returns typed dataclasses. Raw HTTP callers see the same JSON content, accessed by dict key rather than attribute.

pip install amanoki

from amanoki import Client

c = Client()

# SDK style (typed dataclass)
r = c.get_regime("jepx", "tokyo")
print(r.regime, r.price, r.price_unit, r.z_vol)

# Equivalent raw-HTTP shape (dict access)
# r = requests.get("https://api.amanoki.com/v1/power/jepx/tokyo").json()
# print(r["regime"], r["price"], r["price_unit"], r["z_vol"])

s = c.spike_probability("jepx", "tokyo")
for cell in s.cells:
    print(cell.threshold, cell.horizon_min, cell.p, cell.base_rate)

f = c.regime_forecast("jepx", "tokyo", horizon_min=120)
print(f["method"], f["probabilities"], f.get("test_brier"))

ts = c.transitions("jepx", "tokyo", limit=10)
for t in ts:
    print(t.ts_ms, t.from_regime, "->", t.to_regime, "@", t.price)

Async flavour via amanoki.AsyncClient. Full reference at /v1/reference.

Response shape (regime-forecast)

{
  "market": "jepx",
  "area": "tokyo",
  "horizon_min": 120,
  "asof_ms": 1776695400000,
  "method": "weather_conditioned_mlr_v1_5",
  "sample_size_bars": 284112,
  "probabilities": {
    "low": 0.006, "normal": 0.994, "high": 0.0, "scarcity": 0.0
  },
  "base_rates": {
    "low": 0.075, "normal": 0.718, "high": 0.207, "scarcity": 0.0
  },
  "test_brier": 0.0321,
  "baseline_test_brier": 0.4319,
  "weather_fetched_ms": 1776604192971
}

probabilities is the model's prediction at t + horizon_min; base_rates is the unconditional class frequencies from training (context for the model's confidence). test_brier vs baseline_test_brier on the held-out set is the transparent calibration metric — quote both when making decisions.

Response shape (spike-probability)

{
  "market": "jepx",
  "area": "tokyo",
  "hour_of_day_local": 23,
  "is_weekend": false,
  "bucket_size": 1072,
  "method": "empirical_by_hour_and_weekend",
  "sample_size_bars": 36000,
  "cells": [
    {"threshold": 30.0, "horizon_min": 120, "p": 0.0036, "base_rate": 0.014},
    {"threshold": 80.0, "horizon_min": 240, "p": 0.0,    "base_rate": 0.0002}
  ]
}

p is the empirical probability that the maximum price over the next horizon_min minutes crosses threshold, conditioned on the current hour-of-day and weekend flag. bucket_size is how many historical bars fed into that conditional estimate — cells with small buckets (< 100) are noisy and should be discounted.

Error handling

from amanoki import Client, NotFoundError, RateLimitError

try:
    c.get_regime("jepx", "okinawa")  # area not tracked
except NotFoundError:
    ...
except RateLimitError as e:
    time.sleep(e.retry_after or 30)

All error responses ship as application/problem+json (RFC 7807) with {type, title, status, detail, instance}. The type URI points at the relevant section of the methodology so you can read past the symptom to the cause.