Ister /v1#
Composite regional GPU price indices: each series' official print, once a day at its index's close, in the index's hub (Europe's: 16:30 Europe/London; see Indices). REST and JSON, every route also as CSV. Machine-readable: /v1/openapi.json (OpenAPI 3.1). A key comes from the sign-up page; AI assistants read the same routes through the MCP server.
Quick start#
With a key in ISTER_KEY, one call gives the latest print of every series your tier reads:
curl -H "Authorization: Bearer $ISTER_KEY" "https://api.isterindex.com/v1/series"
The answer is JSON: data, the series, and meta, your tier and the dates it reads. Add format=csv for CSV.
Keys#
Send your key in the header: Authorization: Bearer ister_live_…. Tools that can't set a header (Google Sheets) may put it in the query string as api_key=; anyone who sees that URL can use the key, so give such a tool a key of its own and revoke it alone if it leaks. A key lasts 365 days. A rotated key keeps working 7 days beside its new one. A revoked key is refused from the next call. A call with no key reads Europe's H100 series only (EU), for the 7 days up to the print before the latest: the day's figure, and every other series and index, are for key holders.
Indices#
One index per region, each with its hub and close. Name one with index=; with none, the answer is Europe's.
| Code | Index | Close | Currency | Status |
|---|---|---|---|---|
EU | Europe | 16:30 Europe/London | EUR | Published |
NA | North America | 16:30 America/New_York | USD | Dry run: read behind the login, not yet on keys |
AS | Asia-Pacific | 16:30 Asia/Singapore | USD | Dry run: read behind the login, not yet on keys |
An index's figures reach keys when it leaves its dry run. Planned, not yet calculated: Latin America (SA, hub São Paulo, BRL; its close to be set), the Middle East (ME, hub Dubai, 16:30, AED at the UAE central bank's USD peg) and Africa (AF, 16:30 Africa/Johannesburg, ZAR).
Tiers#
| Tier | Routes | Indices | GPUs | History | As first printed | Calls |
|---|---|---|---|---|---|---|
anonymous | series, history, records |
Europe (EU) | H100 | The 7 days up to the print before the latest | No | 60 an hour |
free | series, history, records |
Every published index | H100, B200 | The latest print and 7 days before it | No | 120 an hour |
paid | series, history, records |
Every published index | Every GPU | All of it, from the first print (27 Oct 2026) | Yes | 1200 an hour |
History before your tier's first date is left out of /v1/history, and meta.readable_from says from when; an Index Record named by an earlier date is a 403. A series or index outside your tier is a 403 too; with none named, an answer gives only your tier's series. print_change on /v1/series is worked within your tier's dates as well: a change from further back, and since, are null. Every tier gets the same fields of an Index Record. The index names no provider: no route here gives a provider's name or price.
GET /series#
The latest print of each series, with its changes. Tiers: anonymous, free, paid. Returns a list of Series, with Meta.
| Parameter | In | What |
|---|---|---|
index | query | Index code (EU, NA, AS…); Europe when none is named; see Indices in /v1/docs. Tiers: anonymous EU; free every published index; paid every published index |
gpu | query | GPU model, e.g. H100. Tiers: anonymous H100; free H100, B200; paid every GPU |
type | query | Series type: OD, T12 or SP |
format | query | json (default) or csv |
curl -H "Authorization: Bearer $ISTER_KEY" "https://api.isterindex.com/v1/series"
GET /history#
Prints per series and date, oldest first. Tiers: anonymous, free, paid. Returns a list of History, with Meta.
| Parameter | In | What |
|---|---|---|
index | query | Index code (EU, NA, AS…); Europe when none is named; see Indices in /v1/docs. Tiers: anonymous EU; free every published index; paid every published index |
gpu | query | GPU model, e.g. H100. Tiers: anonymous H100; free H100, B200; paid every GPU |
type | query | Series type: OD, T12 or SP |
from | query | First date, YYYY-MM-DD |
to | query | Last date, YYYY-MM-DD |
view | query | current (default): as restated; first_printed: each print as it first came out (tiers: paid) |
format | query | json (default) or csv |
curl -H "Authorization: Bearer $ISTER_KEY" "https://api.isterindex.com/v1/history"
GET /records/{date}#
One Index Record: the day's print of every series with its disclosure; latest for the last one. As CSV, one row per series. Tiers: anonymous, free, paid. Returns one Index Record, with Meta.
| Parameter | In | What |
|---|---|---|
date | path | YYYY-MM-DD or latest |
index | query | Index code (EU, NA, AS…); Europe when none is named; see Indices in /v1/docs. Tiers: anonymous EU; free every published index; paid every published index |
format | query | json (default) or csv |
curl -H "Authorization: Bearer $ISTER_KEY" "https://api.isterindex.com/v1/records/latest"
Fields#
The fields of each answer. A field not listed here is never sent.
Series#
| Field | Type | What |
|---|---|---|
code | string | Series code: GPU and type (H100.T12); with an index named, index first (EU.H100.T12) |
index | string | Index code (EU, NA, AS…): see Indices in /v1/docs |
gpu | string | GPU model |
type | string | Series type: OD on-demand, T12 12-month term, SP spot |
date | string (date) | Assessment date |
state | string | printed, held or suspended |
calc | string or null | median or trimmed_average |
value_eur | number or null | Value in EUR per GPU-hour; absent when suspended |
value_usd | number or null | Value in USD per GPU-hour, at the ECB rate of the day |
n | integer | Number of providers in the value |
hub_currency | string or null | The currency of the hub whose close the index is based on (GBP for Europe, London); null before it was given |
value_hub | number or null | Value in the hub currency per GPU-hour, at the ECB rate of the day; null with no rate for it |
countries | array | ISO 3166 countries the providers deliver from |
flags | array | Flags the method shows publicly (Thin, Interpolated, Held, Suspended, Not updated today, All providers in one country) |
input_levels | object | Share of the value from each input level (2 firm offers, 3 indicative quotes) |
vat_not_stated | integer | Rows whose VAT treatment the provider does not state |
form_factor_not_stated | integer | Rows whose form factor the provider does not state |
form_factors | object | Share of the value from each form factor |
last_calculated | string or null (date) | For a held series, the date it was last calculated |
switch_notice | string or null | Notice that the calculation changes if the provider count stays |
both_values | object | Near a change of calculation, the value by each |
v02_value_eur | number or null | The value under method v0.2, beside it while both were shown |
print_change | object | Percentage change from the print 1 day, 1 week, 3, 6 and 12 months back, and since the history starts; null with no print that far back. A tier that reads part of the history works them within it: null from further back, and since always null |
History#
| Field | Type | What |
|---|---|---|
series | string | Series code |
index | string | Index code (EU, NA, AS…): see Indices in /v1/docs |
date | string (date) | Assessment date |
kind | string | Always print for outside callers |
state | string | printed, held or suspended |
value_eur | number or null | Value in EUR per GPU-hour; absent when suspended |
value_usd | number or null | Value in USD per GPU-hour, at the ECB rate of the day |
hub_currency | string or null | The currency of the hub whose close the index is based on (GBP for Europe, London); null before it was given |
value_hub | number or null | Value in the hub currency per GPU-hour, at the ECB rate of the day; null with no rate for it |
n | integer | Number of providers in the value |
flags | array | Flags the method shows publicly (Thin, Interpolated, Held, Suspended, Not updated today, All providers in one country) |
Index Record#
| Field | Type | What |
|---|---|---|
index | string | Index code (EU, NA, AS…): see Indices in /v1/docs |
assessment_date | string (date) | Assessment date |
next_assessment | string or null (date) | Next assessment date, under a weekly method |
methodology_version | string | Method version the print was made under |
fx | object | ECB euro reference rates used, with their date |
rules | object | The method's publication rules in force |
types | object | Series types and their names |
series | object | |
note | string or null | A note on the print, such as a restatement |
Meta#
| Field | Type | What |
|---|---|---|
audience | string | The tier the answer is shaped for: anonymous, free or paid |
as_of | string (date) | Date the answer is as of |
method_version | string | Method version of the answer |
history_start | string (date) | First date of the index's history |
readable_from | string (date) | First date your tier reads |
close | object | The index's close: the local time in its hub its prices are as of (EU 16:30 Europe/London; NA 16:30 America/New_York; AS 16:30 Asia/Singapore) |
fixing_time | string (date-time) | The close on as_of, with its offset from UTC |
view | string | current or first_printed |
next_cursor | string or null | Pass back as cursor for the next page; null at the end |
Errors#
RFC 9457 problem details, as application/problem+json: type, title, status, detail, and code, one of:
| Status | code | When |
|---|---|---|
| 400 | bad_parameter | A parameter is unknown or has a bad value |
| 401 | key_required | No key was sent. The answer has WWW-Authenticate: Bearer |
| 401 | invalid_key | The key is mistyped, not known, revoked or expired |
| 403 | not_in_your_access | The route, date, index or GPU is not in your tier |
| 404 | not_found | No such route, or no print on that date |
| 429 | rate_limited | Too many calls this hour. Retry-After gives the seconds to wait |
| 503 | unavailable | The data can't be read right now; try again later |
As restated, or as first printed#
A print can be restated (method §10). /v1/history gives each date's print as it stands now; view=first_printed gives it as it first came out, which is what a contract settled on that day saw. The tiers table above says which tiers read it; on the others it is a 403.
Caching#
Every answer has an ETag. Send it back as If-None-Match and an unchanged answer is a 304 with no body. Latest figures may be kept 60 seconds, a past print an hour. Answers to a key are private: no shared cache may keep them.
Versions and retirement#
The version is in the path. Within /v1 changes only add: a new route, field or parameter. Anything that would break a caller goes in /v2. Before a route, field or version is retired, its answers carry Deprecation (RFC 9745), the date it was deprecated, and Sunset (RFC 8594), the date it stops, with a Link to the notice, at least 6 months apart.
From your tools#
The same routes from a terminal, Python, Excel or Google Sheets.
curl#
curl -H "Authorization: Bearer $ISTER_KEY" "https://api.isterindex.com/v1/series"
Python#
import io, os, pandas as pd, requests
r = requests.get("https://api.isterindex.com/v1/history", params={"gpu": "H100", "type": "T12", "format": "csv"},
headers={"Authorization": f"Bearer {os.environ['ISTER_KEY']}"}, timeout=30)
r.raise_for_status()
history = pd.read_csv(io.StringIO(r.text), parse_dates=["date"])
print(history.tail())Excel (Power Query)#
Data > Get Data > From Other Sources > Blank Query, then Advanced Editor:
let
Source = Csv.Document(Web.Contents("https://api.isterindex.com/v1/history", [Query = [format = "csv", gpu = "H100"], ApiKeyName = "api_key"])),
Table = Table.PromoteHeaders(Source)
in
Table
When Excel asks for credentials, choose Web API and paste the key. Excel keeps it in its credential store, not in the workbook, and sends it as api_key=.
Google Sheets#
=IMPORTDATA("https://api.isterindex.com/v1/history?format=csv&gpu=H100&api_key=ister_live_…")
Anyone who can see the sheet can see the key: give the sheet its own key.
Terms#
Use of the data is under the terms you accept when you get a key. Quote a number with "Source: Ister".