Skip to content

Pricing, Tariffs & Market Data API

Base paths:

  • /core/api/communities/{community_id}/dso
  • /core/api/communities/{community_id}/erse
  • /core/api/erse (global ERSE losses and tariffs)
  • /core/api/communities/{community_id}/coefficients
  • /core/api/communities/{community_id}/bilateral-prices
  • /core/api/communities/{community_id}/opportunity_costs
  • /core/api/omie

These endpoints hold the inputs that market clearing and settlement consume: the DSO measurements and allocation data, the ERSE grid-access tariffs and network losses, the allocation coefficients, the bilateral prices, the opportunity costs and the OMIE day-ahead prices. Most of them are time series on the 15-minute grid, so uploading them for past intervals reopens the sessions they touch (see Business Validity).

Common conventions:

  • Time zone. CSV files use Data (DD/MM/YYYY) and Hora (HH:MM) in Europe/Lisbon. Hora is the end of the interval: the platform subtracts 15 minutes, so the row 00:15 is stored as the interval starting at 00:00. JSON payloads carry the interval start in datetime_timestamp (ISO 8601). Templates are listed in Upload Templates.
  • Asynchronous uploads. Most JSON and file uploads are queued on a Celery worker and answer 201 with {"message": "Processing started", "task_id": "..."}. Poll GET /core/api/communities/{community_id}/tasks/{task_id}?group_task=false until state is SUCCESS or FAILURE. Errors raised inside the task (for example an unknown meter identifier) are only visible there, not in the 201 response.
  • Pagination endpoints. All .../pagination endpoints take limit and offset and return total_count, data, distinct_dates and distinct_times (the distinct values available for the active filters, used by table filter UIs). Filter date is YYYY-MM-DD and time is HH:MM:SS. sort_direction=asc sorts ascending; any other value sorts descending.
  • Permissions. Community-scoped endpoints require a verified user who belongs to the community: members (MEMBER) may only call GET endpoints, community admins (ADMIN) and platform admins (SUPER_USER) may also write. Global endpoints under /core/api/erse are readable by any verified user and writable only by a platform admin. Missing membership or write permission returns 403.

Endpoint summary

Method Path Purpose
POST /core/api/communities/{community_id}/dso Upload DSO loads and aggregator loads as JSON
POST /core/api/communities/{community_id}/dso/file_upload Upload a DSO file (hidden from OpenAPI)
GET /core/api/communities/{community_id}/dso/pagination Paginated DSO loads
GET /core/api/communities/{community_id}/dso/aggregator/pagination Paginated DSO aggregator loads
GET /core/api/communities/{community_id}/dso/metrics/kpis DSO totals for a date range
GET /core/api/communities/{community_id}/dso/metrics Community DSO loads at a chosen granularity
GET /core/api/communities/{community_id}/dso/metrics/meters DSO loads per meter at a chosen granularity
GET /core/api/communities/{community_id}/dso/aggregator/metrics/meters DSO aggregator loads per meter at a chosen granularity
GET /core/api/communities/{community_id}/dso/metrics/meters/share Each meter's share of injection, consumption and grid self-consumption
GET /core/api/communities/{community_id}/erse/tariffs/self_consumption_tariffs_between_dates ERSE self-consumption tariff for every CPE pair and 15-minute interval
POST /core/api/erse/losses Upload the global network losses table (CSV)
GET /core/api/erse/losses/years List years with loaded losses
GET /core/api/erse/losses/{year} Read all losses of a year
GET /core/api/erse/losses/{year}/pagination Paginated losses of a year
DELETE /core/api/erse/losses/{year} Delete all losses of a year
POST /core/api/erse/tariffs Upload the global ERSE tariffs table (XLSX)
GET /core/api/erse/tariffs/validities List loaded tariff validity dates
GET /core/api/erse/tariffs/energy/value Look up one energy tariff value
GET /core/api/erse/tariffs/{validity} Read all tariffs of a validity
DELETE /core/api/erse/tariffs/{validity} Delete all tariffs of a validity
POST /core/api/communities/{community_id}/coefficients/fixed/file_upload Upload fixed allocation coefficients as CSV (hidden from OpenAPI)
POST /core/api/communities/{community_id}/coefficients/fixed Upload fixed allocation coefficients as JSON
GET /core/api/communities/{community_id}/coefficients/fixed/pagination Paginated fixed allocation coefficients
POST /core/api/communities/{community_id}/bilateral-prices Upload per-interval bilateral prices as JSON
POST /core/api/communities/{community_id}/bilateral-prices/file_upload Upload per-interval bilateral prices as CSV (hidden from OpenAPI)
GET /core/api/communities/{community_id}/bilateral-prices/by_provider Bilateral prices of one provider over a date range
GET /core/api/communities/{community_id}/bilateral-prices/pagination Paginated per-interval bilateral prices
POST /core/api/communities/{community_id}/bilateral-prices/fixed Create or replace fixed bilateral prices
GET /core/api/communities/{community_id}/bilateral-prices/fixed List the fixed bilateral prices in force today
PUT /core/api/communities/{community_id}/bilateral-prices/fixed/{receiver_user_id}/{provider_user_id} Set the price of a fixed bilateral pair for a period
DELETE /core/api/communities/{community_id}/bilateral-prices/fixed/{receiver_user_id}/{provider_user_id} Remove the price of a fixed bilateral pair from a date
GET /core/api/communities/{community_id}/opportunity_costs Uploaded opportunity cost of one meter at one interval
GET /core/api/communities/{community_id}/opportunity_costs/by_meter Opportunity costs of one meter over a time range
GET /core/api/communities/{community_id}/opportunity_costs/by_community Opportunity costs of all active meters of the community
POST /core/api/communities/{community_id}/opportunity_costs/file_upload Upload opportunity costs as CSV (hidden from OpenAPI)
POST /core/api/communities/{community_id}/opportunity_costs Upload opportunity costs as JSON
GET /core/api/communities/{community_id}/opportunity_costs/pagination Paginated opportunity costs
GET /core/api/omie OMIE prices, optionally aggregated
GET /core/api/omie/pagination Paginated OMIE prices

DSO

Data sent by the distribution system operator (DSO) for the community's CPEs and aggregator meters. Each row is one meter and one 15-minute interval. Energy values are in kWh. See Meters for CPE and aggregator meters and Settlement for how the values are used.

Upload DSO Data

POST /core/api/communities/{community_id}/dso

Uploads DSO loads (per CPE) and, optionally, DSO aggregator loads as JSON. The request is processed in the background: rows for the same (community_id, datetime_timestamp, meter_id) replace the existing ones, market sessions are created (or reopened) for the timestamps in dso_loads, and sessions touching those timestamps are invalidated.

Auth / permissions: community admin or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
dso_loads body array Yes Rows below. Must be present; may be [] only if dso_aggregator_loads has rows
dso_aggregator_loads body array No Aggregator rows below. Defaults to []

dso_loads[] fields: datetime_timestamp (datetime, interval start), meter_id (UUID), community_id (UUID), energy_surplus, imputed_energy, grid_sc, grid_injection (floats, kWh), consumption (float, optional) and the stage code of each value (energy_surplus_stage, imputed_energy_stage, grid_sc_stage, grid_injection_stage, consumption_stage optional; integers as reported by the DSO).

dso_aggregator_loads[] fields: datetime_timestamp, meter_id, community_id, total_surplus, sharing_energy (floats, kWh), total_surplus_stage, sharing_energy_stage (integers).

Example

curl -X POST https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/dso \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "dso_loads": [
      {
        "datetime_timestamp": "2023-05-01T00:30:00+01:00",
        "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
        "community_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "energy_surplus": 0.0, "energy_surplus_stage": 0,
        "imputed_energy": 0.0, "imputed_energy_stage": 0,
        "grid_sc": 0.0, "grid_sc_stage": 0,
        "consumption": 18.81, "consumption_stage": 0,
        "grid_injection": 0.0, "grid_injection_stage": 0
      }
    ],
    "dso_aggregator_loads": [
      {
        "datetime_timestamp": "2023-05-01T00:30:00+01:00",
        "meter_id": "1b4e28ba-2fa1-11d2-883f-0016d3cca427",
        "community_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "total_surplus": 0.0, "total_surplus_stage": 0,
        "sharing_energy": 0.0, "sharing_energy_stage": 0
      }
    ]
  }'

Response (abridged)

{ "message": "Processing started", "task_id": "9b2c1a52-0d49-4a5f-8c2e-2f7a4e1d6b10" }

Tip

  • Returns 400 with {"message": "No DSO data provided."} when both lists are empty.
  • Because the body has two array fields, they must be sent inside one JSON object (dso_loads / dso_aggregator_loads), not as a bare array.
  • Missing numeric values are stored as 0. Meters and community are validated in the background task, so check the task status for NotFound or inactive-object errors.
  • Uploading DSO data invalidates the affected sessions: they go back to OPEN and their transactions and settlement outputs are deleted. Re-run market clearing and settlement.
  • As implemented, the task finishes with an error if dso_loads has rows and dso_aggregator_loads is empty. Send at least one aggregator row with the loads, or check the task status after every upload.

Upload DSO File

POST /core/api/communities/{community_id}/dso/file_upload

Uploads a DSO file. The meter identifiers in the file (CPE codes) are resolved to community meters of type CPE or AGGREGATOR_METER as they were at each timestamp. Values are processed like Upload DSO Data.

Hidden endpoint

This route is excluded from the OpenAPI schema (include_in_schema=False) but is used by the web application.

Auth / permissions: community admin or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
file_upload body (multipart) file Yes File sent with content type text/csv

Example

curl -X POST https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/dso/file_upload \
  -H "Authorization: Bearer $TOKEN" \
  -F "file_upload=@dso.csv;type=text/csv"

Each line of the file is identifier;code;unit;YYYYMMDD;flag;HHMM|value|state;HHMM|value|state;..., for example:

PT0002000000000000AA;424;kW;20230501;I;0015|75.25|0;0030|71.75|0;0045|70.5|0

code selects the variable: 424 grid injection, 423 consumption, 413 energy surplus, 415 imputed energy, 418 grid self-consumption (loads) and 421 total surplus, 452 sharing energy (aggregator loads). These are the same codes listed in the header of the DSO template in Upload Templates.

Response (abridged)

{ "message": "Processing started", "task_id": "9b2c1a52-0d49-4a5f-8c2e-2f7a4e1d6b10" }

Tip

  • Anything other than text/csv is rejected (InvalidDocumentType, returned as 404).
  • Rows with unit kW are converted to kWh by dividing by 4 (15-minute intervals). The HHMM token is the interval end; the stored datetime_timestamp is the interval start, in Europe/Lisbon with DST handled.
  • The state value of each token is stored as the *_stage of the variable.
  • Unknown identifiers fail the background task; check it with the task status endpoint.
  • Sessions are created or reopened and invalidated exactly as in the JSON upload.

List DSO Loads (Paginated)

GET /core/api/communities/{community_id}/dso/pagination

Returns DSO loads of all meters of the community, one row per meter and interval, with the meter identifier. Used by the DSO data table.

Auth / permissions: any community member or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
limit query integer Yes Page size
offset query integer No Rows to skip. Default 0
date query string No Filter by date, YYYY-MM-DD
time query string No Filter by time, HH:MM:SS
meter_id query string No Filter by meter UUID
sort_column query string No date, time, meter_id (sorts by meter identifier) or any column of the load row (for example grid_injection)
sort_direction query string No asc for ascending, otherwise descending

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/dso/pagination?limit=50&offset=0&date=2023-05-01&sort_column=time&sort_direction=asc" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

{
  "total_count": 96,
  "data": [
    {
      "datetime_timestamp": "2023-04-30T23:30:00+00:00",
      "community_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "meter_identifier": "PT0002000000000000AA",
      "energy_surplus": 0.0,
      "imputed_energy": 0.0,
      "grid_sc": 0.0,
      "consumption": 18.81,
      "grid_injection": 0.0
    }
  ],
  "distinct_dates": [{ "date": "2023-05-01" }],
  "distinct_times": [{ "time": "00:30:00" }]
}

Tip

Without sort_column the order is not defined; always send a sort when paging through many rows. Energy is in kWh.

List DSO Aggregator Loads (Paginated)

GET /core/api/communities/{community_id}/dso/aggregator/pagination

Same as List DSO Loads, for the aggregator-meter data (total_surplus and sharing_energy).

Auth / permissions: any community member or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
limit query integer Yes Page size
offset query integer No Rows to skip. Default 0
date query string No Filter by date, YYYY-MM-DD
time query string No Filter by time, HH:MM:SS
meter_id query string No Filter by meter UUID
sort_column query string No date, time, meter_id or a column of the row
sort_direction query string No asc for ascending, otherwise descending

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/dso/aggregator/pagination?limit=50&offset=0" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

{
  "total_count": 96,
  "data": [
    {
      "datetime_timestamp": "2023-04-30T23:30:00+00:00",
      "community_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "meter_id": "1b4e28ba-2fa1-11d2-883f-0016d3cca427",
      "meter_identifier": "PT0002000000000001AA",
      "total_surplus": 4.2,
      "sharing_energy": 3.1
    }
  ],
  "distinct_dates": [{ "date": "2023-05-01" }],
  "distinct_times": [{ "time": "00:30:00" }]
}

Tip

The aggregator meter is a virtual meter that the DSO reports allocation data for (see Meters). Values are kWh.

Get DSO KPIs

GET /core/api/communities/{community_id}/dso/metrics/kpis

Returns the totals of the DSO values over a date range: one number for the whole community, summed across meters and intervals.

Auth / permissions: any community member or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
start_date query date Yes First day, YYYY-MM-DD
end_date query date Yes Last day, inclusive (the whole day is included)

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/dso/metrics/kpis?start_date=2023-05-01&end_date=2023-05-31" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

{
  "total_grid_injection": 1520.4,
  "total_consumption": 3980.7,
  "total_imputed_energy": 910.2,
  "total_grid_sc": 870.1,
  "total_energy_surplus": 610.3,
  "total_surplus_energy_aggregator": 640.0,
  "total_sharing_energy": 598.5
}

Tip

All totals are in kWh and are 0.0 when there is no data in the range. total_surplus_energy_aggregator and total_sharing_energy come from the aggregator loads, the others from the CPE loads.

Get DSO Loads by Granularity

GET /core/api/communities/{community_id}/dso/metrics

Returns the community's DSO loads summed over all meters and aggregated to the requested granularity. Use it for charts.

Auth / permissions: any community member or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
start_date query date Yes First day, YYYY-MM-DD
end_date query date Yes Last day, inclusive
granularity query string No MIN15 (default), HOUR, DAY, MONTH or YEAR

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/dso/metrics?start_date=2023-05-01&end_date=2023-05-02&granularity=HOUR" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

[
  {
    "datetime_timestamp": "2023-05-01T00:00:00+01:00",
    "grid_injection": 0.0,
    "energy_surplus": 0.0,
    "consumption": 75.3,
    "grid_sc": 0.0
  }
]

Tip

Values are summed per bucket (kWh) and buckets are computed in Europe/Lisbon. Returns [] when there is no data. Only these four fields are returned; use Get DSO KPIs for imputed energy.

Get DSO Loads by Meter

GET /core/api/communities/{community_id}/dso/metrics/meters

Same aggregation as Get DSO Loads by Granularity, but kept separate per meter. The result is an object keyed by meter UUID.

Auth / permissions: any community member or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
start_date query date Yes First day, YYYY-MM-DD
end_date query date Yes Last day, inclusive
granularity query string No MIN15 (default), HOUR, DAY, MONTH or YEAR

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/dso/metrics/meters?start_date=2023-05-01&end_date=2023-05-01&granularity=DAY" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

{
  "7c9e6679-7425-40de-944b-e07fc1f90ae7": [
    {
      "datetime_timestamp": "2023-05-01T00:00:00+01:00",
      "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "grid_injection": 0.0,
      "energy_surplus": 0.0,
      "consumption": 1810.2,
      "grid_sc": 0.0
    }
  ]
}

Tip

Returns {} when there is no data. Values are summed per bucket (kWh).

Get DSO Aggregator Loads by Meter

GET /core/api/communities/{community_id}/dso/aggregator/metrics/meters

Aggregator loads (total_surplus, sharing_energy) per aggregator meter at the requested granularity, as an object keyed by meter UUID.

Auth / permissions: any community member or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
start_date query date Yes First day, YYYY-MM-DD
end_date query date Yes Last day, inclusive
granularity query string No MIN15 (default), HOUR, DAY, MONTH or YEAR

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/dso/aggregator/metrics/meters?start_date=2023-05-01&end_date=2023-05-07&granularity=DAY" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

{
  "1b4e28ba-2fa1-11d2-883f-0016d3cca427": [
    {
      "datetime_timestamp": "2023-05-01T00:00:00+01:00",
      "meter_id": "1b4e28ba-2fa1-11d2-883f-0016d3cca427",
      "total_surplus": 42.0,
      "sharing_energy": 31.5
    }
  ]
}

Tip

Returns {} when there is no aggregator data in the range. Values are kWh.

Get DSO Meter Shares

GET /core/api/communities/{community_id}/dso/metrics/meters/share

Returns each meter's fraction of the community totals of grid injection, consumption and grid self-consumption over the date range.

Auth / permissions: any community member or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
start_date query date Yes First day, YYYY-MM-DD
end_date query date Yes Last day, inclusive

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/dso/metrics/meters/share?start_date=2023-05-01&end_date=2023-05-31" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

[
  {
    "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "grid_injection_share": 0.62,
    "consumption_share": 0.35,
    "grid_sc_share": 0.4
  }
]

Tip

Shares are fractions between 0 and 1 that add up to 1 across meters for each field. A share is 0.0 when the community total of that field is 0. Returns [] when there is no data.

ERSE

Routes mounted on /core/api/communities/{community_id}/erse. The global ERSE tables (losses and tariffs) are in ERSE Losses and ERSE Tariffs.

Get Self-Consumption Tariffs Between Dates

GET /core/api/communities/{community_id}/erse/tariffs/self_consumption_tariffs_between_dates

Computes the ERSE grid-access tariff charged on self-consumed energy for every ordered pair of distinct CPEs of the community (provider and receiver) and every 15-minute interval between two dates. It uses the community's contracts (tariff cycle, voltage levels) and the ERSE tariffs valid on start_date.

Auth / permissions: any community member or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
start_date query date Yes First day, YYYY-MM-DD
end_date query date Yes Last day, inclusive

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/erse/tariffs/self_consumption_tariffs_between_dates?start_date=2025-01-01&end_date=2025-01-01" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

{
  "data": [
    {
      "datetime_timestamp": "2025-01-01 00:00:00+00:00",
      "provider_meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "receiver_meter_id": "9f8b3c1e-4a6d-4d1b-8a52-0c7f6f2d3e11",
      "erse_tariff_value": 0.0123
    }
  ]
}

Tip

  • The response has N x (N - 1) x 96 rows per day for N CPEs, so keep the range short for communities with many CPEs.
  • The 15-minute grid is built from start_date 00:00 to end_date 23:59 in UTC, and datetime_timestamp is returned as a string.
  • Tariffs are taken from the most recent validity on or before start_date, so a range that crosses a tariff validity uses a single table. Values are in EUR/kWh.
  • If ERSE tariffs have not been loaded (see ERSE Tariffs) the tariff values cannot be resolved.

ERSE losses

Global network-loss table, shared by all communities: one fraction (between 0 and 1) per 15-minute interval and voltage level (BT, MT, AT, AT/RNT, MAT). Losses are used to compute the opportunity costs of CPEs from their contracts.

Upload Losses

POST /core/api/erse/losses

Uploads a losses CSV. Rows for the same interval and voltage level replace existing ones, so a year can be corrected by uploading it again.

Auth / permissions: platform admin only (global endpoint, 403 for anyone else).

Parameter In Type Required Description
file body (multipart) file Yes CSV with content type text/csv and exactly the header Data,Hora,BT,MT,AT,AT/RNT,MAT (see template_perdas.csv)

Example

curl -X POST https://<host>/core/api/erse/losses \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@template_perdas.csv;type=text/csv"

The file looks like:

Data,Hora,BT,MT,AT,AT/RNT,MAT
01/01/2025,00:15,0.0938234,0.0362639,0.0129007,0.0226626,0.0173676
01/01/2025,00:30,0.0925803,0.0358695,0.0127697,0.0226626,0.0173676

Response (abridged)

{ "message": "Successfully inserted 480 losses" }

Tip

  • Processed synchronously (no task_id). A wrong content type returns 404 (InvalidDocumentType) and wrong headers or column count 403 (Invalid document).
  • Maximum 36,000 rows per file (about one year of 15-minute data); a bigger file is rejected with an error.
  • Hora is the interval end (00:15 is stored as 00:00). DST changes are handled in Europe/Lisbon.
  • Values are fractions of energy, not percentages: 0.0938 means 9.38%.

List Loss Years

GET /core/api/erse/losses/years

Returns the years for which losses are loaded.

Auth / permissions: any verified user.

Example

curl https://<host>/core/api/erse/losses/years \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

[{ "year": 2024 }, { "year": 2025 }]

Tip

Returns [] (not 404) when no losses are loaded. The order of the years is not guaranteed.

Get Losses by Year

GET /core/api/erse/losses/{year}

Returns every loss row of a year: one entry per interval and voltage level.

Auth / permissions: any verified user.

Parameter In Type Required Description
year path integer Yes Calendar year, for example 2025

Example

curl https://<host>/core/api/erse/losses/2025 \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

[
  { "datetime_timestamp": "2025-01-01T00:00:00+00:00", "voltage_level": "BT", "value": 0.0938234 },
  { "datetime_timestamp": "2025-01-01T00:00:00+00:00", "voltage_level": "MT", "value": 0.0362639 }
]

Tip

A full year is about 175,000 rows (five voltage levels x 35,000 intervals); prefer Get Losses by Year (Paginated) for tables. Returns [] when the year has no data.

Get Losses by Year (Paginated)

GET /core/api/erse/losses/{year}/pagination

Paginated losses of a year with optional filters, for the losses table.

Auth / permissions: any verified user.

Parameter In Type Required Description
year path integer Yes Calendar year
limit query integer Yes Page size
offset query integer Yes Rows to skip
date query string No Filter by date, YYYY-MM-DD
time query string No Filter by time, HH:MM:SS
voltage_level query string No One of BT, MT, AT, AT/RNT, MAT
sortColumn query string No date, time or a column (datetime_timestamp, voltage_level, value)
sortDirection query string No asc or desc. Both sortColumn and sortDirection are required to sort

Example

curl "https://<host>/core/api/erse/losses/2025/pagination?limit=20&offset=0&voltage_level=BT&sortColumn=datetime_timestamp&sortDirection=asc" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

{
  "total_count": 35040,
  "data": [
    { "datetime_timestamp": "2025-01-01T00:00:00+00:00", "voltage_level": "BT", "value": 0.0938234 }
  ],
  "distinct_dates": [{ "date": "2025-01-01" }],
  "distinct_times": [{ "time": "00:00:00" }]
}

Tip

This endpoint uses camelCase sortColumn / sortDirection, unlike the community-scoped pagination endpoints. Without both, rows are returned newest first.

Delete Losses by Year

DELETE /core/api/erse/losses/{year}

Deletes all loss rows of a year, for every voltage level.

Auth / permissions: platform admin only.

Parameter In Type Required Description
year path integer Yes Calendar year to delete

Example

curl -X DELETE https://<host>/core/api/erse/losses/2025 \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

HTTP/1.1 204 No Content

Tip

The delete is immediate and affects all communities. It returns 204 even if the year had no data. Opportunity costs that were calculated earlier are not recalculated until the market runs again.

ERSE tariffs

Global ERSE grid-access tariff table, shared by all communities. A tariff set is identified by its validity date (the date the ERSE publication takes effect). Rows have a type (POWER, ENERGY or CONTRACTED_POWER), the voltage level of the consumption point (ic_voltage_level) and of the injection point (ipr_voltage_level), trimester (1 to 4), period (for example PONTA, CHEIAS, VAZIO), cieg_discount ("0", "50" or "100") and value. See Settlement for how these tariffs enter the settlement.

Upload Tariffs

POST /core/api/erse/tariffs

Uploads the ERSE tariffs spreadsheet. The validity date is read from the file itself, and an existing set with the same validity is replaced.

Auth / permissions: platform admin only.

Parameter In Type Required Description
file body (multipart) file Yes .xlsx with content type application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, in the layout of template_tarifas_ERSE.xlsx

Example

curl -X POST https://<host>/core/api/erse/tariffs \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@template_tarifas_ERSE.xlsx;type=application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"

Response (abridged)

{ "message": "Successfully inserted 612 tariffs" }

Tip

  • The data is read from the sheet Tarifas Acesso_Autoconsumo (validity in cell C1, tables from row 9). A different layout fails with 403 Invalid document.
  • Any other content type returns 404 (InvalidDocumentType). A database failure returns a DatabaseError.
  • Replacement is by validity: uploading the same file again does not duplicate rows, but other validities are untouched.

List Tariff Validities

GET /core/api/erse/tariffs/validities

Returns the validity dates for which a tariff set is loaded.

Auth / permissions: any verified user.

Example

curl https://<host>/core/api/erse/tariffs/validities \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

[{ "validity": "2024-01-01" }, { "validity": "2025-01-01" }]

Tip

Returns [] (not 404) when no tariffs are loaded. Use these dates with Get Tariffs by Validity.

Get Energy Tariff Value

GET /core/api/erse/tariffs/energy/value

Looks up the value of one ENERGY tariff for a date, a pair of voltage levels, a period and a CIEG discount. The trimester is derived from the month of validity.

Auth / permissions: no authentication dependency is declared on this route in the code.

Parameter In Type Required Description
validity query date Yes Date the tariff must be valid on, YYYY-MM-DD (also determines the trimester)
ic_voltage_level query string Yes Voltage level of the consumption point (for example BT, MT, AT, AT/RNT, BTN simples)
ipr_voltage_level query string Yes Voltage level of the injection point
period query string Yes PONTA, CHEIAS, VAZIO, VAZIO_NORMAL, SUPER_VAZIO, FORA_VAZIO or VENDA_REDE
cieg_discount query string Yes 0, 50 or 100

Example

curl "https://<host>/core/api/erse/tariffs/energy/value?validity=2025-02-10&ic_voltage_level=BT&ipr_voltage_level=BT&period=CHEIAS&cieg_discount=0"

Response (abridged)

0.0123

Tip

  • The response is a bare number (EUR/kWh), not an object.
  • It uses the latest tariff set whose validity is on or before validity. AT/RNT is treated as AT, and the BTN variants and BTE as BT for the injection level. If no exact match exists, the consumption and injection levels are swapped before giving up.
  • Returns 404 when no tariff is found, and 422 for values outside the enums. Encode special characters in voltage levels (AT/RNT is AT%2FRNT, spaces as %20).
  • Results are cached in Redis per combination of parameters and the code sets no expiry, so after reloading tariffs a previously requested combination may still return the cached value.

Get Tariffs by Validity

GET /core/api/erse/tariffs/{validity}

Returns all tariff rows of one validity.

Auth / permissions: any verified user.

Parameter In Type Required Description
validity path date Yes Exact validity date as returned by List Tariff Validities

Example

curl https://<host>/core/api/erse/tariffs/2025-01-01 \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

[
  {
    "id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
    "type": "ENERGY",
    "ic_voltage_level": "BT",
    "ipr_voltage_level": "BT",
    "trimester": 1,
    "period": "CHEIAS",
    "cieg_discount": "0",
    "value": 0.0123,
    "validity": "2025-01-01"
  }
]

Tip

The match is exact: a date that is not a loaded validity returns [], not the closest earlier set. Rows of type POWER and CONTRACTED_POWER have trimester, period or ipr_voltage_level set to null where they do not apply.

Delete Tariffs by Validity

DELETE /core/api/erse/tariffs/{validity}

Deletes all tariff rows of one validity.

Auth / permissions: platform admin only.

Parameter In Type Required Description
validity path date Yes Validity date to delete

Example

curl -X DELETE https://<host>/core/api/erse/tariffs/2025-01-01 \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

HTTP/1.1 204 No Content

Tip

Returns 204 even if there were no rows. The delete affects all communities: lookups use the latest validity on or before the requested date, so later calculations fall back to an older set if one exists.

Allocation coefficients

Fixed allocation coefficients: the share of the community's surplus allocated to each meter in each 15-minute interval, used when the community's method_alloc_coeff is FIXED (see Allocation Coefficient Methods and OSTEC with Allocation Coefficients).

Upload Fixed Coefficients File

POST /core/api/communities/{community_id}/coefficients/fixed/file_upload

Uploads fixed coefficients from a CSV. CPE codes are mapped to the community's meters.

Hidden endpoint

This route is excluded from the OpenAPI schema (include_in_schema=False) but is used by the web application.

Auth / permissions: community admin or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
file_upload body (multipart) file Yes CSV (text/csv) with exactly the columns Data,Hora,CPE,Coeficiente de partilha, see template_coeff_fixed.csv

Example

curl -X POST https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/coefficients/fixed/file_upload \
  -H "Authorization: Bearer $TOKEN" \
  -F "file_upload=@template_coeff_fixed.csv;type=text/csv"
Data,Hora,CPE,Coeficiente de partilha
01/05/2023,00:15,PT00000000000000000A,0.200
01/05/2023,00:30,PT00000000000000000A,0.400

Response (abridged)

{ "message": "Processing started", "task_id": "9b2c1a52-0d49-4a5f-8c2e-2f7a4e1d6b10" }

Tip

  • A wrong content type returns 404 and a header mismatch 403 (Invalid document), before the task starts.
  • CPE must be the meter identifier of a community meter; unknown identifiers fail the task.
  • Maximum 36,000 rows per file. Rows for the same community, interval and meter are replaced.
  • Sessions are created or reopened (OPEN) for the interval timestamps and the existing results are invalidated.

Upload Fixed Coefficients

POST /core/api/communities/{community_id}/coefficients/fixed

Uploads fixed coefficients as a JSON array, using meter UUIDs.

Auth / permissions: community admin or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
body body array Yes Each item: datetime_timestamp (datetime, interval start), meter_id (UUID of the CPE), value (float, the coefficient)

Example

curl -X POST https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/coefficients/fixed \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '[
    { "datetime_timestamp": "2023-05-01T00:00:00+01:00", "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "value": 0.2 },
    { "datetime_timestamp": "2023-05-01T00:15:00+01:00", "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "value": 0.4 }
  ]'

Response (abridged)

{ "message": "Processing started", "task_id": "9b2c1a52-0d49-4a5f-8c2e-2f7a4e1d6b10" }

Tip

Returns 400 with {"message": "No coefficients data provided."} for an empty array. Processing is in the background (same side effects as the file upload: replace, open and invalidate sessions). The coefficient value is a dimensionless share; the platform does not check that the shares of an interval add up to 1.

List Fixed Coefficients (Paginated)

GET /core/api/communities/{community_id}/coefficients/fixed/pagination

Returns the stored fixed coefficients, one row per meter and interval.

Auth / permissions: any community member or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
limit query integer Yes Page size
offset query integer Yes Rows to skip
date query string No Filter by date, YYYY-MM-DD
time query string No Filter by time, HH:MM:SS
meter_id query UUID No Filter by meter
sort_column query string No date, time, meter_id, value or datetime_timestamp
sort_direction query string No asc for ascending, otherwise descending

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/coefficients/fixed/pagination?limit=50&offset=0&meter_id=7c9e6679-7425-40de-944b-e07fc1f90ae7&sort_column=date&sort_direction=asc" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

{
  "total_count": 2,
  "data": [
    { "datetime_timestamp": "2023-04-30T23:00:00+00:00", "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "value": 0.2 }
  ],
  "distinct_dates": [{ "date": "2023-05-01" }],
  "distinct_times": [{ "time": "00:00:00" }]
}

Tip

The meter_id filter must be a valid UUID (422 otherwise).

Bilateral prices

Prices agreed between a provider and a receiver member, used by the bilateral market rates. There are two forms, selected by the community's market_rate: per-interval prices (BILATERAL_PRICE, TREE_DNAC) and fixed dated prices (FIXED_BILATERAL_PRICE, FIXED_TREE_DNAC). See Bilateral Prices. Prices are in EUR/kWh and refer to members (user IDs), not meters.

Upload Bilateral Prices

POST /core/api/communities/{community_id}/bilateral-prices

Uploads per-interval bilateral prices as JSON. Processed in the background; rows with the same community, interval, receiver and provider replace the existing ones.

Auth / permissions: community admin or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
body body array Yes Each item: datetime_timestamp (datetime, interval start), receiver_user_id (UUID), provider_user_id (UUID), price (float, EUR/kWh)

Example

curl -X POST https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/bilateral-prices \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "datetime_timestamp": "2023-05-01T00:00:00+01:00",
      "receiver_user_id": "2c26b46b-68ff-4c3a-9d4e-1f0a7a2b8c11",
      "provider_user_id": "5d41402a-bc4b-4a2a-9b6e-3e1d7c4f9a22",
      "price": 0.08
    }
  ]'

Response (abridged)

{ "message": "Processing started", "task_id": "9b2c1a52-0d49-4a5f-8c2e-2f7a4e1d6b10" }

Tip

  • Returns 400 with {"message": "No bilateral prices data provided."} for an empty array.
  • Uploading invalidates the sessions of the affected timestamps (back to OPEN; transactions and settlement outputs deleted).
  • Market clearing is refused unless every session being cleared has at least one provider to receiver price; provide all possible pairs when members can act as either side.
  • Only prices of the community's current market_rate mode are used; prices uploaded for the other mode are ignored.

Upload Bilateral Prices File

POST /core/api/communities/{community_id}/bilateral-prices/file_upload

Uploads per-interval bilateral prices from a CSV, using member names instead of IDs.

Hidden endpoint

This route is excluded from the OpenAPI schema (include_in_schema=False) but is used by the web application.

Auth / permissions: community admin or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
file_upload body (multipart) file Yes CSV (text/csv) with exactly the columns Data,Hora,Produtor,Consumidor,Preço, see template_bilateral_prices.csv

Example

curl -X POST https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/bilateral-prices/file_upload \
  -H "Authorization: Bearer $TOKEN" \
  -F "file_upload=@template_bilateral_prices.csv;type=text/csv"
Data,Hora,Produtor,Consumidor,Preço
01/05/2023,00:15,Membro1,Membro2,1
01/05/2023,00:30,Membro1,Membro2,1

Response (abridged)

{ "message": "Processing started", "task_id": "9b2c1a52-0d49-4a5f-8c2e-2f7a4e1d6b10" }

Tip

  • Produtor and Consumidor must match the member name in the community; unknown names fail the task.
  • Wrong content type returns 404 and wrong headers 403, before the task starts. Maximum 36,000 rows.
  • Same replacement and invalidation behaviour as the JSON upload.

Get Bilateral Prices by Provider

GET /core/api/communities/{community_id}/bilateral-prices/by_provider

Returns the per-interval prices set by one provider, for all receivers, between two dates.

Auth / permissions: any community member or platform admin, with data scoping: a MEMBER may only query their own user ID as provider_user_id; a community ADMIN may query any member of the community. Otherwise 403.

Parameter In Type Required Description
community_id path UUID Yes Community
provider_user_id query UUID Yes Selling member
start_date query date Yes First day, YYYY-MM-DD
end_date query date Yes Last day, inclusive

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/bilateral-prices/by_provider?provider_user_id=5d41402a-bc4b-4a2a-9b6e-3e1d7c4f9a22&start_date=2023-05-01&end_date=2023-05-02" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

[
  {
    "community_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "datetime_timestamp": "2023-04-30T23:00:00+00:00",
    "receiver_user_id": "2c26b46b-68ff-4c3a-9d4e-1f0a7a2b8c11",
    "provider_user_id": "5d41402a-bc4b-4a2a-9b6e-3e1d7c4f9a22",
    "price": 0.08
  }
]

Tip

The date range is expanded to the whole of end_date. A 403 Target user is not a member of this community is returned to admins who query a user outside the community.

List Bilateral Prices (Paginated)

GET /core/api/communities/{community_id}/bilateral-prices/pagination

Paginated per-interval bilateral prices of the community.

Auth / permissions: any community member or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
limit query integer Yes Page size
offset query integer Yes Rows to skip
date query string No Filter by date, YYYY-MM-DD
time query string No Filter by time, HH:MM:SS
receiver_user_id query UUID No Filter by receiver
provider_user_id query UUID No Filter by provider
sort_column query string No date, time, receiver_user_id, provider_user_id, price; any other value sorts by datetime_timestamp
sort_direction query string No asc for ascending, otherwise descending

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/bilateral-prices/pagination?limit=50&offset=0&provider_user_id=5d41402a-bc4b-4a2a-9b6e-3e1d7c4f9a22&sort_column=price&sort_direction=desc" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

{
  "total_count": 192,
  "data": [
    {
      "community_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "datetime_timestamp": "2023-04-30T23:00:00+00:00",
      "receiver_user_id": "2c26b46b-68ff-4c3a-9d4e-1f0a7a2b8c11",
      "provider_user_id": "5d41402a-bc4b-4a2a-9b6e-3e1d7c4f9a22",
      "price": 0.08
    }
  ],
  "distinct_dates": [{ "date": "2023-05-01" }],
  "distinct_times": [{ "time": "00:00:00" }]
}

Tip

Only rows where the receiver or the provider has the MEMBER role in the community are listed. Unlike by_provider, no per-user scoping is applied here.

Upsert Fixed Bilateral Prices

POST /core/api/communities/{community_id}/bilateral-prices/fixed

Creates or replaces fixed bilateral prices: one price per provider to receiver pair for a dated period. Processed synchronously.

Auth / permissions: community admin or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
body body array Yes Each item: receiver_user_id (UUID), provider_user_id (UUID), price (float, EUR/kWh), start_date (date), end_date (date, optional)

Example

curl -X POST https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/bilateral-prices/fixed \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "receiver_user_id": "2c26b46b-68ff-4c3a-9d4e-1f0a7a2b8c11",
      "provider_user_id": "5d41402a-bc4b-4a2a-9b6e-3e1d7c4f9a22",
      "price": 0.08,
      "start_date": "2026-01-01",
      "end_date": null
    }
  ]'

Response (abridged)

{ "message": "1 fixed bilateral price(s) upserted." }

Tip

  • Returns 400 with {"message": "No fixed bilateral prices data provided."} for an empty array.
  • start_date and end_date are inclusive whole days in Europe/Lisbon; end_date: null is open-ended. Sending a new price for [start_date, end_date] replaces the overlapping period and keeps the history outside it. See Business Validity.
  • Sessions in the affected period are invalidated (back to OPEN, transactions and settlement outputs deleted).
  • end_date earlier than start_date is not validated at the schema level and fails with a server error.

List Fixed Bilateral Prices

GET /core/api/communities/{community_id}/bilateral-prices/fixed

Lists the fixed bilateral prices in force today (Europe/Lisbon), one entry per provider to receiver pair.

Auth / permissions: any community member or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community

Example

curl https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/bilateral-prices/fixed \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

[
  {
    "community_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "receiver_user_id": "2c26b46b-68ff-4c3a-9d4e-1f0a7a2b8c11",
    "provider_user_id": "5d41402a-bc4b-4a2a-9b6e-3e1d7c4f9a22",
    "price": 0.08,
    "version_id": "6f1ed002-ab5d-42e0-868f-9a2a2ad3d1f4",
    "start_date": "2026-01-01",
    "end_date": null
  }
]

Tip

There is no effective_at parameter: only the version valid today is returned, so pairs whose period ended or has not started yet are omitted.

Update Fixed Bilateral Price

PUT /core/api/communities/{community_id}/bilateral-prices/fixed/{receiver_user_id}/{provider_user_id}

Sets a new price for an existing pair over [start_date, end_date], keeping the price outside that period.

Auth / permissions: community admin or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
receiver_user_id path UUID Yes Buying member
provider_user_id path UUID Yes Selling member
price body float Yes New price, EUR/kWh
start_date body date Yes First day the price applies (inclusive)
end_date body date No Last day (inclusive). null or omitted = open-ended

Example

curl -X PUT https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/bilateral-prices/fixed/2c26b46b-68ff-4c3a-9d4e-1f0a7a2b8c11/5d41402a-bc4b-4a2a-9b6e-3e1d7c4f9a22 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "price": 0.09, "start_date": "2026-07-01", "end_date": null }'

Response (abridged)

{
  "community_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "receiver_user_id": "2c26b46b-68ff-4c3a-9d4e-1f0a7a2b8c11",
  "provider_user_id": "5d41402a-bc4b-4a2a-9b6e-3e1d7c4f9a22",
  "price": 0.09,
  "version_id": "0b8a7c3e-2f1d-4e6a-9c55-7d4f1b2a3c90",
  "start_date": "2026-07-01",
  "end_date": null
}

Tip

  • Returns 404 with {"message": "Fixed bilateral price not found."} when the pair has no fixed price yet; create it with Upsert Fixed Bilateral Prices first.
  • The affected sessions are invalidated (reopened, results deleted); re-run market clearing and settlement.
  • The returned version_id, start_date and end_date describe the version just written.

Delete Fixed Bilateral Price

DELETE /core/api/communities/{community_id}/bilateral-prices/fixed/{receiver_user_id}/{provider_user_id}

Removes the price of a pair from effective_at onwards, or only for [effective_at, end_date] when end_date is given. Earlier history is kept.

Auth / permissions: community admin or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
receiver_user_id path UUID Yes Buying member
provider_user_id path UUID Yes Selling member
effective_at query date Yes First day to remove, YYYY-MM-DD
end_date query date No Last day to remove (inclusive). Omit to remove everything from effective_at

Example

curl -X DELETE "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/bilateral-prices/fixed/2c26b46b-68ff-4c3a-9d4e-1f0a7a2b8c11/5d41402a-bc4b-4a2a-9b6e-3e1d7c4f9a22?effective_at=2026-07-01&end_date=2026-07-31" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

HTTP/1.1 204 No Content

Tip

Returns 404 with {"message": "Fixed bilateral price not found."} when the pair has no fixed price at all. Sessions in the removed period are invalidated. Market clearing with a fixed bilateral rate needs at least one price pair, so removing the last one blocks clearing.

Opportunity costs

Per-CPE, per-interval buy and sell reference prices (l_buy, l_sell, EUR/kWh) that bound the internal market price. They are either calculated by the platform from contracts, ERSE tariffs, losses and OMIE (is_upload = false), or uploaded by the integrator when retailer prices cannot be shared (is_upload = true). Uploaded values take precedence. See Opportunity Costs.

Get Opportunity Cost at a Timestamp

GET /core/api/communities/{community_id}/opportunity_costs

Returns the uploaded opportunity cost of one meter for one interval.

Auth / permissions: any community member or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
meter_id query UUID Yes CPE meter
datetime_timestamp query string Yes Interval start, ISO 8601

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/opportunity_costs?meter_id=7c9e6679-7425-40de-944b-e07fc1f90ae7&datetime_timestamp=2023-05-01T00:00:00%2B01:00" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

{
  "datetime_timestamp": "2023-04-30T23:00:00+00:00",
  "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "meter_name": null,
  "l_buy": 0.2,
  "l_sell": 0.111,
  "is_upload": true
}

Tip

  • Returns 400 for an unparsable timestamp (Invalid datetime format. Use ISO 8601 format.) and 404 (Opportunity cost not found) when no uploaded row exists, even if a calculated one does; use by_meter or by_community to see both.
  • URL-encode + in the offset as %2B.

Get Opportunity Costs by Meter

GET /core/api/communities/{community_id}/opportunity_costs/by_meter

Returns all opportunity costs of one meter in a time range, both calculated and uploaded.

Auth / permissions: any community member or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
meter_id query UUID Yes CPE meter
start_datetime_timestamp query string Yes Range start, ISO 8601
end_datetime_timestamp query string Yes Range end, ISO 8601 (extended to the end of that day)

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/opportunity_costs/by_meter?meter_id=7c9e6679-7425-40de-944b-e07fc1f90ae7&start_datetime_timestamp=2023-05-01T00:00:00&end_datetime_timestamp=2023-05-01T23:45:00" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

[
  {
    "datetime_timestamp": "2023-05-01T00:00:00+00:00",
    "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "meter_name": null,
    "l_buy": 0.2,
    "l_sell": 0.111,
    "is_upload": true
  }
]

Tip

Returns 400 for invalid datetimes. The same interval can appear twice (one is_upload: true, one false); the calculated copy is ignored by the market while an uploaded one exists. Timestamps without an offset are interpreted as naive, so send an explicit offset.

Get Opportunity Costs by Community

GET /core/api/communities/{community_id}/opportunity_costs/by_community

Lists the opportunity costs of all ACTIVE meters of the community, ordered by time and meter, with optional filters.

Auth / permissions: any community member or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
start_datetime query datetime No Only rows at or after this time
end_datetime query datetime No Only rows at or before this time
is_upload query boolean No true for uploaded rows only, false for calculated rows only
limit query integer No Page size. Default 1000
offset query integer No Rows to skip. Default 0
include_meter_name query boolean No When true, fills meter_name with the meter identifier. Default false

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/opportunity_costs/by_community?start_datetime=2023-05-01T00:00:00%2B01:00&end_datetime=2023-05-01T23:45:00%2B01:00&is_upload=true&include_meter_name=true&limit=500" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

[
  {
    "datetime_timestamp": "2023-04-30T23:00:00+00:00",
    "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "meter_name": "ginasio",
    "l_buy": 0.2,
    "l_sell": 0.111,
    "is_upload": true
  }
]

Tip

The response is a plain array with no total count: page by increasing offset until fewer than limit rows come back. Inactive meters are excluded.

Upload Opportunity Costs File

POST /core/api/communities/{community_id}/opportunity_costs/file_upload

Uploads opportunity costs from a CSV, using meter identifiers.

Hidden endpoint

This route is excluded from the OpenAPI schema (include_in_schema=False) but is used by the web application.

Auth / permissions: community admin or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
file_upload body (multipart) file Yes CSV (text/csv) with exactly the columns Data,Hora,Contador,Valor Compra,Valor Venda, see template_opportunity_cost.csv

Example

curl -X POST https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/opportunity_costs/file_upload \
  -H "Authorization: Bearer $TOKEN" \
  -F "file_upload=@template_opportunity_cost.csv;type=text/csv"
Data,Hora,Contador,Valor Compra,Valor Venda
01/05/2023,00:15,ginasio,0.200,0.111
01/05/2023,00:30,ginasio,0.400,0.111

Response (abridged)

{ "message": "Processing started", "task_id": "9b2c1a52-0d49-4a5f-8c2e-2f7a4e1d6b10" }

Tip

  • Contador is the meter identifier; unknown identifiers fail the task. Valor Compra becomes l_buy and Valor Venda becomes l_sell.
  • Wrong content type returns 404 and wrong headers 403. Maximum 36,000 rows.
  • Rows are stored with is_upload = true, replacing earlier uploaded rows for the same interval and meter, and the sessions of those intervals are invalidated.
  • The upload must cover every session and every CPE of the period being cleared, otherwise market clearing is refused.

Upload Opportunity Costs

POST /core/api/communities/{community_id}/opportunity_costs

Uploads opportunity costs as JSON, using meter UUIDs. Processed synchronously.

Auth / permissions: community admin or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
body body array Yes Each item: datetime_timestamp (datetime, interval start), meter_id (UUID), l_buy (float, EUR/kWh), l_sell (float, EUR/kWh)

Example

curl -X POST https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/opportunity_costs \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '[
    { "datetime_timestamp": "2023-05-01T00:00:00+01:00", "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "l_buy": 0.2, "l_sell": 0.111 },
    { "datetime_timestamp": "2023-05-01T00:15:00+01:00", "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "l_buy": 0.4, "l_sell": 0.111 }
  ]'

Response (abridged)

{ "message": "Successfully upserted 2 records." }

Tip

  • Returns 400 (No records provided.) for an empty array and 404 (Some meters were not found in the database.) when any meter_id does not exist.
  • Rows are stored with is_upload = true (any is_upload or meter_name in the body is ignored) and the affected sessions are invalidated, so reopened sessions need market clearing and settlement again.
  • The meters are looked up globally, not only in this community; send only meters of the community.

List Opportunity Costs (Paginated)

GET /core/api/communities/{community_id}/opportunity_costs/pagination

Paginated opportunity costs of the community, for the opportunity cost table.

Auth / permissions: any community member or platform admin.

Parameter In Type Required Description
community_id path UUID Yes Community
limit query integer Yes Page size
offset query integer Yes Rows to skip
date query string No Filter by date, YYYY-MM-DD
time query string No Filter by time, HH:MM:SS
meter_id query UUID No Filter by meter
is_upload query boolean No true uploaded only, false calculated only
sort_column query string No date, time, meter_id (by meter identifier) or a column (datetime_timestamp, l_buy, l_sell)
sort_direction query string No asc for ascending, otherwise descending

Example

curl "https://<host>/core/api/communities/3fa85f64-5717-4562-b3fc-2c963f66afa6/opportunity_costs/pagination?limit=50&offset=0&is_upload=true&sort_column=date&sort_direction=asc" \
  -H "Authorization: Bearer $TOKEN"

Response (abridged)

{
  "total_count": 96,
  "data": [
    {
      "datetime_timestamp": "2023-04-30T23:00:00+00:00",
      "meter_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "meter_name": null,
      "l_buy": 0.2,
      "l_sell": 0.111,
      "is_upload": true
    }
  ],
  "distinct_dates": [{ "date": "2023-05-01" }],
  "distinct_times": [{ "time": "00:00:00" }]
}

Tip

meter_name is not filled by this endpoint; use by_community?include_meter_name=true when you need the identifier.

OMIE

OMIE day-ahead electricity prices used by indexed contracts and by VANILLA_INTERMEDIATE_MARKET_RATE (see Market). Prices are global, not per community, and are in EUR/MWh (the platform divides by 1000 where EUR/kWh is needed). The platform fetches missing OMIE data itself when it needs it; these endpoints only read it.

Get OMIE Prices

GET /core/api/omie

Returns OMIE prices, optionally for a date range and aggregated to a coarser granularity.

Auth / permissions: no authentication dependency is declared on this router in the code (public).

Parameter In Type Required Description
start_date query date No First day, YYYY-MM-DD. Both dates are needed to filter
end_date query date No Last day, inclusive
granularity query string No MIN15 (default), HOUR, DAY, MONTH or YEAR. Only applied when both dates are given

Example

curl "https://<host>/core/api/omie?start_date=2025-01-01&end_date=2025-01-01&granularity=HOUR"

Response (abridged)

[
  { "datetime_timestamp": "2025-01-01T00:00:00+01:00", "price": 98.4, "adjustment": 0.0, "id": null }
]

Tip

  • Without start_date and end_date it returns every stored row, newest first, ignoring granularity; always send both dates for normal use.
  • With a range, the price of each bucket is the mean of the underlying prices, buckets are computed in Europe/Lisbon, and adjustment is always 0.0.
  • Returns [] when there is no data in the range (including future dates beyond what OMIE has published).

Get OMIE Prices (Paginated)

GET /core/api/omie/pagination

Paginated OMIE prices, for the OMIE table.

Auth / permissions: no authentication dependency is declared on this router in the code (public).

Parameter In Type Required Description
limit query integer Yes Page size
offset query integer Yes Rows to skip
date query string No Filter by date, YYYY-MM-DD
time query string No Filter by time, HH:MM:SS
sortColumn query string No date, time, price, adjustment or datetime_timestamp
sortDirection query string No asc or desc. Both sort parameters are needed to sort

Example

curl "https://<host>/core/api/omie/pagination?limit=24&offset=0&date=2025-01-01&sortColumn=time&sortDirection=asc"

Response (abridged)

{
  "total_count": 96,
  "data": [
    { "datetime_timestamp": "2025-01-01T00:00:00+01:00", "price": 98.4, "adjustment": 0.0, "id": null }
  ],
  "distinct_dates": [{ "date": "2025-01-01" }]
}

Tip

Uses camelCase sortColumn / sortDirection. Without both, rows are ordered newest first. Unlike other paginated endpoints, the response has no distinct_times.