API reference
Markets · FCR
FCR bids for an asset, the valid bid intervals, and the auction results.
GET/v1/assets/{asset_id}/markets/fcr/bids
Get FCR bids
market.read
Get bids for FCR (Frequency Containment Reserve) for a specific asset.
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
If no filters are provided, all bids will be returned.
Since FCR is symmetric, the product is not specified. The bids are for both positive and negative reserve.
Bids for the last 30 days can be retrieved.
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. With start_time, the API matches bids overlapping the half-open window [start_time, end_time).
curl "https://api.ebx.energy/v1/assets/DE_BESS_01/markets/fcr/bids" \
-H "Authorization: Bearer $EBX_TOKEN"import httpx
r = httpx.get(
"https://api.ebx.energy/v1/assets/DE_BESS_01/markets/fcr/bids",
headers={"Authorization": f"Bearer {token}"},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/markets/fcr/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",
"capacity_price_eur_per_mw": 50.1,
"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>"
}
}
]
}- 401no valid token
- 403scope missing
- 404unknown or not visible
- 422validation failed
- 429rate limited
POST/v1/assets/{asset_id}/markets/fcr/bids
Submit FCR bids
market.write
Post bids for FCR (Frequency Containment Reserve) for a specific asset.
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, but does not need to match the entire window. Any block inside replace_scope that is not present in bids remains deleted.
Bids in the FCR market are symmetric, meaning that the same bid can be used for both positive and negative reserve.
Bids can be placed from 11:00 AM CET/CEST one week before the delivery day (i.e. D-7).
Gate Closure Times:
- EBX deadline (recommended): D-1 07:30 CET — submitting before this gives us a buffer to forward your bid to the market.
- Regelleistung gate (enforced): D-1 08: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.
The start_time and end_time fields are required in UTC format (ISO 8601). 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/fcr/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"
},
"bids": [
{
"start_time": "2025-01-02T23:00:00Z",
"end_time": "2025-01-03T03:00:00Z",
"capacity_price_eur_per_mw": 50.10,
"offered_power_mw": 100
},
{
"start_time": "2025-01-03T03:00:00Z",
"end_time": "2025-01-03T07:00:00Z",
"capacity_price_eur_per_mw": 50.10,
"offered_power_mw": 100
}
]
}'In the example above, all pending FCR bids between 2025-01-02T23:00:00Z and 2025-01-03T23:00:00Z are deleted first. Only the two provided blocks are recreated. Any other block in that window remains deleted.
Idempotency is per bid using idempotency_key on each bid object. Scope is defined by the endpoint and the bid interval (start_time, end_time). 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 is treated as a different bid scope.
When placing bids, the API validates that each bid interval matches a known tender from the market calendar. If the interval does not match, a 422 error is returned with suggested valid intervals.
Prequalified capacity ceiling (enforced): For each 15-minute delivery period, the power in this request plus the power already committed by your other FCR bids must not exceed the asset's prequalified FCR capacity, which is a single symmetric figure covering both directions. Bids with status PENDING, SUBMITTED, SUCCESSFUL or FALLBACK count as committed; REJECTED, FAILED and CANCELLED bids release their capacity. A partially awarded SUCCESSFUL bid counts only at the power it won.
If any period would be exceeded, the entire request is rejected with HTTP 409 and error code FCR_PREQUALIFIED_CAPACITY_EXCEEDED — nothing is stored, including the bids that would have fit. The response detail lists every breached period with 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 is enforced as a real limit of zero: no power may be committed at all, and every 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.
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 — query the result endpoints to inspect terminal status.
Path parameters
- asset_idREQUIRED
- string
Request body · required
- bidsREQUIRED
- array[object]
- 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/fcr/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","capacity_price_eur_per_mw":50.1,"offered_power_mw":100}],"mode":"CREATE","replace_scope":{"start_time":"2026-09-10T13:30:00Z","end_time":"2026-09-10T13:30:00Z"}}'import httpx
r = httpx.post(
"https://api.ebx.energy/v1/assets/DE_BESS_01/markets/fcr/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",
"capacity_price_eur_per_mw": 50.1,
"offered_power_mw": 100,
},
],
"mode": "CREATE",
"replace_scope": {
"start_time": "2026-09-10T13:30:00Z",
"end_time": "2026-09-10T13:30:00Z",
},
},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/markets/fcr/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","capacity_price_eur_per_mw":50.1,"offered_power_mw":100}],"mode":"CREATE","replace_scope":{"start_time":"2026-09-10T13:30:00Z","end_time":"2026-09-10T13:30:00Z"}}),
});
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",
"capacity_price_eur_per_mw": 50.1,
"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"
],
"warnings": [
"<string>"
],
"updated_count": 0
}- 400malformed request
- 401no valid token
- 403scope missing
- 404unknown or not visible
- 409state conflict
- 422validation failed
- 429rate limited
DELETE/v1/assets/{asset_id}/markets/fcr/bids
Delete FCR bids by time range
market.write
Delete bids for FCR (Frequency Containment Reserve) for a specific asset by time range.
Use the start_time and end_time fields to specify which bids to delete. Matching is strict: only bids with exactly the same start/end interval are deleted.
Gate Closure Times:
- EBX deadline (recommended): D-1 07:30 CET — deleting before this gives us a buffer to forward your deletion to the market.
- Regelleistung gate (enforced): D-1 08: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_time
- string<date-time>Start time for filtering results
- end_time
- string<date-time>End time for filtering results
curl -X DELETE "https://api.ebx.energy/v1/assets/DE_BESS_01/markets/fcr/bids" \
-H "Authorization: Bearer $EBX_TOKEN"import httpx
r = httpx.delete(
"https://api.ebx.energy/v1/assets/DE_BESS_01/markets/fcr/bids",
headers={"Authorization": f"Bearer {token}"},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/markets/fcr/bids", {
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/fcr/bids/{bid_id}
Update an FCR bid
market.write
Update bids in the Frequency Containment Reserve (FCR) 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 07:30 CET — updating before this gives us a buffer to forward your update to the market.
- Regelleistung gate (enforced): D-1 08: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 to the bid before it 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 cannot be changed after a bid has been submitted (the tender would change). Only offered_power_mw and capacity_price_eur_per_mw 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 FCR_PREQUALIFIED_CAPACITY_EXCEEDED if the new value would push the total committed FCR power above the asset's prequalified FCR capacity in any 15-minute 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. A prequalified capacity of 0 is enforced as a real limit of zero, so no power may be committed 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 bid in ISO 8601 format (UTC, 15-minute aligned)
- end_timeREQUIRED
- string<date-time>The end time for the bid in ISO 8601 format (UTC, 15-minute aligned)
- 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); the limit is configuration, not part of this schema, so it can track the market. - 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/fcr/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","capacity_price_eur_per_mw":50.1,"offered_power_mw":100}'import httpx
r = httpx.put(
"https://api.ebx.energy/v1/assets/DE_BESS_01/markets/fcr/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",
"capacity_price_eur_per_mw": 50.1,
"offered_power_mw": 100,
},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/markets/fcr/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","capacity_price_eur_per_mw":50.1,"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",
"capacity_price_eur_per_mw": 50.1,
"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>"
]
}- 400malformed request
- 401no valid token
- 403scope missing
- 404unknown or not visible
- 409state conflict
- 422validation failed
- 429rate limited
DELETE/v1/assets/{asset_id}/markets/fcr/bids/{bid_id}
Delete an FCR bid by ID
market.write
Delete bids for FCR (Frequency Containment Reserve) for a specific asset.
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 07:30 CET — deleting before this gives us a buffer to forward your deletion to the market.
- Regelleistung gate (enforced): D-1 08: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/fcr/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/fcr/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/fcr/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/fcr/bids/intervals
Get valid FCR bid intervals
market.read
Discover valid bid intervals for FCR (Frequency Containment Reserve).
Returns a list of time intervals during which 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/fcr/bids/intervals" \
-H "Authorization: Bearer $EBX_TOKEN"import httpx
r = httpx.get(
"https://api.ebx.energy/v1/assets/DE_BESS_01/markets/fcr/bids/intervals",
headers={"Authorization": f"Bearer {token}"},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/markets/fcr/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/fcr/results
Get FCR results
market.read
Get the results for FCR (Frequency Containment Reserve) for a specific asset for a specific date.
Results for the bids are available c.a. 08:30AM CET/CEST one day before delivery day (i.e. D - 1). It cannot be guaranteed that results will be available at this time, as some results may be delayed due to external factors.
Use start_time and/or end_time to filter results by interval overlap. 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
You can additionally filter by bid_id or status. If no filters are provided, all results will be returned.
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
The response contains a list of results for the bids. The status field indicates whether the bid was accepted. The accepted_power_mw field indicates the amount of power that was accepted in MW. The capacity_price_eur_per_mw field indicates the offered bid price in EUR/MW. The offered_power_mw field indicates the amount of power that was offered in MW. The clearing_price_eur_per_mw field indicates the FCR clearing price in EUR/MW for the accepted power.
In Germany, FCR capacity is settled pay-as-clear. The capacity_price_eur_per_mw field is the customer's offered bid price, while the clearing_price_eur_per_mw field is the settlement-relevant market clearing price for awarded capacity.
FCR has no separate balancing-energy settlement in this API response. Remuneration is represented via the awarded capacity and its clearing price.
The top-level start_time and end_time in the response describe the span of the returned result intervals, not necessarily the exact query window.
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).
- bid_id
- string<uuid>Unique identifier for the bid
- 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/fcr/results" \
-H "Authorization: Bearer $EBX_TOKEN"import httpx
r = httpx.get(
"https://api.ebx.energy/v1/assets/DE_BESS_01/markets/fcr/results",
headers={"Authorization": f"Bearer {token}"},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/markets/fcr/results", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();{
"start_time": "2026-09-10T13:30:00Z",
"end_time": "2026-09-10T13:30:00Z",
"results": [
{
"start_time": "2026-09-10T13:30:00Z",
"end_time": "2026-09-10T13:30:00Z",
"status": "PENDING",
"accepted_power_mw": 100,
"capacity_price_eur_per_mw": 50.1,
"offered_power_mw": 100,
"clearing_price_eur_per_mw": 50.1,
"external_origin": false
}
]
}- 401no valid token
- 403scope missing
- 404unknown or not visible
- 422validation failed
- 429rate limited