Premium Measurements¶
This example shows Premium PV site measurement uploads, downloads, and sub-unit records. Premium Wind uses the same workflow through wind_power_site_measurements; see the Wind measurement guide for its fields.
In this example, we will use the SDK for the following.
- Upload measurements in batches of up to 1,000 records
- Download all measurements for a period with
skipandtake
The upload example writes data to the site. Replace your-premium-pv-site with your Premium PV Power resource_id.
import pandas as pd
from solcast import pv_power_site_measurements
Upload Measurements in Batches¶
Each create request accepts up to 1,000 measurements. The site example below batches a pandas DataFrame; the sub-unit example uses a list of records.
resource_id = "your-premium-pv-site"
measurements = pd.DataFrame(
[
{
"period_end": "2026-09-28T00:30:00Z",
"period": "PT30M",
"power": 1.25,
},
{
"period_end": "2026-09-28T01:00:00Z",
"period": "PT30M",
"power": 1.4,
},
]
)
# NOTE: this writes measurements to the site!
for start_index in range(0, len(measurements), 1000):
batch = measurements.iloc[start_index : start_index + 1000]
res = pv_power_site_measurements.create_pv_site_measurements(
resource_id=resource_id,
measurements=batch,
format="json",
)
if not res.success:
raise RuntimeError(res.exception)
print(res.to_dict())
{'resource_id': 'cbf6-a872-1cb2-566f', 'accepted': 2}
Sub-unit measurements use the same batch limit. Each record must include the sub_unit label for its inverter.
sub_unit_measurements = [
{
"sub_unit": "inverter-1",
"period_end": "2026-09-28T00:30:00Z",
"period": "PT30M",
"power": 0.65,
},
{
"sub_unit": "inverter-1",
"period_end": "2026-09-28T01:00:00Z",
"period": "PT30M",
"power": 0.72,
},
]
# NOTE: this writes sub-unit measurements to the site!
for i in range(0, len(sub_unit_measurements), 1000):
res = pv_power_site_measurements.create_pv_sub_unit_measurements(
resource_id=resource_id,
measurements=sub_unit_measurements[i : i + 1000],
format="json",
)
if not res.success:
raise RuntimeError(res.exception)
print(res.to_dict())
{'resource_id': 'cbf6-a872-1cb2-566f', 'accepted': 2}
Display Sub-Unit Measurements¶
Pass a sub_unit label to fetch the first page of up to 100 measurements for one inverter. Change skip to request later pages. Use the next section to download all matching site measurements.
sub_unit = "inverter-1"
sub_unit_take = 100
res = pv_power_site_measurements.get_pv_sub_unit_measurements(
resource_id=resource_id,
sub_unit=sub_unit,
start="2026-09-28T00:00:00Z",
end="2026-09-29T00:00:00Z",
skip=0,
take=sub_unit_take,
)
if not res.success:
raise RuntimeError(res.exception)
sub_unit_page = res.to_dict()
sub_unit_records = sub_unit_page["results"]
if sub_unit_records:
sub_unit_df = pd.DataFrame(sub_unit_records).set_index("period_end")
sub_unit_df.index = pd.to_datetime(sub_unit_df.index)
print(
f"Showing {len(sub_unit_records)} of {sub_unit_page['total']} matching records."
)
display(sub_unit_df)
else:
print(f"No measurements found for sub-unit: {sub_unit}")
Showing 2 of 2 matching records.
| sub_unit | power | period | |
|---|---|---|---|
| period_end | |||
| 2026-09-28 00:30:00+00:00 | inverter-1 | 0.65 | PT30M |
| 2026-09-28 01:00:00+00:00 | inverter-1 | 0.72 | PT30M |
Download All Pages of Site Measurements¶
Use to_pandas() to convert each response page to a DataFrame. Use to_dict() to read the offset and total metadata needed to request all matching site measurements.
site_pages = []
site_skip = 0
site_take = 100
while True:
res = pv_power_site_measurements.get_pv_site_measurements(
resource_id=resource_id,
start="2026-09-28T00:00:00Z",
end="2026-09-29T00:00:00Z",
skip=site_skip,
take=site_take,
)
if not res.success:
raise RuntimeError(res.exception)
page = res.to_dict()
page_frame = res.to_pandas()
if not page_frame.empty:
site_pages.append(page_frame)
site_skip = page["offset"] + len(page_frame)
if page_frame.empty or site_skip >= page["total"]:
break
if site_pages:
site_df = pd.concat(site_pages)
display(site_df)
else:
print("No site measurements found for this period.")
| power | |
|---|---|
| period_end | |
| 2026-09-28 00:30:00+00:00 | 1.25 |
| 2026-09-28 01:00:00+00:00 | 1.40 |
Delete Measurements¶
Deletion removes records in the selected time range. Review the resource ID and bounds before setting confirm_delete to True. For sub-unit data, use delete_pv_sub_unit_measurements and pass the sub_unit label.
confirm_delete = False
if confirm_delete:
res = pv_power_site_measurements.delete_pv_site_measurements(
resource_id=resource_id,
start="2026-09-28T00:00:00Z",
end="2026-09-28T02:00:00Z",
)
if not res.success:
raise RuntimeError(res.exception)
print(res.to_dict())
else:
print("Deletion is disabled.")
{'resource_id': 'cbf6-a872-1cb2-566f', 'start': '2026-09-28T00:00:00.0000000Z', 'end': '2026-09-28T02:00:00.0000000Z', 'deleted': 2}
Delete Sub-Unit Measurements¶
Set sub_unit and the required time range to delete only that inverter's records. Keep the confirmation flag false until you are ready to delete.
confirm_sub_unit_delete = False
if confirm_sub_unit_delete:
res = pv_power_site_measurements.delete_pv_sub_unit_measurements(
resource_id=resource_id,
sub_unit=sub_unit,
start="2026-09-28T00:00:00Z",
end="2026-09-28T02:00:00Z",
)
if not res.success:
raise RuntimeError(res.exception)
print(res.to_dict())
else:
print("Sub-unit deletion is disabled.")
{'resource_id': 'cbf6-a872-1cb2-566f', 'start': '2026-09-28T00:00:00.0000000Z', 'end': '2026-09-28T02:00:00.0000000Z', 'deleted': 2}