API reference
Markets · aFRR
Capacity and energy bids are separate resources, each with valid bid intervals, updates, bulk delete and results. Backup market defaults and bid overrides are set per asset.
GET/v1/assets/{asset_id}/backup_markets/afrr/bids
List backup-market bid overrides for the asset
asset.read
Path parameters
- asset_idREQUIRED
- string
Query parameters
- product
- enum
posnegnaReturn only overrides for this direction (pos/neg). Null returns both directions. Only pos/neg are supported for afrr. - start_time
- string<date-time>Return bid overrides whose end_time is strictly after this timestamp. With end_time, the API matches intervals overlapping [start_time, end_time).
- end_time
- string<date-time>Return bid overrides whose start_time is strictly before this timestamp. With start_time, the API matches intervals overlapping [start_time, end_time).
curl "https://api.ebx.energy/v1/assets/DE_BESS_01/backup_markets/afrr/bids" \
-H "Authorization: Bearer $EBX_TOKEN"import httpx
r = httpx.get(
"https://api.ebx.energy/v1/assets/DE_BESS_01/backup_markets/afrr/bids",
headers={"Authorization": f"Bearer {token}"},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/backup_markets/afrr/bids", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();{
"bids": [
{
"id": "00000000-0000-0000-0000-000000000000",
"asset_id": "<string>",
"product": "pos",
"start_time": "2026-09-10T13:30:00Z",
"end_time": "2026-09-10T13:30:00Z",
"price": "<string>",
"max_capacity_mw": 0,
"note": "<string>"
}
]
}- 401no valid token
- 403scope missing
- 404unknown or not visible
- 422validation failed
- 429rate limited
POST/v1/assets/{asset_id}/backup_markets/afrr/bids
Submit a backup-market bid override for a window
asset.write
Times must be aligned to 15-minute delivery blocks; minimum window is one block. Set max_capacity_mw=0 to mark the asset unavailable for backup-market dispatch in the window. Mutations are subject to a 10-second gate closure before each 15-minute delivery block.
Path parameters
- asset_idREQUIRED
- string
Request body · required
- productREQUIRED
- enum
posnegnaDirection an IC config/override applies to. aFRR is directional (pos/neg);nais the symmetric placeholder used by FCR collateralization. - start_timeREQUIRED
- string<date-time>Start time of the bid override interval (UTC, 15-min-aligned).
- end_timeREQUIRED
- string<date-time>End time of the bid override interval (UTC, 15-min-aligned, exclusive).
- price
- number | stringOverride price for the window (EUR/MWh). Null falls back to the default price.
- max_capacity_mw
- numberOverride max backup-market dispatch capacity for the window (MW). The value is the absolute (unsigned) magnitude — the dispatcher applies it symmetrically to both positive and negative directions. Set to 0 to mark the asset unavailable in this window. Null falls back to the default cap.
- note
- stringFree-text note attached to the bid override (e.g. reason for unavailability).
curl -X POST "https://api.ebx.energy/v1/assets/DE_BESS_01/backup_markets/afrr/bids" \
-H "Authorization: Bearer $EBX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"product":"pos","start_time":"2026-09-10T13:30:00Z","end_time":"2026-09-10T13:30:00Z","price":80,"max_capacity_mw":0,"note":"<string>"}'import httpx
r = httpx.post(
"https://api.ebx.energy/v1/assets/DE_BESS_01/backup_markets/afrr/bids",
headers={"Authorization": f"Bearer {token}"},
json={
"product": "pos",
"start_time": "2026-09-10T13:30:00Z",
"end_time": "2026-09-10T13:30:00Z",
"price": 80,
"max_capacity_mw": 0,
"note": "<string>",
},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/backup_markets/afrr/bids", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({"product":"pos","start_time":"2026-09-10T13:30:00Z","end_time":"2026-09-10T13:30:00Z","price":80,"max_capacity_mw":0,"note":"<string>"}),
});
const data = await res.json();{
"id": "00000000-0000-0000-0000-000000000000",
"asset_id": "<string>",
"product": "pos",
"start_time": "2026-09-10T13:30:00Z",
"end_time": "2026-09-10T13:30:00Z",
"price": "<string>",
"max_capacity_mw": 0,
"note": "<string>"
}- 400malformed request
- 401no valid token
- 403scope missing
- 404unknown or not visible
- 409state conflict
- 422validation failed
- 429rate limited
DELETE/v1/assets/{asset_id}/backup_markets/afrr/bids/{bid_id}
Cancel a future backup-market bid override
asset.write
Mutations are subject to a 10-second gate closure before each 15-minute delivery block.
Path parameters
- asset_idREQUIRED
- string
- bid_idREQUIRED
- string<uuid>
curl -X DELETE "https://api.ebx.energy/v1/assets/DE_BESS_01/backup_markets/afrr/bids/00000000-0000-0000-0000-000000000000" \
-H "Authorization: Bearer $EBX_TOKEN"import httpx
r = httpx.delete(
"https://api.ebx.energy/v1/assets/DE_BESS_01/backup_markets/afrr/bids/00000000-0000-0000-0000-000000000000",
headers={"Authorization": f"Bearer {token}"},
)
print(r.status_code)const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/backup_markets/afrr/bids/00000000-0000-0000-0000-000000000000", {
method: "DELETE",
headers: {
Authorization: `Bearer ${token}`,
},
});Returns 204 with no body.
- 400malformed request
- 401no valid token
- 403scope missing
- 404unknown or not visible
- 409state conflict
- 429rate limited
GET/v1/assets/{asset_id}/backup_markets/afrr/default
Get the asset's current backup-market default
asset.read
Path parameters
- asset_idREQUIRED
- string
Query parameters
- productREQUIRED
- enum
posnegna
curl "https://api.ebx.energy/v1/assets/DE_BESS_01/backup_markets/afrr/default?product=pos" \
-H "Authorization: Bearer $EBX_TOKEN"import httpx
r = httpx.get(
"https://api.ebx.energy/v1/assets/DE_BESS_01/backup_markets/afrr/default?product=pos",
headers={"Authorization": f"Bearer {token}"},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/backup_markets/afrr/default?product=pos", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();{
"asset_id": "<string>",
"product": "pos",
"default_price": "<string>",
"default_max_capacity_mw": 0
}- 401no valid token
- 403scope missing
- 404unknown or not visible
- 422validation failed
- 429rate limited
PUT/v1/assets/{asset_id}/backup_markets/afrr/default
Set the asset's default backup-market price/capacity
asset.write
Use to change the asset's baseline backup-market price/capacity from now on. For temporary changes, use POST /bids. Existing bid overrides keep their explicit values; only overrides that did not set a value pick up the new default. Mutations are subject to a 10-second gate closure before each 15-minute delivery block.
Path parameters
- asset_idREQUIRED
- string
Request body · required
- productREQUIRED
- enum
posnegnaDirection an IC config/override applies to. aFRR is directional (pos/neg);nais the symmetric placeholder used by FCR collateralization. - default_priceREQUIRED
- number | stringDefault backup-market price in EUR/MWh. Applied from the next delivery block onwards.
- default_max_capacity_mw
- numberOptional default cap on backup-market dispatch capacity (MW). The value is the absolute (unsigned) magnitude — the dispatcher applies it symmetrically to both positive and negative directions. Null means no cap.
curl -X PUT "https://api.ebx.energy/v1/assets/DE_BESS_01/backup_markets/afrr/default" \
-H "Authorization: Bearer $EBX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"product":"pos","default_price":40,"default_max_capacity_mw":2}'import httpx
r = httpx.put(
"https://api.ebx.energy/v1/assets/DE_BESS_01/backup_markets/afrr/default",
headers={"Authorization": f"Bearer {token}"},
json={
"product": "pos",
"default_price": 40,
"default_max_capacity_mw": 2,
},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/backup_markets/afrr/default", {
method: "PUT",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({"product":"pos","default_price":40,"default_max_capacity_mw":2}),
});
const data = await res.json();{
"asset_id": "<string>",
"product": "pos",
"default_price": "<string>",
"default_max_capacity_mw": 0
}- 400malformed request
- 401no valid token
- 403scope missing
- 404unknown or not visible
- 409state conflict
- 422validation failed
- 429rate limited
GET/v1/assets/{asset_id}/markets/afrr/capacity/bids
Get aFRR capacity bids
market.read
Get bids currently placed in the Automatic Frequency Restoration Reserve (aFRR) capacity market.
Use start_time and/or end_time to filter bids by interval overlap. With both values, the endpoint returns bids overlapping the half-open window [start_time, end_time). Bids do not need to be fully contained in the window.
Boundary behavior:
- a bid ending exactly at
start_timeis excluded - a bid starting exactly at
end_timeis excluded
product narrows the result set further by bid direction. If no parameters are provided, it returns all bids for the asset.
Product can be either pos or neg. pos is positive reserve (inject power into the grid), neg is negative reserve (absorb power from the grid).
Bids can be retrieved for the last 30 days.
Path parameters
- asset_idREQUIRED
- string
Query parameters
- start_time
- string<date-time>Return bids whose end_time is strictly after this timestamp. With end_time, the API matches bids overlapping the half-open window [start_time, end_time). Bids do not need to be fully contained in the window.
- end_time
- string<date-time>Return bids whose start_time is strictly before this timestamp.
- product
- enum
posnegFilter by product. Provide a single value:pos(aFRR+) orneg(aFRR-).
curl "https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/capacity/bids" \
-H "Authorization: Bearer $EBX_TOKEN"import httpx
r = httpx.get(
"https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/capacity/bids",
headers={"Authorization": f"Bearer {token}"},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/capacity/bids", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();{
"bids": [
{
"id": "00000000-0000-0000-0000-000000000000",
"idempotency_key": "<string>",
"start_time": "2026-09-10T13:30:00Z",
"end_time": "2026-09-10T13:30:00Z",
"product": "pos",
"capacity_price_eur_per_mw": 50.1,
"energy_price_eur_per_mwh": 35.8,
"offered_power_mw": 100,
"last_edit_attempt": {
"edit_type": "MODIFY",
"status": "PENDING_RESYNC",
"attempted_at": "2026-09-10T13:30:00Z",
"requested_values": "<string>",
"failure_reason": "<string>",
"failure_detail": "<string>"
}
}
],
"market": "capacity"
}- 401no valid token
- 403scope missing
- 404unknown or not visible
- 422validation failed
- 429rate limited
POST/v1/assets/{asset_id}/markets/afrr/capacity/bids
Submit aFRR capacity bids
market.write
Post a bid in the Automatic Frequency Restoration Reserve (aFRR) capacity market.
This endpoint supports two modes:
mode=CREATEinserts bids.mode=REPLACEdeletes all pending bids fully contained inreplace_scopeand inserts only the provided bids.
In REPLACE mode, replace_scope defines the window to clear. Each bid in bids must be fully contained within that window. If replace_scope.product is omitted, both pos and neg bids in the window are deleted before recreating only the provided bids. Missing blocks or product combinations inside the scope remain deleted.
For capacity bids, provide start_time, end_time, product, capacity_price_eur_per_mw, energy_price_eur_per_mwh, and offered_power_mw.
Time intervals are validated against the market calendar to ensure they match valid Regelleistung tender intervals. This correctly handles DST transitions.
Example request:
curl -X POST "https://api.ebx.energy/v1/assets/{asset_id}/markets/afrr/capacity/bids" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <access_token>" \
-d '{
"mode": "REPLACE",
"replace_scope": {
"start_time": "2025-01-02T23:00:00Z",
"end_time": "2025-01-03T23:00:00Z",
"product": "pos"
},
"bids": [
{
"capacity_price_eur_per_mw": 100.0,
"energy_price_eur_per_mwh": 50.0,
"offered_power_mw": 120,
"start_time": "2025-01-02T23:00:00Z",
"end_time": "2025-01-03T03:00:00Z",
"product": "pos",
"idempotency_key": "example-idempotency-key"
},
{
"capacity_price_eur_per_mw": 95.0,
"energy_price_eur_per_mwh": 45.0,
"offered_power_mw": 100,
"start_time": "2025-01-03T03:00:00Z",
"end_time": "2025-01-03T07:00:00Z",
"product": "pos",
"idempotency_key": "example-idempotency-key-2"
}
]
}'In the example above, all pending pos aFRR capacity bids inside the full-day scope are deleted first. Only the two provided blocks are recreated. Any other pos block in that scope remains deleted.
Idempotency is per bid using idempotency_key on each bid object. Scope is defined by the endpoint plus bid interval/product (start_time, end_time, product). Retrying the same key in the same scope returns the originally stored bid and no overwrite occurs. Reusing the same key for a different interval or product is treated as a different bid scope.
Bids for the capacity market are accepted from 10:00 AM CET/CEST one week before delivery day (i.e. D-7).
Gate Closure Times:
- EBX deadline (recommended): D-1 08:30 CET — submitting before this gives us a buffer to forward your bid to the market.
- Regelleistung gate (enforced): D-1 09:00 CET — submissions or updates for a delivery day after this time are rejected with HTTP 409.
Submissions or updates between the EBX deadline and the Regelleistung gate are accepted but not guaranteed to be forwarded to the market in time.
Prequalified capacity ceiling (enforced): For each 15-minute delivery period and each direction (pos/neg), the power in this request plus the power already committed by your other bids in the same market must not exceed the asset's prequalified aFRR capacity. Bids with status PENDING, SUBMITTED, SUCCESSFUL or FALLBACK count as committed; REJECTED, FAILED and CANCELLED bids release their capacity. The capacity and energy markets are checked independently of one another.
If any period would be exceeded, the entire request is rejected with HTTP 409 and error code AFRR_PREQUALIFIED_CAPACITY_EXCEEDED — nothing is stored, including the bids that would have fit. The response detail lists every breached period with its direction, the total committed power, the ceiling, and the overshoot. In mode=REPLACE, the bids this request would delete are not counted against it — but note the clear has not happened when the request is rejected, so a 409 leaves the existing bids in the scope exactly as they were. A prequalified capacity of 0 in a direction is enforced as a real limit of zero: no power may be committed in that direction, and every such bid is rejected. If you believe your asset's recorded prequalified capacity is wrong, contact EBX to have it corrected — the API will not bid past it.
offered_power_mw is in megawatts (MW). capacity_price_eur_per_mw is in EUR/MW, and must be between 0 and 15,000 EUR/MW. energy_price_eur_per_mwh is in EUR/MWh, and must be between -15,000 and 15,000 EUR/MWh. It follows the following cashflow convention:
energy_price_eur_per_mwh < 0: provider pays grid/TSOenergy_price_eur_per_mwh >= 0: provider receives money from grid/TSO
Bids are stored with status=PENDING and will be submitted to the market before gate closure. Once submitted, status moves to SUBMITTED and then to SUCCESSFUL or REJECTED based on the auction outcome. If a bid never reaches the market (e.g. an outage spanning gate closure) and the auction window has passed, it is marked FAILED. For aFRR energy specifically, if the auction itself fails to clear (RAM Fallback), the bid is marked FALLBACK and the TSO Ersatzpreis applies.
Path parameters
- asset_idREQUIRED
- string
Request body · required
- bidsREQUIRED
- array[object]List of bids for the capacity AFRR market.
- mode
- enumdefault CREATE
CREATEREPLACECREATE inserts bids. REPLACE deletes all pending bids fully contained in replace_scope and then inserts only the provided bids. - replace_scope
- objectRequired when mode=REPLACE. Defines the replacement window to clear before recreating the provided bids.
curl -X POST "https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/capacity/bids" \
-H "Authorization: Bearer $EBX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"bids":[{"idempotency_key":"<string>","start_time":"2026-09-10T13:30:00Z","end_time":"2026-09-10T13:30:00Z","product":"pos","capacity_price_eur_per_mw":50.1,"energy_price_eur_per_mwh":35.8,"offered_power_mw":100}],"mode":"CREATE","replace_scope":{"start_time":"2026-09-10T13:30:00Z","end_time":"2026-09-10T13:30:00Z","product":"pos"}}'import httpx
r = httpx.post(
"https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/capacity/bids",
headers={"Authorization": f"Bearer {token}"},
json={
"bids": [
{
"idempotency_key": "<string>",
"start_time": "2026-09-10T13:30:00Z",
"end_time": "2026-09-10T13:30:00Z",
"product": "pos",
"capacity_price_eur_per_mw": 50.1,
"energy_price_eur_per_mwh": 35.8,
"offered_power_mw": 100,
},
],
"mode": "CREATE",
"replace_scope": {
"start_time": "2026-09-10T13:30:00Z",
"end_time": "2026-09-10T13:30:00Z",
"product": "pos",
},
},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/capacity/bids", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({"bids":[{"idempotency_key":"<string>","start_time":"2026-09-10T13:30:00Z","end_time":"2026-09-10T13:30:00Z","product":"pos","capacity_price_eur_per_mw":50.1,"energy_price_eur_per_mwh":35.8,"offered_power_mw":100}],"mode":"CREATE","replace_scope":{"start_time":"2026-09-10T13:30:00Z","end_time":"2026-09-10T13:30:00Z","product":"pos"}}),
});
const data = await res.json();{
"bids": [
{
"id": "00000000-0000-0000-0000-000000000000",
"idempotency_key": "<string>",
"start_time": "2026-09-10T13:30:00Z",
"end_time": "2026-09-10T13:30:00Z",
"product": "pos",
"capacity_price_eur_per_mw": 50.1,
"energy_price_eur_per_mwh": 35.8,
"offered_power_mw": 100,
"last_edit_attempt": {
"edit_type": "MODIFY",
"status": "PENDING_RESYNC",
"attempted_at": "2026-09-10T13:30:00Z",
"requested_values": "<string>",
"failure_reason": "<string>",
"failure_detail": "<string>"
}
}
],
"created_count": 0,
"deleted_count": 0,
"deleted_bid_ids": [
"00000000-0000-0000-0000-000000000000"
],
"updated_count": 0,
"warnings": [
"<string>"
],
"market": "capacity"
}- 400malformed request
- 401no valid token
- 403scope missing
- 404unknown or not visible
- 409state conflict
- 422validation failed
- 429rate limited
DELETE/v1/assets/{asset_id}/markets/afrr/capacity/bids
Bulk delete aFRR capacity bids by interval
market.write
Bulk delete bids in the Automatic Frequency Restoration Reserve (aFRR) capacity market by exact interval.
Matching is strict: only bids with exactly the same start_time and end_time are deleted. If product is omitted, both pos and neg bids are deleted.
Gate Closure Times:
- EBX deadline (recommended): D-1 08:30 CET — deleting before this gives us a buffer to forward your deletion to the market.
- Regelleistung gate (enforced): D-1 09:00 CET — deletions for a delivery day after this time are rejected with HTTP 409.
Deletions submitted between the EBX deadline and the Regelleistung gate are accepted but not guaranteed to be forwarded to the market in time.
Only bids with status=PENDING can be deleted. Once a bid has been submitted to the market, it cannot be deleted through this API.
Path parameters
- asset_idREQUIRED
- string
Query parameters
- start_timeREQUIRED
- string<date-time>Exact interval start for deletion (UTC).
- end_timeREQUIRED
- string<date-time>Exact interval end for deletion (UTC).
- product
- enum
posnegOptional direction filter.
curl -X DELETE "https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/capacity/bids?start_time=2026-02-03T00:00:00Z&end_time=2026-02-04T00:00:00Z" \
-H "Authorization: Bearer $EBX_TOKEN"import httpx
r = httpx.delete(
"https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/capacity/bids?start_time=2026-02-03T00:00:00Z&end_time=2026-02-04T00:00:00Z",
headers={"Authorization": f"Bearer {token}"},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/capacity/bids?start_time=2026-02-03T00:00:00Z&end_time=2026-02-04T00:00:00Z", {
method: "DELETE",
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();{
"deleted_count": 0,
"deleted_bid_ids": [
"00000000-0000-0000-0000-000000000000"
]
}- 400malformed request
- 401no valid token
- 403scope missing
- 404unknown or not visible
- 409state conflict
- 422validation failed
- 429rate limited
PUT/v1/assets/{asset_id}/markets/afrr/capacity/bids/{bid_id}
Update an aFRR capacity bid
market.write
Update bids in the Automatic Frequency Restoration Reserve (aFRR) capacity market.
This endpoint allows you to modify existing bids for a specific market, date, time, and product.
You can update the bid by providing the bid_id in the URL path. This ID is obtained when the bid was created.
Gate Closure Times:
- EBX deadline (recommended): D-1 08:30 CET — updating before this gives us a buffer to forward your update to the market.
- Regelleistung gate (enforced): D-1 09:00 CET — updates for a delivery day after this time are rejected with HTTP 409.
Late edits (PENDING vs SUBMITTED bids):
- PENDING bids — the change is applied directly before the bid is submitted to the market. The response shows the updated bid with
last_edit_attempt=null. - SUBMITTED bids (already in market, before the Regelleistung gate) — the bid row in the response keeps showing what Regelleistung currently has; the requested change is captured in
last_edit_attemptwith statusPENDING_RESYNC. The market-operations-service forwards it to Regelleistung within ~1 minute. If the gate closes before the forwarding succeeds,last_edit_attempt.statusbecomesFAILED_LATE_EDITand the original bid stays in the market.
start_time, end_time, and product cannot be changed after a bid has been submitted (the tender would change). Only offered_power_mw, capacity_price_eur_per_mw, and energy_price_eur_per_mwh can be modified on a SUBMITTED bid.
For PENDING bids, a changed start_time/end_time must match a valid bidding window in the market calendar — otherwise the request is rejected with HTTP 422 and suggested valid intervals, exactly as on bid creation.
Prequalified capacity ceiling (enforced): Raising offered_power_mw is rejected with HTTP 409 and error code AFRR_PREQUALIFIED_CAPACITY_EXCEEDED if the new value would push the total committed capacity-market power above the asset's prequalified aFRR capacity in any 15-minute delivery period for this bid's direction. This bid's own current power is not counted against the request, so a bid may always be raised to consume the headroom it already holds. Nothing is written when the check fails — for a SUBMITTED bid, no edit attempt is recorded either. A prequalified capacity of 0 in a direction is enforced as a real limit of zero, so no power may be committed in that direction at all.
Specifying the idempotency_key will overwrite the existing idempotency key for the bid (PENDING bids only).
Path parameters
- asset_idREQUIRED
- string
- bid_idREQUIRED
- string<uuid>Bid ID to update
Request body · required
- idempotency_key
- stringOptional idempotency key. Deduplication is scoped to the bid delivery scope within the endpoint (start/end interval, and product for aFRR).
- start_timeREQUIRED
- string<date-time>The start time for the time window for the bids in ISO 8601 format (UTC, 15-minute aligned)
- end_timeREQUIRED
- string<date-time>The end time for the time window for the bids in ISO 8601 format (UTC, 15-minute aligned)
- productREQUIRED
- enum
posneg - capacity_price_eur_per_mwREQUIRED
- number | stringThe price of the offered capacity in EUR/MW (max 2 decimal places). Rejected with 422
PRICE_OUT_OF_RANGEabove the configured market maximum (15000 EUR/MW by default). - energy_price_eur_per_mwhREQUIRED
- number | stringThe price of the offered energy in EUR/MWh (max 2 decimal places). May be negative — paying to absorb energy is a legitimate offer. Rejected with 422
PRICE_OUT_OF_RANGEoutside the configured market range (-15000 to 15000 EUR/MWh by default). - offered_power_mwREQUIRED
- integerThe amount of power offered in MW (integer values only, min 1 MW)
curl -X PUT "https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/capacity/bids/00000000-0000-0000-0000-000000000000" \
-H "Authorization: Bearer $EBX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"idempotency_key":"<string>","start_time":"2026-09-10T13:30:00Z","end_time":"2026-09-10T13:30:00Z","product":"pos","capacity_price_eur_per_mw":50.1,"energy_price_eur_per_mwh":35.8,"offered_power_mw":100}'import httpx
r = httpx.put(
"https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/capacity/bids/00000000-0000-0000-0000-000000000000",
headers={"Authorization": f"Bearer {token}"},
json={
"idempotency_key": "<string>",
"start_time": "2026-09-10T13:30:00Z",
"end_time": "2026-09-10T13:30:00Z",
"product": "pos",
"capacity_price_eur_per_mw": 50.1,
"energy_price_eur_per_mwh": 35.8,
"offered_power_mw": 100,
},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/capacity/bids/00000000-0000-0000-0000-000000000000", {
method: "PUT",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({"idempotency_key":"<string>","start_time":"2026-09-10T13:30:00Z","end_time":"2026-09-10T13:30:00Z","product":"pos","capacity_price_eur_per_mw":50.1,"energy_price_eur_per_mwh":35.8,"offered_power_mw":100}),
});
const data = await res.json();{
"bids": [
{
"id": "00000000-0000-0000-0000-000000000000",
"idempotency_key": "<string>",
"start_time": "2026-09-10T13:30:00Z",
"end_time": "2026-09-10T13:30:00Z",
"product": "pos",
"capacity_price_eur_per_mw": 50.1,
"energy_price_eur_per_mwh": 35.8,
"offered_power_mw": 100,
"last_edit_attempt": {
"edit_type": "MODIFY",
"status": "PENDING_RESYNC",
"attempted_at": "2026-09-10T13:30:00Z",
"requested_values": "<string>",
"failure_reason": "<string>",
"failure_detail": "<string>"
}
}
],
"warnings": [
"<string>"
],
"market": "capacity"
}- 400malformed request
- 401no valid token
- 403scope missing
- 404unknown or not visible
- 409state conflict
- 422validation failed
- 429rate limited
DELETE/v1/assets/{asset_id}/markets/afrr/capacity/bids/{bid_id}
Delete aFRR capacity bid by ID
market.write
Delete a bid in the Automatic Frequency Restoration Reserve (aFRR) capacity market.
Use the bid ID that was returned when the bids were placed to delete a specific bid.
Gate Closure Times:
- EBX deadline (recommended): D-1 08:30 CET — deleting before this gives us a buffer to forward your deletion to the market.
- Regelleistung gate (enforced): D-1 09:00 CET — deletions for a delivery day after this time are rejected with HTTP 409.
Late deletes (PENDING vs SUBMITTED bids):
- PENDING bids — the row is removed from the database. Response:
deleted=true,last_edit_attempt=null. - SUBMITTED bids (already in market, before the Regelleistung gate) — the bid stays in the database (it mirrors what Regelleistung currently has) and a deletion attempt is queued. Response:
deleted=false,last_edit_attempt.status=PENDING_RESYNC. The market-operations-service forwards the deletion to Regelleistung within ~1 minute. If the gate closes before the forwarding succeeds,last_edit_attempt.statusbecomesFAILED_LATE_EDITand the original bid stays in the market.
Path parameters
- asset_idREQUIRED
- string
- bid_idREQUIRED
- string<uuid>Bid ID to delete
curl -X DELETE "https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/capacity/bids/00000000-0000-0000-0000-000000000000" \
-H "Authorization: Bearer $EBX_TOKEN"import httpx
r = httpx.delete(
"https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/capacity/bids/00000000-0000-0000-0000-000000000000",
headers={"Authorization": f"Bearer {token}"},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/capacity/bids/00000000-0000-0000-0000-000000000000", {
method: "DELETE",
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();{
"bid_id": "00000000-0000-0000-0000-000000000000",
"deleted": false,
"last_edit_attempt": {
"edit_type": "MODIFY",
"status": "PENDING_RESYNC",
"attempted_at": "2026-09-10T13:30:00Z",
"requested_values": "<string>",
"failure_reason": "<string>",
"failure_detail": "<string>"
}
}- 400malformed request
- 401no valid token
- 403scope missing
- 404unknown or not visible
- 409state conflict
- 429rate limited
GET/v1/assets/{asset_id}/markets/afrr/capacity/bids/intervals
Get valid aFRR capacity bid intervals
market.read
Discover valid bid intervals for aFRR capacity market.
Returns a list of time intervals during which capacity bids can be submitted. Use this endpoint to find valid start_time and end_time values for your bid submissions.
Important: Daylight Saving Time (DST) Handling
Bid intervals are always specified in UTC. During DST transitions, the duration of market blocks may vary:
- Spring forward (last Sunday of March): A "4-hour local block" (e.g., 00:00-04:00 CET) may be only 3 hours in UTC (e.g., 23:00Z to 02:00Z).
- Fall back (last Sunday of October): A "4-hour local block" may be 5 hours in UTC (e.g., 22:00Z to 03:00Z).
Always use this discovery endpoint to find the correct intervals, especially around DST transition dates.
Query Parameters:
date: Filter by delivery date (YYYY-MM-DD). If not provided, returns intervals for the next 7 days.
Path parameters
- asset_idREQUIRED
- string
Query parameters
- date
- string<date>Filter intervals by delivery date (YYYY-MM-DD). If not provided, returns intervals for the next 7 days.
curl "https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/capacity/bids/intervals" \
-H "Authorization: Bearer $EBX_TOKEN"import httpx
r = httpx.get(
"https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/capacity/bids/intervals",
headers={"Authorization": f"Bearer {token}"},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/capacity/bids/intervals", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();{
"intervals": [
{
"start_time": "2026-09-10T13:30:00Z",
"end_time": "2026-09-10T13:30:00Z"
}
]
}- 401no valid token
- 403scope missing
- 404unknown or not visible
- 422validation failed
- 429rate limited
GET/v1/assets/{asset_id}/markets/afrr/capacity/results
Get aFRR capacity results
market.read
Get aFRR results in the capacity market.
Use start_time and/or end_time (UTC ISO8601) for overlap-based time filtering. With both values, the endpoint returns result rows whose bid interval overlaps the half-open window [start_time, end_time). Results do not need to be fully contained in the window.
Boundary behavior:
- a result ending exactly at
start_timeis excluded - a result starting exactly at
end_timeis excluded
If neither start_time nor end_time is provided, results default to the current UTC day (00:00:00Z to 00:00:00Z next day).
Status values:
PENDING— bid created but not yet submitted to marketSUBMITTED— bid sent to market, awaiting resultsREJECTED— bid was rejected by the marketSUCCESSFUL— bid was accepted by the marketFAILED— bid never reached the market (e.g. an outage spanning gate closure) and the auction window has passed; this is a terminal state with no settlement implicationCANCELLED— the bid was withdrawn before the market closed, either by you or because the asset could no longer back it; it holds no prequalified capacity and has no settlement implication
In Germany, aFRR capacity is remunerated pay-as-bid. The capacity_price_eur_per_mw field is the customer's offered and settlement-relevant capacity price for awarded MW. The response therefore does not expose a separate market clearing_price.
The energy_price_eur_per_mwh field is the balancing-energy offer linked to the capacity bid. It is included for reference but is not a capacity-market settlement field.
accepted_power_mw and offered_power_mw are in MW.
Path parameters
- asset_idREQUIRED
- string
Query parameters
- start_time
- string<date-time>Return results whose bid end_time is strictly after this timestamp. With end_time, the API matches result intervals overlapping the half-open window [start_time, end_time). Results do not need to be fully contained in the window.
- end_time
- string<date-time>Return results whose bid start_time is strictly before this timestamp. With start_time, the API matches result intervals overlapping the half-open window [start_time, end_time).
- product
- enum
posnegFilter by product. Provide a single value:pos(aFRR+) orneg(aFRR-). - status
- enum
PENDINGSUBMITTEDREJECTEDSUCCESSFULFAILEDFALLBACKCANCELLEDFilter by bid status. Valid values:pending,submitted,successful,rejected,failed,cancelled.
curl "https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/capacity/results" \
-H "Authorization: Bearer $EBX_TOKEN"import httpx
r = httpx.get(
"https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/capacity/results",
headers={"Authorization": f"Bearer {token}"},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/capacity/results", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();{
"results": [
{
"start_time": "2026-09-10T13:30:00Z",
"end_time": "2026-09-10T13:30:00Z",
"product": "pos",
"status": "PENDING",
"accepted_power_mw": 100,
"capacity_price_eur_per_mw": 50.1,
"energy_price_eur_per_mwh": 35.8,
"offered_power_mw": 100,
"external_origin": false
}
]
}- 401no valid token
- 403scope missing
- 404unknown or not visible
- 422validation failed
- 429rate limited
GET/v1/assets/{asset_id}/markets/afrr/energy/bids
Get aFRR energy bids
market.read
Get bids currently placed in the Automatic Frequency Restoration Reserve (aFRR) energy market.
Use start_time and/or end_time to filter bids by interval overlap. With both values, the endpoint returns bids overlapping the half-open window [start_time, end_time). Bids do not need to be fully contained in the window.
Boundary behavior:
- a bid ending exactly at
start_timeis excluded - a bid starting exactly at
end_timeis excluded
product narrows the result set further by bid direction. If no parameters are provided, it returns all bids for the asset.
Product can be either pos or neg. pos is positive reserve (inject power into the grid), neg is negative reserve (absorb power from the grid).
Bids can be retrieved for the last 30 days.
Path parameters
- asset_idREQUIRED
- string
Query parameters
- start_time
- string<date-time>Return bids whose end_time is strictly after this timestamp. With end_time, the API matches bids overlapping the half-open window [start_time, end_time). Bids do not need to be fully contained in the window.
- end_time
- string<date-time>Return bids whose start_time is strictly before this timestamp.
- product
- enum
posnegFilter by product. Provide a single value:pos(aFRR+) orneg(aFRR-).
curl "https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/energy/bids" \
-H "Authorization: Bearer $EBX_TOKEN"import httpx
r = httpx.get(
"https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/energy/bids",
headers={"Authorization": f"Bearer {token}"},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/energy/bids", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();{
"bids": [
{
"id": "00000000-0000-0000-0000-000000000000",
"idempotency_key": "<string>",
"start_time": "2026-09-10T13:30:00Z",
"end_time": "2026-09-10T13:30:00Z",
"product": "pos",
"energy_price_eur_per_mwh": 35.8,
"offered_power_mw": 100,
"last_edit_attempt": {
"edit_type": "MODIFY",
"status": "PENDING_RESYNC",
"attempted_at": "2026-09-10T13:30:00Z",
"requested_values": "<string>",
"failure_reason": "<string>",
"failure_detail": "<string>"
}
}
],
"market": "capacity"
}- 401no valid token
- 403scope missing
- 404unknown or not visible
- 422validation failed
- 429rate limited
POST/v1/assets/{asset_id}/markets/afrr/energy/bids
Submit aFRR energy bids
market.write
Post a bid in the Automatic Frequency Restoration Reserve (aFRR) energy market.
This endpoint supports two modes:
mode=CREATEinserts bids.mode=REPLACEdeletes all pending bids fully contained inreplace_scopeand inserts only the provided bids.
In REPLACE mode, replace_scope defines the window to clear. Each bid in bids must be fully contained within that window. If replace_scope.product is omitted, both pos and neg bids in the window are deleted before recreating only the provided bids. Missing quarter-hours or product combinations inside the scope remain deleted.
For energy bids, provide start_time, end_time, product, energy_price_eur_per_mwh, and offered_power_mw.
Time intervals are validated against the market calendar to ensure they match valid Regelleistung tender intervals. This correctly handles DST transitions where quarter-hour counts may vary.
Example request:
curl -X POST "https://api.ebx.energy/v1/assets/{asset_id}/markets/afrr/energy/bids" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <access_token>" \
-d '{
"mode": "REPLACE",
"replace_scope": {
"start_time": "2025-01-03T10:00:00Z",
"end_time": "2025-01-03T11:00:00Z",
"product": "pos"
},
"bids": [
{
"energy_price_eur_per_mwh": -100.0,
"offered_power_mw": 120,
"start_time": "2025-01-03T10:00:00Z",
"end_time": "2025-01-03T10:15:00Z",
"product": "pos",
"idempotency_key": "example-idempotency-key"
},
{
"energy_price_eur_per_mwh": -95.0,
"offered_power_mw": 110,
"start_time": "2025-01-03T10:15:00Z",
"end_time": "2025-01-03T10:30:00Z",
"product": "pos",
"idempotency_key": "example-idempotency-key-2"
}
]
}'In the example above, all pending pos aFRR energy bids inside the one-hour scope are deleted first. Only the two provided quarter-hours are recreated. Any other quarter-hour in that scope remains deleted.
Idempotency is per bid using idempotency_key on each bid object. Scope is defined by the endpoint plus bid interval/product (start_time, end_time, product). Retrying the same key in the same scope returns the originally stored bid and no overwrite occurs. Reusing the same key for a different interval or product is treated as a different bid scope.
Bids for the energy market are accepted after the capacity market results are available (c.a. 09:30 AM CET/CEST) one day before delivery (D-1).
Gate Closure Times:
- EBX deadline (recommended): T-30 min before MTU — submitting before this gives us a buffer to forward your bid to the market.
- Regelleistung gate (enforced): T-25 min before MTU — submissions or updates for an MTU after this time are rejected with HTTP 409.
Submissions or updates between the EBX deadline and the Regelleistung gate are accepted but not guaranteed to be forwarded to the market in time.
Prequalified capacity ceiling (enforced): For each 15-minute delivery period and each direction (pos/neg), the power in this request plus the power already committed by your other energy-market bids must not exceed the asset's prequalified aFRR capacity. Bids with status PENDING, SUBMITTED, SUCCESSFUL or FALLBACK count as committed; REJECTED, FAILED and CANCELLED bids release their capacity. Capacity-market bids are counted separately and do not consume energy-market headroom.
If any period would be exceeded, the entire request is rejected with HTTP 409 and error code AFRR_PREQUALIFIED_CAPACITY_EXCEEDED — nothing is stored, including the bids that would have fit. The response detail lists every breached period with its direction, the total committed power, the ceiling, and the overshoot. In mode=REPLACE, the bids this request would delete are not counted against it — but note the clear has not happened when the request is rejected, so a 409 leaves the existing bids in the scope exactly as they were. A prequalified capacity of 0 in a direction is enforced as a real limit of zero: no power may be committed in that direction, and every such bid is rejected. If you believe your asset's recorded prequalified capacity is wrong, contact EBX to have it corrected — the API will not bid past it.
offered_power_mw is in megawatts (MW). energy_price_eur_per_mwh is in EUR/MWh, and must be between -15,000 and 15,000 EUR/MWh. It follows the following cashflow convention:
energy_price_eur_per_mwh < 0: provider pays grid/TSOenergy_price_eur_per_mwh >= 0: provider receives money from grid/TSO
Bids are stored with status=PENDING and will be submitted to the market before gate closure. Once submitted, status moves to SUBMITTED and then to SUCCESSFUL or REJECTED based on the auction outcome. If a bid never reaches the market (e.g. an outage spanning gate closure) and the auction window has passed, it is marked FAILED. For aFRR energy specifically, if the auction itself fails to clear (RAM Fallback), the bid is marked FALLBACK and the TSO Ersatzpreis applies.
Path parameters
- asset_idREQUIRED
- string
Request body · required
- bidsREQUIRED
- array[object]List of bids for the energy AFRR market.
- mode
- enumdefault CREATE
CREATEREPLACECREATE inserts bids. REPLACE deletes all pending bids fully contained in replace_scope and then inserts only the provided bids. - replace_scope
- objectRequired when mode=REPLACE. Defines the replacement window to clear before recreating the provided bids.
curl -X POST "https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/energy/bids" \
-H "Authorization: Bearer $EBX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"bids":[{"idempotency_key":"<string>","start_time":"2026-09-10T13:30:00Z","end_time":"2026-09-10T13:30:00Z","product":"pos","energy_price_eur_per_mwh":35.8,"offered_power_mw":100}],"mode":"CREATE","replace_scope":{"start_time":"2026-09-10T13:30:00Z","end_time":"2026-09-10T13:30:00Z","product":"pos"}}'import httpx
r = httpx.post(
"https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/energy/bids",
headers={"Authorization": f"Bearer {token}"},
json={
"bids": [
{
"idempotency_key": "<string>",
"start_time": "2026-09-10T13:30:00Z",
"end_time": "2026-09-10T13:30:00Z",
"product": "pos",
"energy_price_eur_per_mwh": 35.8,
"offered_power_mw": 100,
},
],
"mode": "CREATE",
"replace_scope": {
"start_time": "2026-09-10T13:30:00Z",
"end_time": "2026-09-10T13:30:00Z",
"product": "pos",
},
},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/energy/bids", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({"bids":[{"idempotency_key":"<string>","start_time":"2026-09-10T13:30:00Z","end_time":"2026-09-10T13:30:00Z","product":"pos","energy_price_eur_per_mwh":35.8,"offered_power_mw":100}],"mode":"CREATE","replace_scope":{"start_time":"2026-09-10T13:30:00Z","end_time":"2026-09-10T13:30:00Z","product":"pos"}}),
});
const data = await res.json();{
"bids": [
{
"id": "00000000-0000-0000-0000-000000000000",
"idempotency_key": "<string>",
"start_time": "2026-09-10T13:30:00Z",
"end_time": "2026-09-10T13:30:00Z",
"product": "pos",
"energy_price_eur_per_mwh": 35.8,
"offered_power_mw": 100,
"last_edit_attempt": {
"edit_type": "MODIFY",
"status": "PENDING_RESYNC",
"attempted_at": "2026-09-10T13:30:00Z",
"requested_values": "<string>",
"failure_reason": "<string>",
"failure_detail": "<string>"
}
}
],
"created_count": 0,
"deleted_count": 0,
"deleted_bid_ids": [
"00000000-0000-0000-0000-000000000000"
],
"updated_count": 0,
"warnings": [
"<string>"
],
"market": "capacity"
}- 400malformed request
- 401no valid token
- 403scope missing
- 404unknown or not visible
- 409state conflict
- 422validation failed
- 429rate limited
DELETE/v1/assets/{asset_id}/markets/afrr/energy/bids
Bulk delete aFRR energy bids by interval
market.write
Bulk delete bids in the Automatic Frequency Restoration Reserve (aFRR) energy market by exact interval.
Matching is strict: only bids with exactly the same start_time and end_time are deleted. If product is omitted, both pos and neg bids are deleted.
Gate Closure Times:
- EBX deadline (recommended): T-30 min before MTU — deleting before this gives us a buffer to forward your deletion to the market.
- Regelleistung gate (enforced): T-25 min before MTU — deletions for an MTU after this time are rejected with HTTP 409.
Deletions submitted between the EBX deadline and the Regelleistung gate are accepted but not guaranteed to be forwarded to the market in time.
Only bids with status=PENDING can be deleted. Once a bid has been submitted to the market, it cannot be deleted through this API.
Path parameters
- asset_idREQUIRED
- string
Query parameters
- start_timeREQUIRED
- string<date-time>Exact interval start for deletion (UTC).
- end_timeREQUIRED
- string<date-time>Exact interval end for deletion (UTC).
- product
- enum
posnegOptional direction filter.
curl -X DELETE "https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/energy/bids?start_time=2026-02-03T00:00:00Z&end_time=2026-02-04T00:00:00Z" \
-H "Authorization: Bearer $EBX_TOKEN"import httpx
r = httpx.delete(
"https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/energy/bids?start_time=2026-02-03T00:00:00Z&end_time=2026-02-04T00:00:00Z",
headers={"Authorization": f"Bearer {token}"},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/energy/bids?start_time=2026-02-03T00:00:00Z&end_time=2026-02-04T00:00:00Z", {
method: "DELETE",
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();{
"deleted_count": 0,
"deleted_bid_ids": [
"00000000-0000-0000-0000-000000000000"
]
}- 400malformed request
- 401no valid token
- 403scope missing
- 404unknown or not visible
- 409state conflict
- 422validation failed
- 429rate limited
PUT/v1/assets/{asset_id}/markets/afrr/energy/bids/{bid_id}
Update an aFRR energy bid
market.write
Update bids in the Automatic Frequency Restoration Reserve (aFRR) energy market.
This endpoint allows you to modify existing bids for a specific market, date, time, and product.
You can update the bid by providing the bid_id in the URL path. This ID is obtained when the bid was created.
Gate Closure Times:
- EBX deadline (recommended): T-30 min before MTU — updating before this gives us a buffer to forward your update to the market.
- Regelleistung gate (enforced): T-25 min before MTU — updates for an MTU after this time are rejected with HTTP 409.
Late edits (PENDING vs SUBMITTED bids):
- PENDING bids — the change is applied directly before the bid is submitted to the market. The response shows the updated bid with
last_edit_attempt=null. - SUBMITTED bids (already in market, before the Regelleistung gate) — the bid row in the response keeps showing what Regelleistung currently has; the requested change is captured in
last_edit_attemptwith statusPENDING_RESYNC. The market-operations-service forwards it to Regelleistung within ~1 minute. If the gate closes before the forwarding succeeds,last_edit_attempt.statusbecomesFAILED_LATE_EDITand the original bid stays in the market.
start_time, end_time, and product cannot be changed after a bid has been submitted (the tender would change). Only offered_power_mw and energy_price_eur_per_mwh can be modified on a SUBMITTED bid.
For PENDING bids, a changed start_time/end_time must match a valid bidding window in the market calendar — otherwise the request is rejected with HTTP 422 and suggested valid intervals, exactly as on bid creation.
Prequalified capacity ceiling (enforced): Raising offered_power_mw is rejected with HTTP 409 and error code AFRR_PREQUALIFIED_CAPACITY_EXCEEDED if the new value would push the total committed energy-market power above the asset's prequalified aFRR capacity for this bid's direction and delivery period. This bid's own current power is not counted against the request, so a bid may always be raised to consume the headroom it already holds. Nothing is written when the check fails — for a SUBMITTED bid, no edit attempt is recorded either. The check is skipped when the asset's prequalified capacity for that direction is 0, meaning it has not been recorded.
Specifying the idempotency_key will overwrite the existing idempotency key for the bid (PENDING bids only).
Path parameters
- asset_idREQUIRED
- string
- bid_idREQUIRED
- string<uuid>Bid ID to update
Request body · required
- idempotency_key
- stringOptional idempotency key. Deduplication is scoped to the bid delivery scope within the endpoint (start/end interval, and product for aFRR).
- start_timeREQUIRED
- string<date-time>The start time for the time window for the bids in ISO 8601 format (UTC, 15-minute aligned)
- end_timeREQUIRED
- string<date-time>The end time for the time window for the bids in ISO 8601 format (UTC, 15-minute aligned)
- productREQUIRED
- enum
posneg - energy_price_eur_per_mwhREQUIRED
- number | stringThe price of the offered energy in EUR/MWh (max 2 decimal places). May be negative — paying to absorb energy is a legitimate offer. Rejected with 422
PRICE_OUT_OF_RANGEoutside the configured market range (-15000 to 15000 EUR/MWh by default). - offered_power_mwREQUIRED
- integerThe power offered in MW (integer values only, min 1 MW). While this is an energy market, bids specify power capacity available for activation.
curl -X PUT "https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/energy/bids/00000000-0000-0000-0000-000000000000" \
-H "Authorization: Bearer $EBX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"idempotency_key":"<string>","start_time":"2026-09-10T13:30:00Z","end_time":"2026-09-10T13:30:00Z","product":"pos","energy_price_eur_per_mwh":35.8,"offered_power_mw":100}'import httpx
r = httpx.put(
"https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/energy/bids/00000000-0000-0000-0000-000000000000",
headers={"Authorization": f"Bearer {token}"},
json={
"idempotency_key": "<string>",
"start_time": "2026-09-10T13:30:00Z",
"end_time": "2026-09-10T13:30:00Z",
"product": "pos",
"energy_price_eur_per_mwh": 35.8,
"offered_power_mw": 100,
},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/energy/bids/00000000-0000-0000-0000-000000000000", {
method: "PUT",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({"idempotency_key":"<string>","start_time":"2026-09-10T13:30:00Z","end_time":"2026-09-10T13:30:00Z","product":"pos","energy_price_eur_per_mwh":35.8,"offered_power_mw":100}),
});
const data = await res.json();{
"bids": [
{
"id": "00000000-0000-0000-0000-000000000000",
"idempotency_key": "<string>",
"start_time": "2026-09-10T13:30:00Z",
"end_time": "2026-09-10T13:30:00Z",
"product": "pos",
"energy_price_eur_per_mwh": 35.8,
"offered_power_mw": 100,
"last_edit_attempt": {
"edit_type": "MODIFY",
"status": "PENDING_RESYNC",
"attempted_at": "2026-09-10T13:30:00Z",
"requested_values": "<string>",
"failure_reason": "<string>",
"failure_detail": "<string>"
}
}
],
"warnings": [
"<string>"
],
"market": "capacity"
}- 400malformed request
- 401no valid token
- 403scope missing
- 404unknown or not visible
- 409state conflict
- 422validation failed
- 429rate limited
DELETE/v1/assets/{asset_id}/markets/afrr/energy/bids/{bid_id}
Delete aFRR energy bid by ID
market.write
Delete a bid in the Automatic Frequency Restoration Reserve (aFRR) energy market.
Use the bid ID that was returned when the bids were placed to delete a specific bid.
Gate Closure Times:
- EBX deadline (recommended): T-30 min before MTU — deleting before this gives us a buffer to forward your deletion to the market.
- Regelleistung gate (enforced): T-25 min before MTU — deletions for an MTU after this time are rejected with HTTP 409.
Late deletes (PENDING vs SUBMITTED bids):
- PENDING bids — the row is removed from the database. Response:
deleted=true,last_edit_attempt=null. - SUBMITTED bids (already in market, before the Regelleistung gate) — the bid stays in the database (it mirrors what Regelleistung currently has) and a deletion attempt is queued. Response:
deleted=false,last_edit_attempt.status=PENDING_RESYNC. The market-operations-service forwards the deletion to Regelleistung within ~1 minute. If the gate closes before the forwarding succeeds,last_edit_attempt.statusbecomesFAILED_LATE_EDITand the original bid stays in the market.
Path parameters
- asset_idREQUIRED
- string
- bid_idREQUIRED
- string<uuid>Bid ID to delete
curl -X DELETE "https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/energy/bids/00000000-0000-0000-0000-000000000000" \
-H "Authorization: Bearer $EBX_TOKEN"import httpx
r = httpx.delete(
"https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/energy/bids/00000000-0000-0000-0000-000000000000",
headers={"Authorization": f"Bearer {token}"},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/energy/bids/00000000-0000-0000-0000-000000000000", {
method: "DELETE",
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();{
"bid_id": "00000000-0000-0000-0000-000000000000",
"deleted": false,
"last_edit_attempt": {
"edit_type": "MODIFY",
"status": "PENDING_RESYNC",
"attempted_at": "2026-09-10T13:30:00Z",
"requested_values": "<string>",
"failure_reason": "<string>",
"failure_detail": "<string>"
}
}- 400malformed request
- 401no valid token
- 403scope missing
- 404unknown or not visible
- 409state conflict
- 429rate limited
GET/v1/assets/{asset_id}/markets/afrr/energy/bids/intervals
Get valid aFRR energy bid intervals
market.read
Discover valid bid intervals for aFRR energy market.
Returns a list of time intervals during which energy bids can be submitted. Use this endpoint to find valid start_time and end_time values for your bid submissions.
Important: Daylight Saving Time (DST) Handling
Bid intervals are always specified in UTC. During DST transitions, the number of quarter-hour periods in a day may vary:
- Spring forward: The day has 92 quarter-hours instead of 96.
- Fall back: The day has 100 quarter-hours instead of 96.
Always use this discovery endpoint to find the correct intervals, especially around DST transition dates.
Query Parameters:
date: Filter by delivery date (YYYY-MM-DD). If not provided, returns intervals for the next 48 hours.
Path parameters
- asset_idREQUIRED
- string
Query parameters
- date
- string<date>Filter intervals by delivery date (YYYY-MM-DD). If not provided, returns intervals for the next 7 days.
curl "https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/energy/bids/intervals" \
-H "Authorization: Bearer $EBX_TOKEN"import httpx
r = httpx.get(
"https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/energy/bids/intervals",
headers={"Authorization": f"Bearer {token}"},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/energy/bids/intervals", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();{
"intervals": [
{
"start_time": "2026-09-10T13:30:00Z",
"end_time": "2026-09-10T13:30:00Z"
}
]
}- 401no valid token
- 403scope missing
- 404unknown or not visible
- 422validation failed
- 429rate limited
GET/v1/assets/{asset_id}/markets/afrr/energy/results
Get aFRR energy results
market.read
Get aFRR results in the energy market.
Use start_time and/or end_time (UTC ISO8601) for overlap-based time filtering. With both values, the endpoint returns result rows whose bid interval overlaps the half-open window [start_time, end_time). Results do not need to be fully contained in the window.
Boundary behavior:
- a result ending exactly at
start_timeis excluded - a result starting exactly at
end_timeis excluded
If neither start_time nor end_time is provided, results default to the current UTC day (00:00:00Z to 00:00:00Z next day).
Status values:
PENDING— bid created but not yet submitted to marketSUBMITTED— bid sent to market, awaiting resultsREJECTED— bid was rejected by the marketSUCCESSFUL— bid was accepted by the marketFAILED— bid never reached the market (e.g. an outage spanning gate closure) and the auction window has passed; this is a terminal state with no settlement implicationFALLBACK— the market failed to clear (RAM Fallback) and the TSO Ersatzpreis applies for the delivery periodCANCELLED— the bid was withdrawn before the market closed, either by you or because the asset could no longer back it; it holds no capacity and has no settlement implication
The aFRR energy market is activation-based. The energy_price_eur_per_mwh field is the submitted offer price in EUR/MWh, while financial settlement is determined after delivery from the actual activated energy. This response exposes awarded/activated power and the submitted offer, not a final settlement price.
accepted_power_mw and offered_power_mw are in MW.
Path parameters
- asset_idREQUIRED
- string
Query parameters
- start_time
- string<date-time>Return results whose bid end_time is strictly after this timestamp. With end_time, the API matches result intervals overlapping the half-open window [start_time, end_time). Results do not need to be fully contained in the window.
- end_time
- string<date-time>Return results whose bid start_time is strictly before this timestamp. With start_time, the API matches result intervals overlapping the half-open window [start_time, end_time).
- product
- enum
posnegFilter by product. Provide a single value:pos(aFRR+) orneg(aFRR-). - status
- enum
PENDINGSUBMITTEDREJECTEDSUCCESSFULFAILEDFALLBACKCANCELLEDFilter by bid status. Valid values:pending,submitted,successful,rejected,failed,fallback,cancelled.
curl "https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/energy/results" \
-H "Authorization: Bearer $EBX_TOKEN"import httpx
r = httpx.get(
"https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/energy/results",
headers={"Authorization": f"Bearer {token}"},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/markets/afrr/energy/results", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();{
"results": [
{
"start_time": "2026-09-10T13:30:00Z",
"end_time": "2026-09-10T13:30:00Z",
"product": "pos",
"status": "PENDING",
"accepted_power_mw": 100,
"energy_price_eur_per_mwh": 35.8,
"offered_power_mw": 100,
"external_origin": false
}
]
}- 401no valid token
- 403scope missing
- 404unknown or not visible
- 422validation failed
- 429rate limited