Public API
Base URL: https://telescope.energy/api/v2
No authentication is required. Never send a private API key from a public browser client.
Download OpenAPI · Authentication · Troubleshooting
| Method | Endpoint | Task |
|---|---|---|
GET | /api/v2/public/products | List public products |
GET | /api/v2/public/products/{slug} | Read public product details |
GET | /api/v2/public/products/{slug}/preview | Read released public preview values |
GET | /api/v2/public/products/{slug}/performance | Read public forecast performance |
GET | /api/v2/public/products/{slug}/performance/lead-times | Read error by lead time |
GET | /api/v2/public/products/{slug}/performance/availability | Read available performance dates |
Use these endpoints to explore public products, previews and accuracy. For operational forecast access, use the Customer API.
Examples use illustrative identifiers and dates. Python uses the standard library; TypeScript runs server-side with fetch support.
List public products
GET /api/v2/public/products
Lists publicly visible forecast products and their variants without authentication.
Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
featured | Query | true, false | No | Return only featured products when true. |
Request
curl 'https://telescope.energy/api/v2/public/products'
import json
from urllib.request import urlopen
with urlopen("https://telescope.energy/api/v2/public/products", timeout=30) as response:
print(json.dumps(json.load(response), indent=2))
const response = await fetch("https://telescope.energy/api/v2/public/products")
if (!response.ok) throw new Error(await response.text())
console.log(await response.json())
Response
200 OK
Illustrative excerpt; fields and arrays are shortened. See the downloadable OpenAPI for schemas and expanded examples.
{
"products": [
{
"slug": "fcr-de",
"title": "Frequency Containment Reserve (FCR) · DE",
"description": "Probabilistic forecast of German FCR capacity prices in local four-hour products.",
"category": "Balancing",
"featured": false,
"variants": [
{
"key": "capacity-price",
"label": "Capacity Price",
"isDefault": true,
"publicationStatus": "published",
"offeringKey": "fcr-capacity-price-de",
"timezone": "Europe/Berlin",
"resolution": "PT4H",
"unit": "EUR_MWH",
"outputs": [
{ "key": "p50", "valueType": "number", "unit": "EUR_MWH", "quantileLevel": 0.5 }
],
"series": [
{ "key": "symmetric", "label": "Symmetric", "direction": "symmetric" }
],
"firstDeliveryDayOffset": 1,
"maxHorizonDays": 7,
"updatePolicy": { "mode": "gate_closure", "gateDaysBeforeDelivery": 1, "gateLocalTime": "08:00" },
"historySince": "2026-07-27",
"latestReleasedAt": "2026-07-26T17:00:00.000Z"
}
]
}
]
}
| Field | Meaning |
|---|---|
products[].slug | Identifier for product details and preview requests. |
products[].variants | Available variants with their delivery and output metadata. |
Usage notes
- featured accepts true or false; other values return 422 INVALID_BOOLEAN_FILTER.
- Public visibility does not imply access to authenticated customer forecasts.
Read public product details
GET /api/v2/public/products/{slug}
Retrieves the public description, variants, series, outputs and units of one product.
Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
slug | Path | string | Yes | Resource identifier returned by the catalog or list endpoint. |
Request
curl 'https://telescope.energy/api/v2/public/products/afrr-de'
import json
from urllib.request import urlopen
with urlopen("https://telescope.energy/api/v2/public/products/afrr-de", timeout=30) as response:
print(json.dumps(json.load(response), indent=2))
const response = await fetch("https://telescope.energy/api/v2/public/products/afrr-de")
if (!response.ok) throw new Error(await response.text())
console.log(await response.json())
Response
200 OK
Illustrative excerpt; fields and arrays are shortened. See the downloadable OpenAPI for schemas and expanded examples.
{
"product": {
"slug": "afrr-de",
"title": "Automatic Frequency Restoration Reserve (aFRR) · DE",
"description": "Probabilistic forecasts for German aFRR capacity prices, energy prices, and activated reserve power.",
"category": "Balancing",
"featured": false,
"variants": [
{
"key": "capacity-price",
"label": "Capacity Price",
"isDefault": true,
"publicationStatus": "published",
"offeringKey": "afrr-capacity-price-de",
"timezone": "Europe/Berlin",
"resolution": "PT4H",
"unit": "EUR_MWH",
"outputs": [
{ "key": "p50", "valueType": "number", "unit": "EUR_MWH", "quantileLevel": 0.5 }
],
"series": [
{ "key": "positive", "label": "Positive", "direction": "positive" },
{ "key": "negative", "label": "Negative", "direction": "negative" }
],
"firstDeliveryDayOffset": 1,
"maxHorizonDays": 7,
"updatePolicy": { "mode": "gate_closure", "gateDaysBeforeDelivery": 1, "gateLocalTime": "09:00" },
"historySince": "2026-07-27",
"latestReleasedAt": "2026-07-26T17:00:00.000Z"
},
{
"key": "energy-price",
"label": "Energy Price",
"isDefault": false,
"publicationStatus": "published",
"offeringKey": "afrr-energy-price-de",
"timezone": "Europe/Berlin",
"resolution": "PT15M",
"unit": "EUR_MWH",
"outputs": [
{ "key": "p50", "valueType": "number", "unit": "EUR_MWH", "quantileLevel": 0.5 }
],
"series": [
{ "key": "positive", "label": "Positive", "direction": "positive" },
{ "key": "negative", "label": "Negative", "direction": "negative" }
],
"firstDeliveryDayOffset": 1,
"maxHorizonDays": 7,
"updatePolicy": { "mode": "gate_closure", "gateDaysBeforeDelivery": 1, "gateLocalTime": "09:00" },
"historySince": "2026-07-27",
"latestReleasedAt": "2026-07-26T17:00:00.000Z"
}
]
}
}
| Field | Meaning |
|---|---|
product | Product metadata and variants. |
product.variants[].key | Use as variant on preview and performance endpoints. |
Usage notes
- An unavailable or unknown product returns 404 PRODUCT_NOT_FOUND.
- Use the Customer API for operational forecasts.
Read released public preview values
GET /api/v2/public/products/{slug}/preview
Retrieves one local day of public preview values and available actuals. Gate-closure previews use the last publication before the gate; rolling previews use the last release strictly before delivery minus the selected lead.
Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
slug | Path | string | Yes | Resource identifier returned by the catalog or list endpoint. |
variant | Query | string | No | Variant key; omit for the default variant. |
series | Query | string | No | Series key; defaults to the first configured series. |
leadMinutes | Query | 5, 60, 1440 | No | Rolling availability lead in minutes: 5 min, 1 h or 24 h; defaults to 60. Ignored for gate closure. |
beforeDay | Query | YYYY-MM-DD | No | Exclusive local day cursor returned as nextBeforeDay. Defaults to tomorrow; history is limited to 90 local days. |
Request
curl 'https://telescope.energy/api/v2/public/products/afrr-de/preview?variant=activations'
import json
from urllib.request import urlopen
with urlopen("https://telescope.energy/api/v2/public/products/afrr-de/preview?variant=activations", timeout=30) as response:
print(json.dumps(json.load(response), indent=2))
const response = await fetch("https://telescope.energy/api/v2/public/products/afrr-de/preview?variant=activations")
if (!response.ok) throw new Error(await response.text())
console.log(await response.json())
Response
200 OK
Illustrative excerpt; fields and arrays are shortened. See the downloadable OpenAPI for schemas and expanded examples.
{
"preview": {
"slug": "afrr-de",
"variantKey": "activations",
"outputKey": "p50",
"seriesKey": "positive",
"leadMinutes": 60,
"from": "2026-07-25T22:00:00.000Z",
"to": "2026-07-26T22:00:00.000Z",
"releasedUntil": "2026-07-26T11:00:00.000Z",
"historyFrom": "2026-04-27T22:00:00.000Z",
"nextBeforeDay": "2026-07-26",
"actualStatus": "provisional",
"values": [
{ "validTime": "2026-07-26T10:00:00.000Z", "value": 385.6, "actual": null }
]
}
}
| Field | Meaning |
|---|---|
preview.values | Forecast, optional p10/p90 bounds and actuals; unavailable values are null. |
preview.nextBeforeDay | Pass as beforeDay to load the previous local day. |
preview.releasedUntil | Boundary of publicly available preview data. |
preview.actualStatus | Whether the actuals are provisional or final. |
Usage notes
- History is limited to 90 local days. Select a variant and series using the product metadata.
- Gate-closure previews cover delivery intervals through today. Rolling previews exclude the most recent hour; the boundary is rounded down to a quarter hour and is exclusive.
- Quantiles come from the same publication as the point forecast. Products without p50 may return a probability output.
- An unknown product or variant returns 404 PRODUCT_NOT_FOUND.
- Use the Customer API for current forecasts and reproducible version selection.
Read public forecast performance
GET /api/v2/public/products/{slug}/performance
Returns forecast accuracy for 1, 7 or 30 completed local days, or one selected date. Price and quantity forecasts use P50 mean absolute error (MAE); probability forecasts use the Brier score.
Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
slug | Path | string | Yes | Resource identifier returned by the catalog or list endpoint. |
variant | Query | string | No | Case-sensitive resource identifier. |
series | Query | string | No | Case-sensitive resource identifier. |
period | Query | 1, 7, 30, date | No | Defaults to 30. |
day | Query | YYYY-MM-DD | No | Required only for period=date; within the 90-day history and product horizon. Current and future delivery days require actuals; use the availability endpoint for selectable dates. |
Request
curl 'https://telescope.energy/api/v2/public/products/balancing-price-de/performance?period=30'
import json
from urllib.request import urlopen
with urlopen("https://telescope.energy/api/v2/public/products/balancing-price-de/performance?period=30", timeout=30) as response:
print(json.dumps(json.load(response), indent=2))
const response = await fetch("https://telescope.energy/api/v2/public/products/balancing-price-de/performance?period=30")
if (!response.ok) throw new Error(await response.text())
console.log(await response.json())
Response
200 OK
Illustrative excerpt; fields and arrays are shortened. See the downloadable OpenAPI for schemas and expanded examples.
{
"performance": {
"slug": "balancing-price-de",
"variantKey": "forecast",
"seriesKey": "signed",
"timezone": "Europe/Berlin",
"metric": "mae",
"unit": "EUR_MWH",
"fromDay": "2026-08-01",
"toDay": "2026-08-30",
"historyFrom": "2026-06-02",
"lastDay": "2026-08-30",
"computedAt": "2026-08-31T00:00:00.000Z",
"updating": false,
"headline": {
"value": 8.4,
"status": "available",
"count": 2880,
"expected": 2880,
"actualCount": 2880,
"coverage": 1,
"scoredDays": 30,
"totalDays": 30,
"provisional": true,
"sparse": false,
"key": "lead:5",
"leadMinutes": 5
},
"points": []
}
}
| Field | Meaning |
|---|---|
performance.updating / computedAt | Indicates a pending refresh and the last included calculation time. Previous results remain available during recalculation. |
performance.metric / unit | MAE uses the target’s unit; the Brier score measures squared probability error. Lower scores are better. |
performance.headline | Score, status, observation count and coverage for the selected period. |
performance.headline.provisional | True when provisional actuals contribute; the score may change. |
Usage notes
- The headline uses forecasts published strictly before market gate closure, or five minutes before delivery for rolling products.
- At least 50% of expected intervals must be matched; sparsely observed series require at least 30 matches.
- Directions are evaluated separately. Period scores aggregate observation errors, not unweighted daily averages. Missing metrics are not zero.
- Fixed periods end yesterday in the product time zone. For a specific date, use period=date and day; the availability endpoint lists eligible dates.
- Only intervals whose decision cutoff has passed are scored. Provisional actuals are included, and revised actuals may change results. Responses can be cached for up to 60 seconds.
- Unknown or unsupported products return 404.
Read error by lead time
GET /api/v2/public/products/{slug}/performance/lead-times
Compares forecast accuracy at the available minimum decision leads over the last 30 completed local delivery days.
Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
slug | Path | string | Yes | Resource identifier returned by the catalog or list endpoint. |
variant | Query | string | No | Case-sensitive resource identifier. |
series | Query | string | No | Case-sensitive resource identifier. |
Request
curl 'https://telescope.energy/api/v2/public/products/balancing-price-de/performance/lead-times'
import json
from urllib.request import urlopen
with urlopen("https://telescope.energy/api/v2/public/products/balancing-price-de/performance/lead-times", timeout=30) as response:
print(json.dumps(json.load(response), indent=2))
const response = await fetch("https://telescope.energy/api/v2/public/products/balancing-price-de/performance/lead-times")
if (!response.ok) throw new Error(await response.text())
console.log(await response.json())
Response
200 OK
Illustrative excerpt; fields and arrays are shortened. See the downloadable OpenAPI for schemas and expanded examples.
{
"performance": {
"slug": "balancing-price-de",
"variantKey": "forecast",
"seriesKey": "signed",
"timezone": "Europe/Berlin",
"metric": "mae",
"unit": "EUR_MWH",
"fromDay": "2026-08-01",
"toDay": "2026-08-30",
"historyFrom": "2026-06-02",
"lastDay": "2026-08-30",
"computedAt": "2026-08-31T00:00:00.000Z",
"updating": false,
"headline": null,
"points": [
{
"value": 8.4,
"status": "available",
"count": 2880,
"expected": 2880,
"actualCount": 2880,
"coverage": 1,
"scoredDays": 30,
"totalDays": 30,
"provisional": true,
"sparse": false,
"key": "lead:5",
"leadMinutes": 5
}
]
}
}
| Field | Meaning |
|---|---|
performance.updating / computedAt | Indicates a pending refresh and the last included calculation time. Previous results remain available during recalculation. |
performance.points | One score, status, count and coverage per leadMinutes. |
performance.metric / unit | MAE for point forecasts or Brier score for probabilities. Lower scores are better. |
Usage notes
- Each lead requires at least 25% coverage over the full 30-day period and matches on at least 7 distinct delivery days. Sparse series also require at least 30 matches. Directions are evaluated separately; scores aggregate observation errors.
- For gate-closure products, selection is also capped at the market gate. The selected forecast may be older than the requested minimum lead.
- Provisional actuals may contribute. Treat missing scores as unavailable, not zero.
- Responses can be cached for up to 60 seconds. Unknown or unsupported products return 404.
Read available performance dates
GET /api/v2/public/products/{slug}/performance/availability
Lists local dates with an available headline accuracy score for the selected product variant and series.
Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
slug | Path | string | Yes | Resource identifier returned by the catalog or list endpoint. |
variant | Query | string | No | Case-sensitive resource identifier. |
series | Query | string | No | Case-sensitive resource identifier. |
Request
curl 'https://telescope.energy/api/v2/public/products/balancing-price-de/performance/availability?series=signed'
import json
from urllib.request import urlopen
with urlopen("https://telescope.energy/api/v2/public/products/balancing-price-de/performance/availability?series=signed", timeout=30) as response:
print(json.dumps(json.load(response), indent=2))
const response = await fetch("https://telescope.energy/api/v2/public/products/balancing-price-de/performance/availability?series=signed")
if (!response.ok) throw new Error(await response.text())
console.log(await response.json())
Response
200 OK
Illustrative excerpt; fields and arrays are shortened. See the downloadable OpenAPI for schemas and expanded examples.
{
"availability": {
"slug": "balancing-price-de",
"variantKey": "forecast",
"seriesKey": "signed",
"timezone": "Europe/Berlin",
"historyFrom": "2026-06-02",
"lastDay": "2026-08-30",
"latestAvailableDay": "2026-08-30",
"availableDays": [
"2026-08-29",
"2026-08-30"
]
}
}
| Field | Meaning |
|---|---|
availability.availableDays | Dates accepted by the performance endpoint with period=date. |
availability.latestAvailableDay | Most recent qualifying date, or null. |
Usage notes
- Includes up to 90 days of history, plus today and future delivery dates within the product horizon when qualifying actuals and elapsed decision cutoffs exist.
- Uses the headline decision time and coverage thresholds; provisional actuals qualify.
- Responses can be cached for up to 60 seconds and expire at local midnight. An unknown series returns 422; an unavailable product returns 404.
Errors and next steps
Errors contain error.code, error.message, optional error.details and requestId. The same request ID is returned in X-Request-ID. See Troubleshooting for recovery steps and retain the request ID when reporting a failure.