SDK.
The amanoki package is the Python client for the Amanoki
API, which publishes volatility-regime labels and spike
probabilities on JEPX's 30-minute settlement intervals — the
Japanese regional areas plus the system price, as
get_areas("jepx") lists them — along with forecast
distributions and the Japanese fuel-cost adjustment (燃料費調整).
If you are calling from Python, it saves you writing the request
layer; everything it does is also reachable with a plain
GET against https://api.amanoki.com.
It is a thin wrapper: one method per endpoint, nothing hidden, and
no state kept between calls. Sync (Client) and async
(AsyncClient) carry the same method set and each
method returns the same type in both. Most methods return a typed
dataclass you read by attribute; six return the raw decoded JSON,
which you read by key. The method table marks
which is which, and getting that wrong is the one mistake this
client makes easy — r.regime on a dataclass,
r["probabilities"] on a dict.
The table below is the market side of the API. The fuel-cost
adjustment endpoints under /v1/nencho/ —
tariffs, schedule and
fuel-prices — are reachable with httpx.get
against the same base URL, and are described on
fuel adjustment and in
methodology.
Install
pip install amanoki
Python 3.10+. Transitive deps: httpx only. No
scientific stack at import time; no C extensions.
Methods
One row per endpoint. The Returns column is the one
to read before writing the line that consumes the result: attr
means a typed dataclass, read with r.field; dict
means the decoded JSON, read with r["field"].
| Method | Backs | Returns | What it answers |
|---|---|---|---|
list_markets() | GET /v1/markets | attr (list) | Which markets are catalogued, and which have data. |
get_areas(market) | GET /v1/markets/{m}/areas | list[str] | The area names valid in that market's URLs. |
get_regime(m, a) | GET /v1/power/{m}/{a} | attr | What state this area is in right now, and the price and z-score behind it. |
regime_forecast(m, a, horizon_min=120) | GET /v1/power/{m}/{a}/regime-forecast | dict | Probability of each state at t + horizon, with the model's held-out score alongside. |
spike_probability(m, a) | GET /v1/power/{m}/{a}/spike-probability | attr | How often price has crossed a threshold within 2 or 4 hours, from a moment like this one. |
transitions(m, a, limit=50) | GET /v1/power/{m}/{a}/transitions | attr (list) | The recent state changes, newest first. |
transition_matrix(m, a) | GET /v1/power/{m}/{a}/matrix | dict | How often each state has been followed by each other state. |
regime_durations(m, a) | GET /v1/power/{m}/{a}/durations | dict | How long each state has tended to last once entered. |
history(m, a, from_ms=, to_ms=, limit=500) | GET /v1/power/{m}/{a}/history | dict | Per-bar price and volatility features, oldest first. |
spatial_influence(m, a, target_regime="high", max_lag_bars=4) | GET /v1/power/{m}/{a}/spatial-influence | dict | Which other areas have tended to enter that state before this one does, ranked by lag correlation. |
status() | GET /v1/status | attr | What the service reports about itself — see /status. |
health() | GET /v1/health | dict | Liveness, for a monitor. |
On history: from_ms and to_ms
are UTC milliseconds since the epoch and clip the window inclusively;
limit caps the number of bars returned and the server
rejects anything above 2000 with a 422. Bars come back oldest first,
and limit keeps the newest ones when the window holds
more than it.
On timestamps generally: last_update_ms (on a regime
snapshot), asof_ms (on a forecast) and ts_ms
(on a transition or a history bar) are all UTC milliseconds, and all
of them name the start of the settlement interval being
described rather than the moment we fetched it. Because JEPX is
a day-ahead market, that timestamp is routinely several hours ahead
of the current time, so a staleness check written as
now − ts will read negative in normal operation.
Typed responses
The dataclass-returning methods are the common path, and they are
typed so an editor can complete the field names. A regime snapshot
carries the label, the cleared price and its unit, the z-score behind
the label, and the timestamp of the interval it describes. A spike
table carries one cell per (threshold, horizon) pair, each with the
conditional probability p and the unconditional
base_rate to read it against, plus a
bucket_size on the response saying how many historical
bars fed the (hour × weekend) bucket those cells were counted from —
a bucket under about 100 bars is thin and worth discounting.
from amanoki import Client, PriceRegimeSnapshot, SpikeProbability
c = Client()
r: PriceRegimeSnapshot = c.get_regime("jepx", "tokyo")
print(r.regime, r.price, r.price_unit, r.z_vol, r.last_update_ms)
s: SpikeProbability = c.spike_probability("jepx", "tokyo")
print(s.method, s.sample_size_bars, s.is_weekend)
for cell in s.cells:
print(cell.threshold, cell.horizon_min, cell.p, cell.base_rate)
Async
AsyncClient mirrors Client method for
method. Both mixes of access appear here on purpose:
status() returns a dataclass, so it is read by
attribute, and regime_forecast() returns a dict, so it
is read by key.
import asyncio
from amanoki import AsyncClient
async def main():
async with AsyncClient() as c:
status = await c.status() # dataclass → attribute
print(status.markets_with_data, status.latest_bar_ms)
r = await c.regime_forecast("jepx", "tokyo", horizon_min=120)
print(r["method"], r["probabilities"], r.get("test_brier")) # dict → key
asyncio.run(main())
Errors
Every non-2xx response is raised as an exception rather than returned, so the happy path stays free of status checks. The classes are ordinary Python exceptions; catch the specific one you can act on and let the rest propagate.
import time
from amanoki import (
AmanokiError, AuthError, NotFoundError, RateLimitError, ServerError
)
try:
c.get_regime("jepx", "okinawa") # area not tracked → 404
except NotFoundError as e:
print("404:", e)
except RateLimitError as e:
time.sleep(e.retry_after or 30)
except ServerError:
# transient; retry with exponential backoff
pass
NotFoundError means the market, the area, or the route
is not there; AuthError applies once API keys exist;
RateLimitError carries retry_after in
seconds; ServerError is worth retrying, and
AmanokiError is the base class if you want to catch
everything at once.
All HTTP errors raise an AmanokiError subclass.
The underlying HTTP body is RFC 7807 application/problem+json;
the SDK unwraps it into the exception's message while keeping
status_code and (for 429) retry_after
as structured attributes.
Versioning
SDK major version tracks the API surface, not the service version. Breaking changes to the method list bump the SDK major. New endpoints arrive as minor releases. Current: amanoki 0.1.0.
Source
The SDK is a thin wrapper. If the methods you want aren't
covered yet, the full OpenAPI reference
is the source of truth — every endpoint is callable with
httpx.get against the base URL
https://api.amanoki.com, and the typed dataclasses
can be reconstructed from the JSON without the SDK.