European gas storage data, ready for your workflow
gas-risiko.de publishes structured, machine-readable JSON data covering daily storage levels, 14-day ARIMAX forecasts with full quantile bands, 365-day seasonal outlooks, cross-border flow z-scores, and refill stress scenarios — updated every morning from primary sources.
All datasets show export freshness versus the JSON build time. Production runs twice daily on Europe/Berlin time (≈06:00 morning refresh and ≈23:45 after AGSI+ evening publication), sourcing AGSI+, ENTSO-G, Open-Meteo, SMARD, and TTF. A GitHub Actions backup may run around 00:30 UTC. Amber in the table can mean an upstream source is still flagged stale even when the export is recent.
Two tiers, one clear boundary
Full public dashboard for all 7 countries. Best-effort data, no account required.
Higher tiers (The Strategic Hub · The Enterprise Grid) with forecast vintages / as-of API, PDF reports, webhook alerts, and raw Monte Carlo paths are in development. Contact info@mh-analytics.eu for early interest.
Five structured feeds
/api/data?country=DEfill_level_pctstorage_pct_rankrequired_injection_rate_ppdayP10/P25/P50/P75/P90p_alarmsource_statusseasonal_profilestorage_type_breakdowncoefficientsdiagnostics/api/annual?country=DEbaseline p10/p50/p90standardised scenarioswinter_minimum (OLS)p_breach_15pctood_boundsscenario_translation_metaess_paths/api/scenarios?country=DEcategory / severityparametersdelta_winter_min_ppdelta_se_at_winter_mincorridor_metadatascenario_translation_meta/api/network?country=DEflow_gwhcapacity_gwhutilization_pcttrailing_30d_meanzscore_30dis_lngstress_flag/api/refill-stress?country=DEsensitivity_gridcritical_boundaryoct31_fill_pcttarget_shortfall_twhdays_below_15seasonal_ratesInspect a real payload
All responses are versioned JSON with a contract_version or endpoint-specific contract-version field. Quantiles are always monotone; NaN values are serialised as null.
{
"contract_version": "1.0",
"generated_at": "2026-04-26T04:07:33.912Z",
"latest": {
"date": "2026-04-24",
"fill_level_pct": 37.42,
"gas_in_storage_twh": 182.1,
"storage_pct_rank": 31.4,
"required_injection_rate_ppday": 0.284,
"d_current": 0.91
},
"predictions": [
{ "date": "2026-04-25", "P10": 36.8, "P50": 37.6, "P90": 38.4 },
{ "date": "2026-04-26", "P10": 36.2, "P50": 37.1, "P90": 38.0 }
],
"seasonal_profile": {
"120": { "p10": 44.8, "p50": 56.1, "p90": 66.8 }
},
"storage_type_breakdown": {
"country": "DE",
"cavern_fraction": 0.49,
"pore_fraction": 0.51
},
"source_status": {
"agsi": { "stale": false, "last_date": "2026-04-24", "fetched_at": "2026-04-26T03:55Z" },
"ttf": { "stale": false, "last_date": "2026-04-25", "fetched_at": "2026-04-26T04:01Z" }
}
}Who uses this data
Rigorous, non-alarmist, transparent
Practical information for procurement
Contract templates for procurement
Machine-readable API contract
The full OpenAPI 3.1 specification covers public endpoints and authenticated /api/v1 routes, request parameters, response schemas, examples, error models, cache/ETag semantics, and RateLimit headers. Use it to generate client stubs, validate integration tests, or run in Swagger UI.
curl -s "https://gas-risiko.de/api/data?country=DE" \
-H "Accept: application/json" | jq '.latest,.predictions[:2]'ETAG=$(curl -sI "https://gas-risiko.de/api/data?country=DE" | grep -i etag | awk '{print $2}' | tr -d '\r')
curl -sv "https://gas-risiko.de/api/data?country=DE" -H "If-None-Match: $ETAG"curl -i "https://gas-risiko.de/api/v1/data?country=DE" \
-H "Accept: application/json" \
-H "Api-Key: $GAS_RISIKO_API_KEY"curl -s "https://gas-risiko.de/api/scenarios?country=DE" \
-H "Accept: application/json" \
| jq '.scenarios[] | {id:.scenario_id,category,delta:.delta_winter_min_pp}'import requests, json
BASE = "https://gas-risiko.de"
etag = None
def fetch_if_changed(country: str = "DE") -> dict | None:
global etag
headers = {"If-None-Match": etag} if etag else {}
r = requests.get(f"{BASE}/api/data", params={"country": country}, headers=headers)
if r.status_code == 304:
return None # data unchanged
r.raise_for_status()
etag = r.headers.get("ETag")
return r.json()
data = fetch_if_changed("DE")
if data:
latest = data["latest"]
print(f"Fill: {latest['fill_level_pct']:.1f}% "
f"P50 d+1: {data['predictions'][0]['P50']:.1f}%")import requests
COUNTRIES = ["DE", "NL", "FR", "IT", "AT", "CZ", "PL"]
BASE = "https://gas-risiko.de"
risks = {}
for c in COUNTRIES:
r = requests.get(f"{BASE}/api/annual", params={"country": c})
r.raise_for_status()
d = r.json()
risks[c] = {
"winter_min_p50": d["baseline"]["winter_minimum"]["p50"],
"p_breach_15pct": d["baseline"]["p_breach_15pct_any_winter"],
"ood_bounds": d.get("ood_bounds", {}),
}
for c, v in sorted(risks.items(), key=lambda x: x[1]["p_breach_15pct"], reverse=True):
print(f"{c} winter-min P50={v['winter_min_p50']:.1f}% "
f"P(breach 15%)={v['p_breach_15pct']:.0%}")Integrate gas-risiko.de data into your stack
Write to discuss data licensing, update SLAs, custom country coverage, white-label deployment, or integration support.
info@mh-analytics.eu →Data is updated daily from public primary sources (AGSI+, ENTSO-G, Open-Meteo, SMARD, TTF). This site does not constitute financial advice. Forecasts carry explicit uncertainty — always check diagnostics and interval coverage before use in production systems.