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"].

MethodBacksReturnsWhat it answers
list_markets()GET /v1/marketsattr (list)Which markets are catalogued, and which have data.
get_areas(market)GET /v1/markets/{m}/areaslist[str]The area names valid in that market's URLs.
get_regime(m, a)GET /v1/power/{m}/{a}attrWhat 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-forecastdictProbability of each state at t + horizon, with the model's held-out score alongside.
spike_probability(m, a)GET /v1/power/{m}/{a}/spike-probabilityattrHow 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}/transitionsattr (list)The recent state changes, newest first.
transition_matrix(m, a)GET /v1/power/{m}/{a}/matrixdictHow often each state has been followed by each other state.
regime_durations(m, a)GET /v1/power/{m}/{a}/durationsdictHow long each state has tended to last once entered.
history(m, a, from_ms=, to_ms=, limit=500)GET /v1/power/{m}/{a}/historydictPer-bar price and volatility features, oldest first.
spatial_influence(m, a, target_regime="high", max_lag_bars=4)GET /v1/power/{m}/{a}/spatial-influencedictWhich other areas have tended to enter that state before this one does, ranked by lag correlation.
status()GET /v1/statusattrWhat the service reports about itself — see /status.
health()GET /v1/healthdictLiveness, 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.