Skip to content

REST-API

Via https://api.pqopen.com, there is a REST-API available for gathering actual and historic measurement data from the PQopen Monitoring System. You can request snippets of Cycle-by-Cycle data from the last 30 days as well as aggregated data (1s, 600s) from selected Parameters.

Endpoints for Metadata

The Endpoints and detailed description of the API and experimental interface is available here: https://api.pqopen.com/docs

/v1/metadata/locations/{family} [GET]

Request available locatation tags for the specific data family (either cbc or aggregated)

Returns json-Object of available locations to be queried via /v1/data requests

/v1/metadata/fields/{family} [GET]

Request available fields (measurement channels) for the specific data family (either cbc or aggregated)

Returns json-Object of available fields to be queried via /v1/data requests

/v1/metadata/aggintervals [GET]

Requests available aggregation intervals from the aggregated-data collection.

Returns json-Object of available aggregation intervals to be queried via /v1/data requests

Endpoints for Measurement Data

/v1/data/aggregated [POST]

Query aggregated data for specified interval. The request body must be of type:

{  
    "range_start": "2025-12-21T13:00:21Z",  
    "range_stop": "2025-12-21T14:00:21Z",  
    "location": "AT/Graz",  
    "interval_sec": 1,  
    "fields": ["U1_rms", "U1_THD"] 
}

If the fields list empty, all available fields for the selected combination are returned.

Returns a Parquet-File Stream in case of success.

/v1/data/cbc [POST]

Query cycle-by-cycle data for specified interval. The request body must be of type:

{  
    "range_start": "2025-12-21T13:00:21Z",  
    "range_stop": "2025-12-21T14:00:21Z",  
    "location": "AT/Graz", 
    "fields": ["Freq", "U1_1p_rms"] 
}

If the fields list empty, all available fields for the selected combination are returned.

Returns a Parquet-File Stream in case of success.

Limitations

  • Maximum number of measurement points: 1 Mio. (must be reduced by time span or number of fields)
  • Maximum number of requests per hour: 50

Python Examples

If you are using python, I prepared short examples of programmatically requesting some data.

Requesting available Locations

import requests

base_url = "https://api.pqopen.com"
API_KEY = "YOUR-API-KEY"
headers={"X-API-Key": API_KEY}

res = requests.get(base_url+"/v1/meta/locations/aggregated", headers=headers)
if res.status_code == 200:
    print("locations aggregated data:", res.json()["locations"])

Prints the following:

['AT/Graz', 'CH/Solothurn', 'DE/Berlin', 'DE/Eisenberg', 'DE/Essen', 'DE/Stade', 'ES/Pamplona']

Requesting Fields

import requests

base_url = "https://api.pqopen.com"
API_KEY = "YOUR-API-KEY"
headers={"X-API-Key": API_KEY}

res = requests.get(base_url+"/v1/meta/fields/aggregated", headers=headers)
if res.status_code == 200:
    print("fields aggregated data:", res.json()["fields"])

Prints the following:

['Freq', 'U1_H_rms_00', 'U1_H_rms_01'...'U3_IH_rms_50', 'U3_THD', 'U3_pst', 'U3_rms']

Requesting Aggregated Data

Request last hour's data from selected fields

import requests
import polars as pl
import datetime

base_url = "https://api.pqopen.com"
API_KEY = "YOUR-API-KEY"
headers={"X-API-Key": API_KEY}

range_stop_dt = datetime.datetime.now(datetime.UTC)
range_start_dt = range_stop_dt - datetime.timedelta(hours=1)

request_body = {
  "range_start": range_start_dt.isoformat(),
  "range_stop": range_stop_dt.isoformat(),
  "location": "AT/Graz",
  "interval_sec": 1,
  "fields": ["Freq", "U1_rms", "U2_rms", "U3_rms"]
}

res = requests.post(base_url+"/v1/data/aggregated", json=request_body, headers=headers)
if res.status_code == 200:
    agg_data = pl.read_parquet(res.content)
    print(agg_data)
else:
    print(res.reason, res.text)

Prints the following:

shape: (3_600, 3)
┌─────────────────────────┬─────────┬──────────┐
 _time                    Freq     U1_rms    ---                      ---      ---       datetime[μs, UTC]        f64      f64      ╞═════════════════════════╪═════════╪══════════╡
 2025-12-22 07:33:21 UTC  49.9873  230.019   2025-12-22 07:33:22 UTC  49.9872  230.1252  2025-12-22 07:33:23 UTC  49.9871  230.1179  2025-12-22 07:33:24 UTC  49.9865  230.1027 

Requesting Cycle-by-Cycle Data

Request last 10 minutes data from frequency field

import requests
import polars as pl
import datetime

base_url = "https://api.pqopen.com"
API_KEY = "YOUR-API-KEY"
headers={"X-API-Key": API_KEY}

range_stop_dt = datetime.datetime.now(datetime.UTC)
range_start_dt = range_stop_dt - datetime.timedelta(minutes=10)

request_body = {
  "range_start": range_start_dt.isoformat(),
  "range_stop": range_stop_dt.isoformat(),
  "location": "AT/Graz",
  "fields": ["Freq"]
}

res = requests.post(base_url+"/v1/data/cbc", json=request_body, headers=headers)
if res.status_code == 200:
    cbc_data = pl.read_parquet(res.content)
    print(cbc_data)
else:
    print(res.reason, res.text)

Prints the following:

shape: (179_987, 2)
┌────────────────────────────────┬─────────┐
 _time                           Freq     ---                             ---      datetime[μs, UTC]               f64     ╞════════════════════════════════╪═════════╡
 2025-12-22 07:33:21.009101 UTC  49.988   2025-12-22 07:33:21.029117 UTC  49.9881  2025-12-22 07:33:21.049116 UTC  49.9869  2025-12-22 07:33:21.069115 UTC  49.9888