Skip to content

API reference

Module functions

The functions below use a shared default client; pass client=pse.Client(...) to change settings.

pse_opendata.get(report, start, end=None, *, client=None, **kwargs)

Data of a report of the reports API; see :meth:Client.get.

pse_opendata.legacy(code, start, end=None, *, client=None, **kwargs)

Hourly data of the legacy CSV export; see :meth:Client.legacy.

pse_opendata.history(series, start, end=None, *, client=None, **kwargs)

Hourly series stitched from the legacy export and the API; see :meth:Client.history.

pse_opendata.rce(start, end=None, *, client=None, **kwargs)

RCE price (rce-pln); see :meth:Client.rce.

pse_opendata.sdac(start, end=None, *, client=None, **kwargs)

SDAC price for Poland (csdac-pln); see :meth:Client.sdac.

pse_opendata.fundamentals(start, end=None, *, client=None, **kwargs)

Demand, generation and exchange (his-wlk-cal); see :meth:Client.fundamentals.

pse_opendata.imbalance_prices(start, end=None, *, client=None, **kwargs)

Imbalance and balancing energy prices (crb-rozl); see :meth:Client.imbalance_prices.

pse_opendata.balancing_capacity(start, end=None, *, client=None, **kwargs)

Balancing capacity prices and volumes; see :meth:Client.balancing_capacity.

pse_opendata.reports(legacy=False)

The catalogue as a frame: one row per report of the reports API (or of the legacy export).

pse_opendata.to_hourly(frame, key=())

Hourly means of a sub-hourly frame (e.g. 15-minute).

An hour gets a value only when all its sub-intervals are present and not empty (four quarter-hours for a 15-minute frame); otherwise the value is NaN. Text columns other than key are dropped. Hours are clock hours in UTC, which are also clock hours in Polish time (the offset is a whole number of hours).

pse_opendata.splice_factor(legacy, api, days=14)

Level ratio legacy / API over days before and after the seam, matched by local weekday and hour.

Both series are hourly values indexed by interval start (tz-aware). Only (weekday, hour) slots present on both sides are used. Multiply the API part by the result to bring it to the legacy level.

Client

pse_opendata.Client(*, cache_dir=_DEFAULT, refresh=False, offline=False, allow_stale=False, revision_days=None, timeout=60.0, retries=4, min_interval=0.2, legacy_min_interval=15.0, page_size=20000, session=None, now=None, sleep=None)

Client for the public data of PSE.

Parameters:

Name Type Description Default
cache_dir str | Path | None

directory of the monthly Parquet cache; default $PSE_OPENDATA_CACHE or the user cache directory (see :mod:pse_opendata.cache); None disables the cache.

_DEFAULT
refresh bool

download again even months that the cache holds as final.

False
offline bool

use the cache only; a missing month raises :class:CacheMissError.

False
allow_stale bool

when a download fails, use the cached copy of the month (with a warning) instead of raising.

False
revision_days int | None

override the per-report number of days after the end of a month during which the cache re-downloads it (see :attr:pse_opendata.catalog.Report.revision_days).

None
timeout float

seconds per request (at least 180 for the legacy export).

60.0
retries int

retries per request, with exponential backoff, on network errors, HTTP 429 and 5xx.

4
min_interval float

pause in seconds between requests to the reports API.

0.2
legacy_min_interval float

pause in seconds between requests to the legacy export (it rate-limits hard).

15.0
page_size int

rows per page of the reports API ($first; the server allows at most 100000).

20000
session Session | None

a requests.Session to use (e.g. with a proxy); also how tests replay recorded responses.

None
now Callable[[], datetime] | None

function returning the current time (UTC), for tests.

None
sleep Callable[[float], None] | None

replacement for time.sleep, for tests.

None

Attributes:

Name Type Description
loads list[MonthLoad]

one :class:MonthLoad per month loaded so far, in order (cached, downloaded or stale), e.g. to show how fresh the data are; a month that could not be loaded raises instead. Clear it with client.loads.clear().

records(endpoint, *, filter=None, select=None, orderby=None, page_size=None, single_page=False)

Raw records of any endpoint of the reports API, following nextLink pages.

filter and orderby are OData expressions, e.g. filter="business_date eq '2025-01-01'". Works for endpoints that are not in the catalogue too; no parsing and no cache. single_page=True raises instead of following nextLink (for reports whose paging can skip rows).

get(report, start, end=None, *, fields=None, dropna=False)

Data of one report for business dates start..end (inclusive, Polish local days).

Returns a frame with start_utc, end_utc, start_local, end_local, business_date, the row keys of the report (if any) and the value fields (all of them, or fields), one row per interval. end defaults to start. Dates before the first date of the report are skipped. dropna=True drops rows in which all requested values are empty (e.g. future intervals of today).

legacy(code, start, end=None, *, fields=None)

Hourly data of a legacy export report (e.g. "PL_WYK_KSE") for business dates start..end.

Same columns as :meth:get. Dates outside the range of the export are skipped. The server is slow (about a minute per month) and requests are spaced by legacy_min_interval seconds; months are cached for good.

history(series, start, end=None, *, rescale=False)

Hourly series across the switch from the legacy export to the reports API (2024-06-14).

series is one of "rce", "demand", "wind", "pv" (see :data:pse_opendata.stitch.SERIES). Columns: the five time columns, the series (named like series) and source ("legacy" or "api"). With rescale=True the API part is multiplied by :func:splice_factor, computed on the two weeks either side of the seam; the factor is stored in frame.attrs["splice_factor"].

rce(start, end=None, **kwargs)

RCE price, PLN/MWh (report rce-pln).

sdac(start, end=None, **kwargs)

SDAC day-ahead price for Poland, PLN/MWh (report csdac-pln).

fundamentals(start, end=None, **kwargs)

Demand, generation (wind wi, PV pv, ...) and exchange balance, MW (report his-wlk-cal).

imbalance_prices(start, end=None, **kwargs)

Imbalance price CEN and balancing energy prices, PLN/MWh (report crb-rozl).

balancing_capacity(start, end=None, procurement='primary')

Balancing capacity prices and volumes per reserve product in one frame.

procurement="primary" (day-ahead, hourly: cmbp-tp and mbp-tp) or "supplementary" (15-minute: cmbu-tu and mbu-tu). Columns <product>_price (PLN/MW/h) and <product>_mw (MW), e.g. afrr_g_price and afrr_g_mw.

pse_opendata.MonthLoad(source, name, month, status, fetched_utc, rows) dataclass

How one calendar month of one report was loaded; see :attr:Client.loads.

status is "cached" (a final or offline copy from the cache), "downloaded" or "stale" (the download failed and allow_stale used the cached copy). fetched_utc is when the data were downloaded (for cached and stale months, when the cached copy was). rows counts the rows of the requested days.

Catalogue

pse_opendata.catalog.Report(name, title, title_pl, group, resolution, fields, first_date, description, key=(), unique_key=True, notes=(), revision_days=3, time_field='dtime_utc') dataclass

A report of the reports API (https://api.raporty.pse.pl/api/).

resolution is the length of one interval; the API stamps each row with the interval END (time_field). key lists the fields that, with the interval, identify a row when a report has several rows per interval (the entity key in the OpenAPI description, e.g. one row per cross-section); rows are sorted on the interval and these fields and de-duplicated on them. unique_key=False marks reports whose rows can repeat in every field (identical anonymous offers). The keyset paging of the API skips rows at page boundaries for them, so they are requested one business date at a time in a single page, and nothing is de-duplicated. revision_days: PSE republishes some reports after corrections; this is how many days after the end of a month the data may still change (latest republication observed for Sep 2025 - Oct 2026, rounded up). The cache treats a month as final only when it was downloaded at least this long after the month ended.

value_fields property

Fields that are not row keys.

pse_opendata.catalog.Field(name, description, unit=None, unit_verified=True, dtype='float') dataclass

One column of a report.

pse_opendata.catalog.LegacyReport(code, title, title_pl, page, columns, first_date, description, notes=(), last_date=LEGACY_END, resolution='1h') dataclass

A report of the legacy export https://www.pse.pl/getcsv/-/export/csv//data_od//data_do/.

Hourly rows, local time (hour 1 = 00:00-01:00, extra autumn hour 2A), ;-separated, decimal comma, cp1250.

pse_opendata.catalog.get_report(name)

Catalogue entry of a report, e.g. get_report("rce-pln").

pse_opendata.catalog.get_legacy_report(code)

Catalogue entry of a legacy export report, e.g. get_legacy_report("PL_WYK_KSE").

Exceptions

pse_opendata.exceptions

Exceptions raised by pse-opendata. All of them derive from :class:PseError.

PseError

Bases: Exception

Base class of every error raised by this package.

PseApiError(message, *, status=None, code=None, url=None)

Bases: PseError

An HTTP request to a PSE server failed (after retries, where retrying makes sense).

Attributes:

Name Type Description
status

HTTP status code, or None when no response was received (network error, timeout).

code

error code from the JSON error body of the reports API (e.g. "BadRequest"), if any.

url

the requested URL.

UnknownReportError

Bases: PseError, LookupError

The report name is not in the catalogue (see :func:pse_opendata.reports).

UnknownFieldError

Bases: PseError, ValueError

A requested field does not belong to the report.

CacheMissError

Bases: PseError

Offline mode was requested but a month is not in the cache.

LegacyFormatError

Bases: PseError, ValueError

The legacy CSV export returned something that is not the expected CSV (e.g. an HTML error page).