1KOMMA5° Heartbeat API — curl Reference¶
Reverse-engineered reference for every endpoint used by the onekommafive Python client.
Requests unless noted send Authorization: Bearer $BEARER_TOKEN. All personal identifiers below are anonymised (Erika Mustermann / Musterstraße 1 / Hamburg / DE). Example UUIDs use the placeholder <uuid>.
Table of contents¶
- Setup
- Environment variables
- Bearer token
- Base URLs
- Endpoint entry template
- User and customer
- Authenticated user profile
- Customer record (v3)
- Price guarantee
- Subscriptions
- Subscription eligibility
- System and site
- List systems
- Single system (v4)
- System details (v1, extended)
- Site details (v2, superset)
- Site status and assets
- Active feature flags
- Device gateways (standalone, v2)
- Live data
- Live overview
- Energy
- Energy today
- Energy historical
- Heartbeat savings
- Prices
- Market prices
- Price customizations
- Comparison price
- EV chargers and wallbox
- List EV chargers
- Available charging modes
- Update EV settings
- List wallboxes (hardware)
- Energy management (EMS)
- Get EMS settings
- Set EMS mode
- Weather
- Weather forecast
- Heartbeat AI
- Optimizations
- Self-sufficiency events
- AI summary
- Analytics
- Impact overview (CO2)
- Energy trader (lifetime)
- Monthly trading savings
- Heartbeat prices
- Smart meter
- Smart meter registration
- Notifications
- Latest notifications
- Notification settings
- API meta
- Supported versions
- Unit reference
- Known API quirks
Setup¶
Environment variables¶
export ONEKOMMAFIVE_USERNAME="user@example.com"
export ONEKOMMAFIVE_PASSWORD="s3cr3t"
# Optional — pin to a specific system UUID (used by the CLI when
# multiple systems are visible to the account).
export ONEKOMMAFIVE_SYSTEM="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
Bearer token¶
The API accepts a JWT valid for 24 hours. The easiest way to obtain one is via the Python client:
export BEARER_TOKEN=$(python -c '
from onekommafive import Client
import os
c = Client(os.environ["ONEKOMMAFIVE_USERNAME"], os.environ["ONEKOMMAFIVE_PASSWORD"])
print(c.get_token())
')
The token can also be printed once and exported manually for quick tests:
python -c '
from onekommafive import Client
import os
c = Client(os.environ["ONEKOMMAFIVE_USERNAME"], os.environ["ONEKOMMAFIVE_PASSWORD"])
print(c.get_token())
'
Base URLs¶
| Subdomain | Purpose |
|---|---|
heartbeat.1komma5grad.com/api/ |
System, energy, EMS, EV, weather, AI, analytics |
customer-identity.1komma5grad.com/api/ |
User profile, customer, price guarantee, active features |
siteId and systemId are the same UUID in this API. The demo system always has ID 00000000-0000-0000-0000-000000000000.
Endpoint entry template¶
Each endpoint below follows the same structure:
METHOD /pathheader- One-line purpose
- Query parameters table (when any)
- Request body (POST / PATCH only)
- Example — a runnable curl invocation
- Response — anonymised JSON payload
- Notes — quirks, gotchas, related endpoints
User and customer¶
Authenticated user profile¶
GET /api/v1/users/me — profile of the currently authenticated user, plus a summary of every site they can access.
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
https://customer-identity.1komma5grad.com/api/v1/users/me | jq .
Response
{
"id": "<uuid>",
"createdAt": "ISO8601",
"firstName": "Erika",
"lastName": "Mustermann",
"externalId": "auth0|abcdef1234567890",
"email": "user@example.com",
"phone": null,
"status": "ACTIVE",
"connectedSystems": [
{
"systemId": "<uuid>",
"systemName": "Mustermann",
"addressName": null,
"addressLine1": "Musterstraße 1",
"addressLine2": null,
"addressZipCode": "20095",
"addressCity": "Hamburg",
"addressCountry": "DE",
"technicalContactId": "<uuid>"
}
]
}
Notes
- Lives on the
customer-identityhost, notheartbeat. connectedSystemslists every site the caller is authorised for — useful for multi-system accounts (families, installer logins).
Customer record (v3)¶
GET /api/v3/customers/$CUSTOMER_ID — full customer profile; superset of the customer block embedded in /systems/{id}/details.
$CUSTOMER_ID comes from the customerId field of /api/v1/systems/{id}/details.
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://customer-identity.1komma5grad.com/api/v3/customers/$CUSTOMER_ID" | jq .
Response
{
"id": "<uuid>",
"firstName": "Erika",
"lastName": "Mustermann",
"contactEmail": "user@example.com",
"contactPhone": "+490000000000",
"companyName": null,
"companyTaxId": null,
"addressName": null,
"addressLine1": "Musterstraße 1",
"addressLine2": null,
"addressZipCode": "20095",
"addressCity": "Hamburg",
"addressCountry": "Deutschland",
"crmContactId": "<crm-id>",
"customerType": "UNKNOWN",
"title": null,
"crmBranchLocation": "1KOMMA5° <Region>",
"createdAt": "ISO8601",
"updatedAt": "ISO8601"
}
Notes
- Email field is
contactEmail, notemail(which is used by the embeddedSystemCustomer). addressCountryis a plain-name string (e.g."Deutschland"), not an ISO code — differs from/systems/{id}which returns"DE".customerTypeobserved only as"UNKNOWN"so far; likely also"PRIVATE"/"BUSINESS".crmBranchLocationis the assigned 1KOMMA5° branch office (e.g."1KOMMA5° Moers").
Price guarantee¶
GET /api/v1/customers/$CUSTOMER_ID/price-guarantee?systemId=$ONEKOMMAFIVE_SYSTEM — contractual electricity-price guarantee.
Query parameters
| Name | Required | Description |
|---|---|---|
systemId |
yes | System UUID. Path takes customerId, but the required query parameter is confusingly named systemId (not customerId, not siteId). Omitting it returns HTTP 400. |
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://customer-identity.1komma5grad.com/api/v1/customers/$CUSTOMER_ID/price-guarantee?systemId=$ONEKOMMAFIVE_SYSTEM" | jq .
Response
{
"priceGuaranteeUnit": "ct/kWh",
"priceGuaranteeValue": 12,
"priceGuaranteeVersion": "DE_PRICE_GUARANTEE_V2"
}
Notes
- Lives on the
customer-identityhost. - Observed versions:
DE_PRICE_GUARANTEE_V2(Germany). The same version identifier also appears in thepriceGuaranteeVersionfield of individual subscription records.
Subscriptions¶
GET /api/v1/customers/$CUSTOMER_ID/subscriptions — all active service contracts for a customer. Typical composition: an electricity contract (DYNAMIC_PULSE), a smart-meter contract (SMART_METER), a platform-access contract (HEARTBEAT), and a trading contract (ENERGY_TRADER).
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://customer-identity.1komma5grad.com/api/v1/customers/$CUSTOMER_ID/subscriptions" | jq .
Response (excerpt — one DYNAMIC_PULSE contract; PII fields anonymised)
{
"data": [
{
"id": "<uuid>",
"type": "DYNAMIC_PULSE",
"status": "ACTIVE",
"customerId": "<uuid>",
"siteId": "<uuid>",
"countryCode": "DE",
"createdAt": "ISO8601",
"updatedAt": "ISO8601",
"signedDate": "ISO8601",
"startDate": "ISO8601",
"endDate": null,
"price": 0,
"currency": "EURO",
"billingFrequency": "MONTHLY",
"renewal": "AUTOMATIC",
"noticePeriodInterval": "MONTHS",
"noticePeriodNumber": 1,
"paymentMethod": "DIRECT_DEBIT",
"paymentIban": "<IBAN>",
"electricityContractNumber": "<number>",
"marketLocationId": "<11-digit-id>",
"priceGuaranteeUnit": "ct/kWh",
"priceGuaranteeValue": 12,
"priceGuaranteeVersion": "DE_PRICE_GUARANTEE_V2",
"heartbeatPriceGuarantee": "NONE",
"deliveryAddressStreet": "Musterstraße",
"deliveryAddressHouseNumber": "1",
"deliveryAddressZipCode": "20095",
"deliveryAddressCity": "Hamburg",
"termsAndConditionsLink": "https://1k5.link/tos-dynamic-pulse",
"statusHistory": [ /* contract state-transition history */ ],
"metadata": {
"version": "1",
"payload": {
/* Full Zoho booking record: IBAN, former supplier, hardware
selection, feature flags, address, salutation, delivery
preferences, Lumenaza IDs — all PII */
}
},
"crmDealId": null,
"lumenazaContractId": "<id>",
"lumenazaConsumerId": "<id>",
"zohoReferenceId": "<id>"
}
],
"pageIndex": 0,
"pageSize": 15,
"totalPages": 1,
"totalItems": 4
}
Other contract types share the universal fields (id, type, status, price, dates, notice period, renewal, termsAndConditionsLink) but have type-specific details:
SMART_METER— hasnullprice, 24-month notice, plusmeterId,supplier,deviceManufacturer,deviceMeasuringType,marketLocationIdConsumption/FeedIn,crmBranchLocation(installer name),meterInstallationDate.HEARTBEAT— minimal shape,price: 0,paymentMethod: "NO_PAYMENT".ENERGY_TRADER— typical price 14.99 EUR / month.
Notes
- Lives on the
customer-identityhost. $CUSTOMER_IDcomes from thecustomerIdfield of/api/v1/systems/{id}/details.- PII handling: the response mixes universal contract metadata with heavy PII —
paymentIban, complete delivery/billing addresses, CRM identifiers (crmDealId,zohoReferenceId,lumenazaContractId,lumenazaConsumerId,crmInstallationId), astatusHistoryblock, and an embeddedmetadata.payloadwith the full Zoho booking record (IBAN again, former supplier, feature flags, hardware selection). Callers building dashboards should stick to the universal fields; PII should never be logged or shared. - SMART_METER duplication: the same meter details are also served by
/sites/{id}/smart-meter. Prefer that endpoint if you only need meter data. - Invoices endpoint (
GET /api/v1/customers/{cid}/subscriptions/{sub_id}/invoices) exists but returns an empty list on accounts without generated invoices. Not documented here until a populated response is available for reference.
Subscription eligibility¶
GET /api/v1/sites/$ONEKOMMAFIVE_SYSTEM/subscription-eligibility — which 1KOMMA5°Care add-on products the backend currently offers to the account. Distinct from Subscriptions: that one lists contracts the customer already holds, this one lists upsell candidates with a per-product eligibility flag.
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v1/sites/$ONEKOMMAFIVE_SYSTEM/subscription-eligibility" | jq .
Response
{
"subscriptions": [
{
"type": "PV_SERVICE",
"eligible": false,
"reason": "The eligibility1K5Care tag for 1KOMMA5° PV & Battery Service subscription is not listed on any sales order. ..."
},
{
"type": "MAINTENANCE_HEAT_PUMP",
"eligible": false,
"reason": "The eligibility1K5Care tag for 1KOMMA5° Heatpump Maintenance subscription is not listed on any sales order. ..."
}
]
}
Notes
- Lives on the
heartbeathost (unlike the siblingsubscriptionsendpoint, which is oncustomer-identity). - Observed
typevalues (not exhaustive):PV_SERVICE,MAINTENANCE_HEAT_PUMP. New types will appear as 1KOMMA5° ships more add-ons. - Eligibility is gated by a per-sales-order
eligibility1K5Caretag maintained in 1KOMMA5°'s CRM; thereasonstring carries the backend's own explanation wheneligibleisfalse. - The response has no pagination wrapper — just the
subscriptionslist.
System and site¶
List systems¶
GET /api/v2/systems — all systems (sites) the authenticated caller has access to, paginated.
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
https://heartbeat.1komma5grad.com/api/v2/systems | jq .
Response
{
"pageIndex": 0,
"pageSize": 15,
"totalItems": 2,
"totalPages": 1,
"data": [
{
"id": "<uuid>",
"systemName": "Mustermann",
"status": "ACTIVE",
"addressLine1": "Musterstraße 1",
"addressCity": "Hamburg",
"addressCountry": "DE",
"addressLongitude": 0.0,
"addressLatitude": 0.0,
"externalPartnerId": null,
"dynamicPulseCompatible": true,
"deviceGateways": [
{
"id": "<uuid>",
"gridxStartCode": "<hex-token>",
"serialNumber": "I###-###-###-###-###-P-X",
"installationDate": "YYYY-MM-DD"
}
]
}
]
}
Notes
- The list variant carries
deviceGatewaysinline. The single-system variant (v4) does not — use/detailsfor those.
Single system (v4)¶
GET /api/v4/systems/$ONEKOMMAFIVE_SYSTEM — static metadata for one system.
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v4/systems/$ONEKOMMAFIVE_SYSTEM" | jq .
Notes
- Does not include
deviceGateways,energyTraderActive, orelectricityContractActive. Those were dropped from v2 to v4 — for the full picture use/details(v1).
System details (v1, extended)¶
GET /api/v1/systems/$ONEKOMMAFIVE_SYSTEM/details — richer than the v4 endpoint. Adds empType, technicalContact*, embedded customer block, smart-meter status, earliestMeasurement, and installed deviceGateways. Also brings back the v2-only energyTraderActive / electricityContractActive.
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v1/systems/$ONEKOMMAFIVE_SYSTEM/details" | jq .
Response
{
"id": "<uuid>",
"empType": "GRIDX",
"systemName": "Mustermann",
"status": "ACTIVE",
"addressName": null,
"addressLine1": "Musterstraße 1",
"addressLine2": null,
"addressZipCode": "20095",
"addressCity": "Hamburg",
"addressCountry": "DE",
"addressLongitude": 0.0,
"addressLatitude": 0.0,
"technicalContactId": "<uuid>",
"technicalContactName": "1KOMMA5° <Region>",
"customerId": "<uuid>",
"customer": {
"id": "<uuid>",
"firstName": "Erika",
"lastName": "Mustermann",
"email": "user@example.com"
},
"externalPartnerId": null,
"dynamicPulseCompatible": true,
"energyTraderActive": true,
"electricityContractActive": true,
"hasThirdPartySmartMeter": null,
"thirdPartySmartMeterMeterId": null,
"thirdPartySmartMeterDeletedAt": null,
"thirdPartySmartMeterMarketLocationId": null,
"earliestMeasurement": "YYYY-MM-DD",
"createdAt": "ISO8601",
"updatedAt": "ISO8601",
"deviceGateways": [
{
"id": "<uuid>",
"gridxStartCode": "<hex-token>",
"serialNumber": "I000-000-000-000-000-X-X",
"installationDate": "YYYY-MM-DD"
}
]
}
Notes
empTypedescribes the energy-management provider; so far only"GRIDX"has been observed.externalPartnerId— UUID of an external partner (e.g. reseller, EVU) the system is assigned to;nullfor consumer-direct accounts. Added by the backend in 2026-10 and now returned on all system/site details endpoints.gridxStartCode/serialNumberondeviceGatewaysare hardware pairing tokens — sensitive, do not log or share.
Site details (v2, superset)¶
GET /api/v3/sites/$ONEKOMMAFIVE_SYSTEM/details — superset of the two /systems/{id} endpoints above. Adds bidding zone, EMP connection block, impactedByEnwg, grid-connection capacity and — most usefully — the current EMS runtime state (emsMode, emsState, emsStateReasons), which no other endpoint surfaces.
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v3/sites/$ONEKOMMAFIVE_SYSTEM/details" | jq .
Response
{
"id": "<uuid>",
"siteName": "Mustermann",
"status": "ACTIVE",
"empType": "GRIDX",
"biddingZone": "DE_LU",
"biddingZoneEic": "10Y1001A1001A82H",
"emsMode": "TOU",
"emsState": "OPERATIONAL",
"emsStateReasons": [],
"empDetails": {
"empConnectionId": "<uuid>",
"empConfigurationId": "<uuid>",
"serialNumber": "I000-000-000-000-000-X-X",
"startCode": "<hex-token>",
"installationDate": "YYYY-MM-DD"
},
"empReferenceId": "<uuid>",
"gridConnectionPointPhases": 3,
"maxCurrentPerPhaseAmpere": 63,
"physicalAttributes": {},
"addressLine1": "Musterstraße 1",
"addressZipCode": "20095",
"addressCity": "Hamburg",
"addressCountry": "DE",
"addressLatitude": 0.0,
"addressLongitude": 0.0,
"customerId": "<uuid>",
"customer": {
"id": "<uuid>",
"firstName": "Erika",
"lastName": "Mustermann",
"email": "user@example.com"
},
"technicalContactId": "<uuid>",
"technicalContactName": "1KOMMA5° <Region>",
"externalPartnerId": null,
"dynamicPulseCompatible": true,
"earliestMeasurement": "YYYY-MM-DD",
"energyTraderActive": true,
"electricityContractActive": true,
"impactedByEnwg": false,
"createdAt": "ISO8601",
"updatedAt": "ISO8601"
}
Notes
biddingZone— ENTSO-E bidding zone (Germany + Luxembourg =DE_LU).emsMode— operating mode (TOU= time-of-use / Dynamic Pulse).emsState/emsStateReasons— current EMS runtime state (OPERATIONALwith an empty reason list has been observed; likely populated with codes during faults).empDetailscarries gateway pairing values (serialNumber,startCode) — sensitive, do not log or share.impactedByEnwg— German regulatory flag related to the Energiewirtschaftsgesetz (EnWG). Exact meaning not documented by the API. Most plausible interpretation: §14a EnWG (since 2024-01-01, DSOs may reduce controllable consumption devices — heat pump / wallbox / battery storage / AC ≥ 4.2 kW — during grid stress, in exchange for reduced grid fees). Atruevalue would presumably mean the site has at least one such device registered under §14a.gridConnectionPointPhases/maxCurrentPerPhaseAmpere— grid-connection capacity (phase count; max amperes per phase). Both can benull.customeris the short embedded block; for the full record see Customer record (v3).externalPartnerId— see the note on System details (v1, extended); same field, same semantics.earliestMeasurement,energyTraderActive,electricityContractActiveare also on/systems/{id}/details— redundant here but included in one response.- Does not carry
deviceGateways— use/systems/{id}/detailsfor those. - v2 and v3 return byte-identical payloads (verified 2026-08-02) — no reason to switch.
Site status and assets¶
GET /api/v3/sites/$ONEKOMMAFIVE_SYSTEM/status-and-assets — site connection status plus the installed hardware inventory (inverter, heat pump, meter, EV charger).
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v3/sites/$ONEKOMMAFIVE_SYSTEM/status-and-assets" | jq .
Response
{
"status": "CONNECTED",
"assets": [
{
"id": "<uuid>",
"type": "HYBRID | HEAT_PUMP | METER | EV_CHARGER",
"empType": "GRIDX",
"name": "Wallbox",
"connectionStatus": { "status": "CONNECTED" },
"manufacturer": "...",
"model": "...",
"serialnumber": "...",
"firmware": "...",
"network": { "address": "<local-ip>" },
"heatPumpMeterType": "HOUSEHOLD"
}
]
}
Notes
- Type-specific fields:
namehas only been observed onEV_CHARGERassets.firmwareis often missing onMETERandHEAT_PUMP.heatPumpMeterTypeappears only onHEAT_PUMP(values e.g."HOUSEHOLD").serialnumberis lowercase-n in the API (notserialNumber). On heat pumps the value can take the formmac_<lowercase-mac>.- Typical asset composition for a full installation:
| Type | Manufacturer | Model |
|---|---|---|
HYBRID |
Sungrow | SH6.0RT-V112 |
HEAT_PUMP |
Stiebel Eltron | WPMsystem |
METER |
Chint | DTSU666 |
EV_CHARGER |
go-e | HOMEfix 11kW |
Active feature flags¶
GET /api/v2/customers/$CUSTOMER_ID/sites/$ONEKOMMAFIVE_SYSTEM/active-features — active feature codes for the given customer + site pair.
$CUSTOMER_ID comes from the customerId field of /api/v1/systems/{id}/details.
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://customer-identity.1komma5grad.com/api/v2/customers/$CUSTOMER_ID/sites/$ONEKOMMAFIVE_SYSTEM/active-features" | jq .
Response
Notes
- Lives on the
customer-identityhost, notheartbeat. - Known feature codes (not exhaustive — the list can grow):
| Code | Meaning |
|---|---|
DYNAMIC_TARIFF |
Dynamic electricity tariff is active |
TIME_OF_USE_OPTIMIZATION |
Time-variable tariff optimisation by the EMS |
SMART_CHARGING |
EV smart-charging available |
Device gateways (standalone, v2)¶
GET /api/v2/device-gateways?systemId=$ONEKOMMAFIVE_SYSTEM — paginated list of device gateways for the system, with GridX backend identifiers, installer details, and registration timestamps.
Richer than the deviceGateways block inside /api/v1/systems/{id}/details, which only exposes id, gridxStartCode, serialNumber, and installationDate.
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v2/device-gateways?systemId=$ONEKOMMAFIVE_SYSTEM" | jq .
Response
{
"data": [
{
"id": "870aa545-3c8f-4e59-94db-3325bab31782",
"createdAt": "2025-01-24T09:59:38.587Z",
"updatedAt": "2025-01-24T10:14:22.044Z",
"type": "GRIDX",
"serialNumber": "I482-510-000-014-892-P-X",
"systemId": "…",
"claimedByUserId": "…",
"gridxStartCode": "C603BADF65D59E0E",
"gridxSystemId": "…",
"gridxGatewayId": "…",
"provisioningJob": {
"installerId": "…",
"installerName": "1KOMMA5° Rheinland",
"installationDate": "2025-01-24"
},
"system": {
"id": "…",
"technicalContactId": "…",
"technicalContactName": "1KOMMA5° Rheinland",
"addressCity": "Tönisvorst",
"addressCountry": "DE",
"addressLine1": "Sternstr. 125",
"addressZipCode": "47918"
}
}
],
"pageIndex": 0,
"pageSize": 15,
"totalPages": 1,
"totalItems": 1
}
Live data¶
Live overview¶
GET /api/v3/systems/$ONEKOMMAFIVE_SYSTEM/live-overview — real-time energy overview.
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v3/systems/$ONEKOMMAFIVE_SYSTEM/live-overview" | jq .
Response (excerpt)
{
"timestamp": "ISO8601",
"status": "ONLINE",
"liveHeroView": {
"selfSufficiency": 1,
"production": { "value": 0, "unit": "W" },
"consumption": { "value": 666.53, "unit": "W" },
"gridFeedIn": { "value": 4.66, "unit": "W" },
"gridConsumption": { "value": 0, "unit": "W" },
"grid": { "value": -4.66, "unit": "W" },
"totalStateOfCharge": 0.45,
"evChargersAggregated": { "power": { "value": 0, "unit": "W" } },
"heatPumpsAggregated": { "power": { "value": 0, "unit": "W" } }
},
"summaryCards": {
"grid": { "power": { "value": -4.66, "unit": "W" } },
"battery": { "power": { "value": 671.19, "unit": "W" }, "stateOfCharge": 0.45 },
"photovoltaic": { "production": { "value": 0, "unit": "W" } },
"evChargers": [
{
"applianceId": "<uuid>",
"currentSoc": null,
"power": { "value": 0, "unit": "W" },
"powerSource": null
}
],
"heatPumps": [ { "applianceId": "<uuid>", "power": { "value": 0, "unit": "W" } } ],
"household": { "power": { "value": 666.53, "unit": "W" } }
}
}
Notes
- All power values are in Watts (not kW).
grid.valuesign convention: negative = feeding in, positive = drawing from the grid.- Prefer
summaryCards.battery.poweroverliveHeroView.production-derived battery estimates (API convention: negative = charging; the client flips the sign to positive-for-charging).
Energy¶
Energy today¶
GET /api/v2/systems/$ONEKOMMAFIVE_SYSTEM/energy-today — today's production and consumption plus a timestamped timeseries.
Query parameters
| Name | Values | Description |
|---|---|---|
resolution |
1h (default), 15m |
Time-series bucket size. |
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
'https://heartbeat.1komma5grad.com/api/v2/systems/'"$ONEKOMMAFIVE_SYSTEM"'/energy-today?resolution=1h' | jq .
Response (excerpt)
{
"energyProduced": { "value": 30.76, "unit": "kWh" },
"selfSufficiencyPercent": 0.61,
"heartbeatSavings": { "value": 6.48, "unit": "€" },
"grid": {
"feedIn": { "value": 6.76, "unit": "kWh" },
"supply": { "value": 10.33, "unit": "kWh" }
},
"battery": {
"charge": { "value": 22.42, "unit": "kWh" },
"discharge": { "value": 14.58, "unit": "kWh" }
},
"consumption": {
"direct": { "value": 4.60, "unit": "kWh" },
"total": { "value": 26.50, "unit": "kWh" },
"consumers": {
"ev": { "value": 5.0, "unit": "kWh" },
"heatPump": { "value": 12.0, "unit": "kWh" },
"household": { "value": 13.5, "unit": "kWh" },
"battery": { "value": ..., "unit": "kWh" }
}
},
"timestampedProductionAndConsumption": {
"data": {
"2026-03-08T12:00Z": {
"production": 5.008,
"consumption": {
"household": 0.267,
"householdTotal": 0.602,
"ev": 0,
"evCharge": 0,
"heatPump": 0,
"heatPumpTotal": 0,
"battery": 4.688,
"direct": 0.267
},
"gridSupply": 0.334,
"gridFeedIn": 0.053,
"batteryStateOfCharge": 0.536,
"batteryCharge": 4.688,
"batteryDischarge": 0
}
},
"metadata": { "units": { "production": "kW", "gridSupply": "kW", "gridFeedIn": "kW" } }
}
}
Notes
- Scalar totals are in kWh; timeseries values in kW.
householdvshouseholdTotal:household= share sourced directly from PV;householdTotal= total consumption from all sources (PV + battery + grid). Same convention forheatPump/heatPumpTotalandev/evCharge.
Energy historical¶
GET /api/v3/systems/$ONEKOMMAFIVE_SYSTEM/energy-historical — historical energy for an inclusive date range. Same payload shape as Energy today.
Query parameters
| Name | Values | Description |
|---|---|---|
from |
YYYY-MM-DD |
Start date. |
to |
YYYY-MM-DD |
End date. |
resolution |
1h (default), 15m |
For 15m, the date range must be a single day. |
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
'https://heartbeat.1komma5grad.com/api/v3/systems/'"$ONEKOMMAFIVE_SYSTEM"'/energy-historical?from=2026-03-07&to=2026-03-07&resolution=1h' | jq .
Notes
- Payload structure is identical to Energy today.
Heartbeat savings¶
GET /api/v1/systems/$ONEKOMMAFIVE_SYSTEM/energy-savings — aggregated Heartbeat savings (EUR) for a date range as a single value. Useful when the timeseries overhead of energy-today/energy-historical is not needed.
Query parameters (both optional)
| Name | Values | Description |
|---|---|---|
from |
YYYY-MM-DD |
Start date. Date-only — passing a time component returns HTTP 400. |
to |
YYYY-MM-DD |
End date. Same date-only rule. |
Without parameters the endpoint returns a server-side rolling window (undocumented — neither today nor the current month).
Example
# Default rolling window
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v1/systems/$ONEKOMMAFIVE_SYSTEM/energy-savings" | jq .
# Custom date range
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v1/systems/$ONEKOMMAFIVE_SYSTEM/energy-savings?from=2026-07-01&to=2026-07-31" | jq .
Response
Prices¶
Market prices¶
GET /api/v4/systems/$ONEKOMMAFIVE_SYSTEM/charts/market-prices — spot electricity prices with grid-cost and VAT breakdowns.
Query parameters
| Name | Values | Description |
|---|---|---|
from |
ISO-8601 with millis, e.g. 2026-03-01T00:00:00.000Z |
Start (UTC). |
to |
ISO-8601 with millis, e.g. 2026-03-01T23:59:59.999Z |
End (UTC). |
resolution |
1h, 15m |
Bucket size. |
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
'https://heartbeat.1komma5grad.com/api/v4/systems/'"$ONEKOMMAFIVE_SYSTEM"'/charts/market-prices?from=2026-03-01T00%3A00%3A00.000Z&to=2026-03-01T23%3A59%3A59.999Z&resolution=1h' | jq .
Response
{
"energyMarket": { "averagePrice": { "price": { "amount": "0.137", "currency": "EUR" }, "unit": "kWh" }, "highestPrice": {}, "lowestPrice": {} },
"energyMarketWithGridCosts": { },
"energyMarketWithGridCostsAndVat": { },
"vat": 0.19,
"gridCostsTotal": { "price": { "amount": "0.1636964", "currency": "EUR" }, "unit": "kWh" },
"usesFallbackGridCosts": false,
"timeseries": {
"2026-03-10T18:00Z": {
"marketPrice": "0.23715",
"marketPriceWithVat": "0.2822085",
"marketPriceWithGridCost": "0.37471",
"marketPriceWithGridCostAndVat": "0.4459049",
"gridCosts": "0.13756",
"gridConsumption": 0.00956125,
"gridFeedIn": 0.01150825
}
}
}
Notes
- All prices are delivered as strings in EUR/kWh.
- Timestamps are UTC.
gridConsumption/gridFeedInare in kWh.
Price customizations¶
GET /api/v2/systems/$ONEKOMMAFIVE_SYSTEM/price-customizations — user-configured prices (grid price, comparison price, monthly base fee).
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v2/systems/$ONEKOMMAFIVE_SYSTEM/price-customizations" | jq .
Response
{
"gridEnergyPrice": { "price": { "amount": "0.3039", "currency": "EUR" }, "unit": "kWh" },
"comparisonEnergyPrice": { "price": { "amount": "0.274", "currency": "EUR" }, "unit": "kWh" },
"monthlyBasePrice": { "amount": "13.9", "currency": "EUR" }
}
Notes
- Prices are strings (EUR/kWh);
monthlyBasePriceis in EUR / month.
Comparison price¶
GET /api/v2/comparison-price?siteId=$ONEKOMMAFIVE_SYSTEM — single-value grid-supplier reference price. Equivalent to comparisonEnergyPrice from Price customizations, without the surrounding envelope.
Query parameters
| Name | Required | Description |
|---|---|---|
siteId |
yes | Site UUID as a query parameter — not a path segment. |
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v2/comparison-price?siteId=$ONEKOMMAFIVE_SYSTEM" | jq .
Response
EV chargers and wallbox¶
List EV chargers¶
GET /api/v2/sites/$ONEKOMMAFIVE_SYSTEM/assets/evs — the vehicle-side charging profiles registered to a system (charging mode, target SoC, departure schedule).
For the physical wallbox hardware, see List wallboxes (hardware).
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v2/sites/$ONEKOMMAFIVE_SYSTEM/assets/evs" | jq .
Response (array; single flat object per vehicle)
[
{
"id": "<uuid>",
"type": "EV",
"empType": "GRIDX",
"name": "...",
"connectionStatus": { "status": "UNKNOWN" },
"manufacturer": "...",
"model": "...",
"capacity": { "value": 77000, "unit": "Wh" },
"chargingMode": "SMART_CHARGE",
"departureTime": "06:30",
"targetSoc": 0.8,
"chargerId": "<uuid>",
"manualSoc": 0.8,
"dataSource": "USER_INPUT",
"manualSocTimestamp": "ISO8601",
"defaultSoc": 0.35,
"minChargingCurrent": { "value": 2, "unit": "A" }
}
]
Notes
capacity.unitvaries per user (WhorkWh— the Python SDK normalises to Wh inEVCharger.capacity_wh()).manualSocis a decimal in[0, 1](not a percentage), and is set manually because the wallbox has no SoC feedback channel.departureTimeis the (single) scheduled departure for smart charging; the vehicle is expected to reachtargetSocby that time. The v1-era distinction betweentargetSocandprimaryScheduleDepartureSocwas consolidated in v2 — the app UI now shows one SoC value for scheduled departure.
Available charging modes¶
GET /api/v1/sites/$ONEKOMMAFIVE_SYSTEM/assets/evs/displayed-ev-charging-modes — charging modes available at this site and whether each is currently enabled.
Note the /sites/ prefix (path IDs are the same as for /systems/).
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v1/sites/$ONEKOMMAFIVE_SYSTEM/assets/evs/displayed-ev-charging-modes" | jq .
Response
{
"displayedEvChargingModes": [
{ "type": "SMART_CHARGE", "disabled": false },
{ "type": "SOLAR_CHARGE", "disabled": false },
{ "type": "QUICK_CHARGE", "disabled": false }
],
"emsMode": "TOU"
}
Notes
emsMode: "TOU"= time-of-use (Dynamic Pulse tariff active, exchange prices drive charging decisions).
Update EV settings¶
PATCH /api/v2/sites/$ONEKOMMAFIVE_SYSTEM/assets/evs/$EV_ID — update charging mode, target SoC, current SoC, or departure time.
Each call sends a partial flat body with just the field to change. The endpoint also still accepts the legacy nested form ({"chargeSettings": {...}}), but new callers should prefer the flat form.
Set charging mode
# Allowed values: SMART_CHARGE | QUICK_CHARGE | SOLAR_CHARGE
curl -s -X PATCH \
-H "Authorization: Bearer $BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"chargingMode": "SOLAR_CHARGE"}' \
"https://heartbeat.1komma5grad.com/api/v2/sites/$ONEKOMMAFIVE_SYSTEM/assets/evs/$EV_ID" | jq .
Set current SoC (decimal 0.0–1.0)
curl -s -X PATCH \
-H "Authorization: Bearer $BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"manualSoc": 0.8}' \
"https://heartbeat.1komma5grad.com/api/v2/sites/$ONEKOMMAFIVE_SYSTEM/assets/evs/$EV_ID" | jq .
Set target SoC (decimal 0.0–1.0)
curl -s -X PATCH \
-H "Authorization: Bearer $BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"targetSoc": 0.9}' \
"https://heartbeat.1komma5grad.com/api/v2/sites/$ONEKOMMAFIVE_SYSTEM/assets/evs/$EV_ID" | jq .
Set departure time (format HH:MM)
curl -s -X PATCH \
-H "Authorization: Bearer $BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"departureTime": "07:30"}' \
"https://heartbeat.1komma5grad.com/api/v2/sites/$ONEKOMMAFIVE_SYSTEM/assets/evs/$EV_ID" | jq .
Assign a wallbox to this vehicle — sets chargerId on the EV. The 1KOMMA5° model is 1:1 exclusive, so setting chargerId on EV B automatically releases whichever EV A was previously bound to the same wallbox. The app UI offers no separate "unassign"; only a switch.
curl -s -X PATCH \
-H "Authorization: Bearer $BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"chargerId": "<wallbox-uuid>"}' \
"https://heartbeat.1komma5grad.com/api/v2/sites/$ONEKOMMAFIVE_SYSTEM/assets/evs/$EV_ID" | jq .
List wallboxes (hardware)¶
GET /api/v1/sites/$ONEKOMMAFIVE_SYSTEM/assets/ev-chargers — the physical wallbox hardware assigned to a site (wallbox id, name, currently-paired EV).
Distinct from List EV chargers which returns the vehicle-side profile. assignedEvId links to an entry in /assets/evs.
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v1/sites/$ONEKOMMAFIVE_SYSTEM/assets/ev-chargers" | jq .
Response (array)
The legacy /api/v1/systems/{id}/devices/ev-chargers route returned gridxHardwareId in place of id. That path is gone from the SDK as of v0.2.0 because it returned HTTP 422 error_code 30401 on non-GridX (e.g. Enphase-based) setups; the site-scoped path above works universally.
Energy management (EMS)¶
Get EMS settings¶
GET /api/v1/systems/$ONEKOMMAFIVE_SYSTEM/ems/actions/get-settings — current EMS configuration and per-device manual settings.
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v1/systems/$ONEKOMMAFIVE_SYSTEM/ems/actions/get-settings" | jq .
Response
{
"systemId": "<uuid>",
"consentGiven": true,
"overrideAutoSettings": false,
"timeOfUseEnabled": true,
"manualSettings": {
"0": {
"type": "EV_CHARGER",
"id": "<uuid>",
"assignedEvId": "<uuid>",
"activeChargingMode": "SMART_CHARGE"
},
"1": {
"type": "BATTERY",
"enableForecastCharging": false
},
"2": {
"type": "HEAT_PUMP",
"id": "<uuid>",
"useSolarSurplus": true,
"maxSolarSurplusUsage": { "value": 2, "unit": "kW" }
}
}
}
Notes
overrideAutoSettings: false= AI automatic mode active.manualSettingsuses numeric string keys ("0","1","2"); use thetypefield for identification.
Set EMS mode¶
POST /api/v1/systems/$ONEKOMMAFIVE_SYSTEM/ems/actions/set-manual-override — switch between automatic and manual override.
Request body
overrideAutoSettings: false→ automatic mode.overrideAutoSettings: true→ manual override.
Example
# Enable automatic mode
curl -s -X POST \
-H "Authorization: Bearer $BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"overrideAutoSettings": false}' \
"https://heartbeat.1komma5grad.com/api/v1/systems/$ONEKOMMAFIVE_SYSTEM/ems/actions/set-manual-override" | jq .
Notes
- Successful response is HTTP 201, not 200.
Weather¶
Weather forecast¶
GET /api/v1/systems/$ONEKOMMAFIVE_SYSTEM/weather — weather forecast for the site location: daily summaries for today + tomorrow, plus 3-hour slots for the next 48 hours.
Primarily used by the AI for PV-yield estimation and charging planning.
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v1/systems/$ONEKOMMAFIVE_SYSTEM/weather" | jq .
Response
{
"today": {
"temperatureCelsius": 16.1,
"precipitationMm": 5.2,
"precipitationProbability": 86.5,
"sunshineMinutes": 383.7,
"sunrise": "2026-03-11T05:57Z",
"sunset": "2026-03-11T17:32Z",
"weatherSymbolId": 5
},
"tomorrow": { },
"fineGrainedForecasts": [
{
"periodStart": "2026-03-11T15:00Z",
"windSpeed": 3.8,
"temperatureCelsius": 10.2,
"weatherSymbolId": 5,
"sunshineMinutes": 0,
"precipitationMm": 1.07,
"precipitationProbability": 51.4
}
]
}
Notes
- All timestamps in UTC.
fineGrainedForecastscontains 3-hour slots for the next 48 hours. precipitationProbabilityis a float in0–100.sunshineMinutesontoday/tomorrowis a full-day forecast (can exceed 600); on slots it's max ~60 minutes (out of 180).- Symbol
2(fair) can appear even with lowsunshineMinutes— it describes broken cloud cover, not necessarily much sunshine.
Weather symbol IDs
Night IDs follow the pattern day-ID + 100 (e.g. 5 → 105). Night symbols appear in fineGrainedForecasts slots after sunset.
| Day ID | Night ID | Meaning |
|---|---|---|
1 |
101 |
Sunny / clear |
2 |
102 |
Fair (partly cloudy) |
3 |
103 |
Changing cloudiness |
4 |
104 |
Overcast / heavy cloud |
5 |
105 |
Rain |
8 |
108 |
Slight cloud with showers |
15 |
115 |
Heavy rain / showers |
Values derived from observation, not officially documented.
Heartbeat AI¶
Optimizations¶
GET /api/v1/heartbeat-ai/optimizations — AI optimisation decisions (battery charge / discharge / EV charge) for a time window.
Query parameters
| Name | Required | Description |
|---|---|---|
siteId |
yes | Site UUID. |
from |
yes | ISO-8601 with milliseconds, URL-encoded. Format %Y-%m-%dT%H:%M:%S.000Z. |
to |
yes | ISO-8601 with milliseconds, URL-encoded. Format %Y-%m-%dT%H:%M:%S.999Z. |
view |
yes | historic for arbitrary past ranges; live for the current slot. Without this parameter the endpoint answers 200 with {"events": []} — silent failure. |
view values
historic— arbitraryfrom/toranges for past reviews. Returns actual events for windows in which the AI took decisions.live— the currently-running optimisation slot. The SPA polls this with a 15-minute window ending on the current instant; the same shape is expected from the caller.
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
'https://heartbeat.1komma5grad.com/api/v1/heartbeat-ai/optimizations?siteId='"$ONEKOMMAFIVE_SYSTEM"'&from=2026-03-08T00%3A00%3A00.000Z&to=2026-03-08T23%3A59%3A59.999Z&view=historic' | jq .
Response
{
"events": [
{
"id": "<uuid>",
"timestamp": "ISO8601",
"data": {
"decision": "BATTERY_CHARGE_FROM_GRID",
"from": "ISO8601",
"to": "ISO8601",
"asset": "BATTERY",
"marketPrice": { "value": 24.76, "currency": "EUR" },
"stateOfCharge": 3,
"log": ["ISO8601", "..."]
}
}
]
}
Notes
- Known
decisionvalues (not exhaustive):
| Value | Asset | Meaning |
|---|---|---|
BATTERY_CHARGE_FROM_GRID |
BATTERY | Charge battery from the grid (cheap price) |
BATTERY_NO_DISCHARGE |
BATTERY | Do not discharge battery (price too low) |
EV_CHARGE_FROM_GRID |
EV | Charge EV from the grid |
marketPrice.valueis in EUR/MWh — but empirically closer to the feed-in / trader-side price than to the spot purchase price. See Self-sufficiency events for the observed factor ~4-5 delta vscharts/market-prices.stateOfChargeis a percentage (0–100).from/toare always exactly 15 minutes apart — one slot per event.logis the API's slot-aggregation trick: when consecutive 15-min slots within the same hour bucket (:00–:59:59UTC) carry the samedecision, they're rolled into one event.log[0]equalsto, entries step forward by 15 min, and the list never crosses an hour boundary — solen(log)is 0–3, and the covered time span is1 + len(log)slots.marketPriceandstateOfChargestill describe only the first slot. Ignoringlogundercounts consecutive same-decision slots by up to 4×.
Self-sufficiency events¶
GET /api/v1/heartbeat-ai/self-sufficiency — AI events that explain self-sufficiency outcomes (typically the granular battery discharge/charge trace).
Same payload shape as Optimizations, but a different subset of AI activity — the two are complementary. In a window where /optimizations returns [], /self-sufficiency can return multiple events.
Query parameters — same as Optimizations.
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
'https://heartbeat.1komma5grad.com/api/v1/heartbeat-ai/self-sufficiency?siteId='"$ONEKOMMAFIVE_SYSTEM"'&from=2026-08-01T00%3A00%3A00.000Z&to=2026-08-01T23%3A59%3A59.999Z' | jq .
Response — identical to Optimizations.
Notes on marketPrice
Empirically the values are much lower than the spot price returned by charts/market-prices at the same timestamp — a factor of roughly 4–5 lower. Presumably the feed-in / trader-side price used by the Dynamic-Pulse regime, but the API does not document which of the two it is.
Example (author's account):
| Time | AI marketPrice (EUR/MWh → ct/kWh) |
charts/market-prices (ct/kWh) |
|---|---|---|
| 00:15 | 35.30 → 3.53 | 16.27 |
| 03:00 | 34.55 → 3.46 | 15.45 |
| 06:45 | 31.11 → 3.11 | 14.64 |
AI summary¶
GET /api/v2/heartbeat-ai/summary — aggregated Heartbeat-AI metrics for a resolution window: self-sufficiency, feed-in earnings, CO₂ saved, effective Heartbeat price, and peak-price avoidance.
Query parameters
| Name | Required | Description |
|---|---|---|
siteId |
yes | Site UUID. |
resolution |
yes | One of 1W, 1M, 1Y. Any other value returns HTTP 400. |
Only resolution=1M returns all metrics. 1W and 1Y return only co2Saved, production, carTravelEmission; selfSufficiency and energyEarned come back as null.
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v2/heartbeat-ai/summary?siteId=$ONEKOMMAFIVE_SYSTEM&resolution=1M" | jq .
Response (resolution=1M)
{
"selfSufficiency": {
"percentage": 0.73,
"bySolar": { "value": 331.25, "unit": "kWh" },
"byBattery": { "value": 301.30, "unit": "kWh" },
"socialStanding": null
},
"energyEarned": {
"earnedAmount": { "amount": "30.41", "currency": "EUR" },
"soldEnergy": { "value": 378.75, "unit": "kWh" },
"feedInPrice": { "price": { "amount": "0.0803", "currency": "EUR" }, "unit": "kWh" },
"socialStanding": null
},
"co2Saved": {
"co2Saved": 210.5,
"production": { "value": 580.0, "unit": "kWh" },
"carTravelEmission": { "value": 825.0, "unit": "km" },
"socialStanding": null
},
"heartbeatPrice": { "price": { "amount": "0.0745", "currency": "EUR" }, "unit": "kWh" },
"heartbeatPriceSocialStanding": null,
"peakPriceAvoided": {
"priceAvoided": { "amount": "22.50", "currency": "EUR" },
"batteryChargingCost": { "amount": "37.68", "currency": "EUR" },
"gridChargingCost": { "amount": "60.18", "currency": "EUR" }
}
}
peakPriceAvoided = savings from strategically discharging the battery to avoid expensive grid hours: priceAvoided = net saving, gridChargingCost = what grid consumption would have cost without the strategy, batteryChargingCost = what the strategic battery charging did cost (net saving = grid − battery). Top-level fields of the same name (priceAvoided, batteryChargingCost, gridChargingCost) are empirically always null — the actual value sits exclusively in this nested block.
Response (resolution=1W / 1Y)
{
"selfSufficiency": null,
"energyEarned": null,
"co2Saved": { "co2Saved": null, "production": {}, "carTravelEmission": {} },
"heartbeatPrice": { },
"peakPriceAvoided": null
}
socialStanding fields (throughout) presumably contain community percentiles — always observed as null so far, possibly feature-flagged or anonymised.
Analytics¶
Impact overview (CO2)¶
GET /api/v2/systems/$ONEKOMMAFIVE_SYSTEM/impact-overview — lifetime figures: kg of CO₂ saved for the site, aggregate for the entire customer base, and a global marketing estimate.
The endpoint ignores from, to, and resolution — values are always lifetime totals.
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v2/systems/$ONEKOMMAFIVE_SYSTEM/impact-overview" | jq .
Response
{
"co2Savings": { "value": 3099.24, "unit": "kg" },
"co2CollectiveSavings": { "value": 84779526.33, "unit": "kg" },
"co2GlobalSavingsEstimate": { "value": 2000000, "unit": "tons" }
}
Notes
co2GlobalSavingsEstimateis a marketing figure (tons), not a site-specific measurement.
Energy trader (lifetime)¶
GET /api/v2/energy-trader?siteId=$ONEKOMMAFIVE_SYSTEM — cumulative trading savings for the site. Values accumulate over the site's entire trading history; no date range is supported.
Query parameters
| Name | Required | Description |
|---|---|---|
siteId |
yes | Site UUID — as a query parameter, not a path segment (unlike most heartbeat endpoints). |
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v2/energy-trader?siteId=$ONEKOMMAFIVE_SYSTEM" | jq .
Response
{
"energyTrader": {
"status": "ACTIVE",
"greenEnergySavings": { "amount": "2678.49", "currency": "EUR" },
"energyTraderSavings": { "amount": "231.26", "currency": "EUR" }
}
}
Monthly trading savings¶
GET /api/v1/energy-trader-savings/$ONEKOMMAFIVE_SYSTEM/month — average monthly savings from variable-price trading. Complements the lifetime view of Energy trader.
Path parameter caveat: the segment is the site ID (= $ONEKOMMAFIVE_SYSTEM), not the customer ID, despite the misleading URL structure. Verified live (customer_id → HTTP 403, site_id → 200).
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v1/energy-trader-savings/$ONEKOMMAFIVE_SYSTEM/month" | jq .
Response
Heartbeat prices¶
GET /api/v3/heartbeat-prices?siteId={id} — financial breakdown across five aggregation windows (day, week, month, halfYear, year). Each window has the same structure: PV production, grid feed-in, grid consumption, site totals, and the effective per-kWh Heartbeat price. This is the primary economic dashboard endpoint.
Query parameters
| Name | Required | Description |
|---|---|---|
siteId |
yes | Site UUID as a query parameter — not a path segment. |
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v3/heartbeat-prices?siteId=$ONEKOMMAFIVE_SYSTEM" | jq .
Response (one window shown — remaining windows have identical structure)
{
"day": { /* … same shape as year, populated for the trailing day … */ },
"week": { /* … trailing week … */ },
"month": { /* … trailing month … */ },
"halfYear": { /* … trailing 6 months … */ },
"year": {
"vat": 0.19,
"shouldReportImplausiblePvAndFeedIn": true,
"shouldReportOverriddenPvCost": false,
"usesFeedInEarningsAsHbPrice": false,
"feedInDiscrepancy": 50.46,
"pvProduction": {
"energyProduced": { "value": 7212.12, "unit": "kWh" },
"cost": { "amount": "360.61", "currency": "EUR" },
"price": { "price": { "amount": "0.05", "currency": "EUR" }, "unit": "kWh" }
},
"gridFeedIn": {
"energyFedIn": { "value": 1819.25, "unit": "kWh" },
"compensation": { "amount": "146.09", "currency": "EUR" },
"price": { "price": { "amount": "0.0803", "currency": "EUR" }, "unit": "kWh" }
},
"gridConsumption": {
"energyConsumed": { "value": 8807.68, "unit": "kWh" },
"cost": { "amount": "2377.00", "currency": "EUR" },
"price": { "price": { "amount": "0.2699", "currency": "EUR" }, "unit": "kWh" }
},
"totalConsumption": { "value": 14200.55, "unit": "kWh" },
"totalEnergyCost": { "amount": "2591.52", "currency": "EUR" },
"heartbeatPrice": { "price": { "amount": "0.1825", "currency": "EUR" }, "unit": "kWh" },
"comparisonTariff": { "price": { "amount": "0.2740", "currency": "EUR" }, "unit": "kWh" },
"gridElectricityCost": { "amount": "123.30", "currency": "EUR" },
"energyTaxReduction": { "amount": "0", "currency": "EUR" },
"fixedCostsAndSavings": { "amount": "123.30", "currency": "EUR" },
"peakShavingSavings": null,
"swedishCostsAndSavings": null,
"module1ProvisioningDate": "2025-09-19T00:00Z",
"module1ActiveDaysCount": 365,
"grossModule1SavingsPerYear": { "amount": "121", "currency": "EUR" },
"grossModule1TotalSavings": { "amount": "121", "currency": "EUR" },
"comparisonGridFee": { "amount": "0.0989", "currency": "EUR" },
"comparisonGridFeesTotal": { "amount": "257.04", "currency": "EUR" },
"variableGridFeesTotal": { "amount": "225.12", "currency": "EUR" },
"module3SavingsTotal": { "amount": "31.92", "currency": "EUR" },
"enwg14aTotalSavings": { "amount": "152.92", "currency": "EUR" }
}
}
Notes
Three distinct price semantics — do not confuse:
| Field | Meaning |
|---|---|
pvProduction.price |
Heartbeat's internal valuation of own PV production. Empirically constant at 0.05 EUR/kWh across all windows. Not a market price — an accounting convention. |
gridFeedIn.price |
The contractual feed-in tariff the grid operator pays for exported energy. |
gridConsumption.price |
The effective per-kWh grid-purchase price averaged over the window (varies with dynamic tariff). |
heartbeatPrice.price |
The site's effective all-in per-kWh price for consumed energy — reflects the PV/battery/grid mix minus feed-in earnings plus fixed costs. |
comparisonTariff.price |
Static grid-supplier reference (grundversorger) used for savings comparisons. |
VAT convention (undocumented, working assumption):
The values are presented 1:1 as displayed in the 1KOMMA5° app. German consumer-app price displays are conventionally gross (VAT-inclusive), and comparisonTariff ≈ 0.274 EUR/kWh matches typical German utility gross tariffs. A net-vs-gross mismatch between site prices and grundversorger reference would be misleading UX — so the values are most likely gross. The vat: 0.19 field is included but the API does not document whether it has been applied or is informational. If your application is sensitive to gross/net semantics, verify against your electricity invoice.
Quality flags (per window):
shouldReportImplausiblePvAndFeedIn: truesignals that PV/feed-in values in that window may be implausible (typically appears forhalfYearandyearwhen historical data has gaps).usesFeedInEarningsAsHbPricecontrols whether theheartbeatPricecalculation uses the actual feed-in earnings instead of the internal PV valuation.feedInDiscrepancy— numeric metric of the discrepancy between measured and expected feed-in.
Regional/feature-flagged (usually null, structure unknown when populated):
peakShavingSavings— presumably savings from peak-shaving strategyswedishCostsAndSavings— Sweden-specific cost structure
module1* bundle (four fields, all null until 1KOMMA5° provisions the bundle for the account):
| Field | Meaning |
|---|---|
module1ProvisioningDate |
ISO date the bundle was provisioned. On the observed account this coincided with the iMSys installation. |
module1ActiveDaysCount |
Days the bundle was active in the window. Mirrors the window length verbatim (1/7/30/180/365). |
grossModule1SavingsPerYear |
Annual gross savings projection, in EUR. Constant across all five windows. |
grossModule1TotalSavings |
Accumulated bundle savings over the window, in EUR. API-computed as grossModule1SavingsPerYear × module1ActiveDaysCount / 365. |
Working hypothesis: the §14a EnWG "Modul 1" flat-rate grid-fee reduction per BNetzA BK6-22-300, granted automatically for accounts with an iMSys plus a controllable consumption device (wallbox, heat pump, PV battery) unless another module (2 or 3) was actively chosen. On the observed account the API value 121 EUR/year matches the tariff area's published brutto 144 EUR/year via 121 × 1.19 — so the API value is the net amount. A second data point from a different grid area would promote this from hypothesis to documented fact.
§14a EnWG "Modul 3" bundle (five fields, all null until the site opts into Modul 3 — variable Netzentgelte HT/NT per BK6-22-300, added by the backend in 2026-10):
| Field | Meaning |
|---|---|
comparisonGridFee |
Flat grid-fee reference tariff used as the Modul-3 comparison baseline, in EUR/kWh. |
comparisonGridFeesTotal |
Grid fees the site would have paid in the window under the flat reference tariff, in EUR. |
variableGridFeesTotal |
Grid fees actually incurred in the window under the HT/NT variable tariff, in EUR. |
module3SavingsTotal |
Window savings from the variable tariff, in EUR; equals comparisonGridFeesTotal − variableGridFeesTotal. |
enwg14aTotalSavings |
Combined §14a EnWG savings in the window (Modul 1 flat-rate reduction plus Modul 3 time-variable grid-fee savings), in EUR. |
Smart meter¶
Smart meter registration¶
GET /api/v1/sites/$ONEKOMMAFIVE_SYSTEM/smart-meter — regulatory smart-meter registration data: ENTSO-E control-area EIC, DSO BDEW code, municipality concession fee per kWh — each with validity periods.
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v1/sites/$ONEKOMMAFIVE_SYSTEM/smart-meter" | jq .
Response
{
"siteId": "<uuid>",
"controlAreaEIC": "10YDE-RWENET---I",
"controlAreaEIC__metadata": {
"qualityDescription": "Imported from Enet via address lookup",
"updatedAt": "ISO8601"
},
"dsoBdewCode": [
{
"validFromDate": "2020-01-01",
"validUntilDate": "2027-12-31",
"reference": "9900000000009",
"__metadata": { "qualityDescription": "Imported ...", "updatedAt": "ISO8601" }
}
],
"concessionFeeEURperkWh": [
{
"validFromDate": "2020-01-01",
"validUntilDate": "2027-12-31",
"value": 0.0159,
"__metadata": { "qualityDescription": "Imported ...", "updatedAt": "ISO8601" }
}
]
}
Notes
controlAreaEIC= ENTSO-E control zone (Germany:10YDE-*per transmission-system operator — TenneT / 50Hertz / Amprion / RWENET).dsoBdewCode= 13-digit BDEW code of the distribution-system operator.concessionFeeEURperkWh.value= municipality concession fee in EUR/kWh.- Both arrays contain historic entries with
validFromDate/validUntilDate; the current entry is typically the first element. __metadatablocks document origin and last update — usuallyImported from Enet via address lookup.
Notifications¶
Latest notifications¶
GET /api/v1/users/$USER_ID/notifications/latest?systemId=$ONEKOMMAFIVE_SYSTEM — recent push / in-app notifications for the authenticated user, scoped to one system.
$USER_ID comes from GET /api/v1/users/me.
Query parameters
| Name | Required | Description |
|---|---|---|
systemId |
yes | System UUID — omitting it returns HTTP 400. |
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v1/users/$USER_ID/notifications/latest?systemId=$ONEKOMMAFIVE_SYSTEM" | jq .
Response
{
"data": [
{
"id": "<uuid>",
"createdAt": "ISO8601",
"updatedAt": "ISO8601",
"systemId": "<uuid>",
"userId": "<uuid>",
"type": "ENERGY_MARKET_UPPER_TARGET_REACHED",
"read": true,
"dismissed": false,
"locale": "de",
"title": "Energiepreise steigen",
"body": "Achtung! Die Energiepreise werden heute um 22:00 auf 20.41 ct/kWh steigen. …",
"notificationDetails": {
"settings": {},
"meta": {
"price": { "value": 20.41, "unit": "ct/kWh" },
"dateTime_utc": "ISO8601"
}
}
}
]
}
Notes
typevalues match the categories from Notification settings below.
Notification settings¶
GET /api/v1/systems/$ONEKOMMAFIVE_SYSTEM/users/$USER_ID/notifications/settings — user preferences per notification category with channel toggles (app / push / email).
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v1/systems/$ONEKOMMAFIVE_SYSTEM/users/$USER_ID/notifications/settings" | jq .
Response
{
"langCode": "de",
"settings": {
"CO2_IMPACT": [],
"BATTERY_SOC": [],
"BROADCAST_NEW_ELECTRICITY_PRICES": [
{
"subscriptionId": "<uuid>",
"channels": { "app": true, "push": true, "email": false },
"personalizations": {}
}
],
"SYSTEM_DATA_COLLECTION_ENDED": [],
"EV_DYNAMIC_PULSE": [],
"ENERGY_MARKET_UPPER_TARGET_REACHED": [],
"ENERGY_MARKET_LOWER_TARGET_REACHED": [],
"SYSTEM_HEALTH": []
}
}
An empty array per category = not subscribed. An entry per category carries the associated subscription ID and the active channels.
Observed categories (not exhaustive)
| Category | Meaning |
|---|---|
CO2_IMPACT |
CO₂ impact milestones |
BATTERY_SOC |
Battery SoC thresholds |
BROADCAST_NEW_ELECTRICITY_PRICES |
Daily electricity-price forecast broadcast |
SYSTEM_DATA_COLLECTION_ENDED |
System-data collection stopped |
EV_DYNAMIC_PULSE |
Dynamic-Pulse EV trigger |
ENERGY_MARKET_UPPER_TARGET_REACHED |
Price alert — upper threshold |
ENERGY_MARKET_LOWER_TARGET_REACHED |
Price alert — lower threshold |
SYSTEM_HEALTH |
System health warnings |
API meta¶
Supported versions¶
GET /api/v1/supported-versions — not site-scoped meta endpoint returning target and minimum-supported versions for each client channel.
Example
curl -s -H "Authorization: Bearer $BEARER_TOKEN" \
"https://heartbeat.1komma5grad.com/api/v1/supported-versions" | jq .
Response
{
"b2b": { "targetVersion": "1.10.0", "minimumSupportedVersion": "1.12.0" },
"b2c": { "targetVersion": "1.73.0", "minimumSupportedVersion": "1.73.0" }
}
Notes
b2b= installer / partner client channel.b2c= end-user app channel.- Useful for warning when your own client implementation drops below
minimumSupportedVersion.
Unit reference¶
| Endpoint | Unit convention |
|---|---|
live-overview |
W (instantaneous power) |
energy-today, energy-historical |
kW (timeseries) / kWh (daily totals) |
charts/market-prices |
Prices as string EUR/kWh, quantities in kWh |
heartbeat-ai/optimizations, heartbeat-ai/self-sufficiency |
EUR/MWh (see quirks) |
heartbeat-ai/summary |
kWh / EUR / kg CO₂ / km car equivalent, prices as string EUR/kWh |
impact-overview |
kg CO₂ (site + collective), tons (global estimate) |
energy-trader, energy-trader-savings/.../month |
EUR |
heartbeat-prices |
kWh / EUR / EUR/kWh (prices as string, most likely gross) |
energy-savings |
EUR |
price-customizations, comparison-price |
String EUR/kWh (base fee: EUR/month) |
price-guarantee |
Value per priceGuaranteeUnit (e.g. ct/kWh) |
smart-meter |
Concession fee in EUR/kWh |
assets/evs |
Capacity in Wh or kWh (user-dependent — Python SDK normalises to Wh), charging current in A |
ems/actions/get-settings |
kW (maxSolarSurplusUsage) |
Known API quirks¶
Consolidated reference of every non-obvious behaviour documented above:
Query parameters vs path segments
siteIdis a query parameter (not a path segment) in:/energy-trader,/comparison-price,/heartbeat-ai/summary,/heartbeat-ai/optimizations,/heartbeat-ai/self-sufficiency,/users/{uid}/notifications/latest.- In
/customers/{cid}/price-guaranteethe required query parameter is namedsystemId(notcustomerId, notsiteId), despite the customer scope in the path. /energy-trader-savings/{site_id}/monthtakes the site ID in the path, not the customer ID.
Date and time formats
energy-savingsrequires date-only (YYYY-MM-DD) — datetimes with a time component return HTTP 400.heartbeat-ai/optimizationsandheartbeat-ai/self-sufficiencyrequire ISO-8601 with milliseconds (%Y-%m-%dT%H:%M:%S.000Z/%Y-%m-%dT%H:%M:%S.999Z), URL-encoded.charts/market-pricesrequires ISO-8601 with millisecond precision, URL-encoded.energy-historicalaccepts date-only. Forresolution=15mthe range must be a single day.
Resolution / range constraints
heartbeat-ai/summary: onlyresolution=1Mreturns all metrics.1Wand1Yreturn onlyco2Saved+production+carTravelEmission;selfSufficiencyandenergyEarnedarenull. Any other resolution returns HTTP 400.impact-overviewignores query parameters (always lifetime).energy-traderaccepts no date range (always lifetime).
Hosts
- The
customer-identityhost is used by:/users/me,/customers/{cid}(v3),/customers/{cid}/sites/{sid}/active-features,/customers/{cid}/price-guarantee. - Everything else lives on
heartbeat.
Field naming inconsistencies
status-and-assets: the API returnsserialnumberin lowercase-n, notserialNumber. On heat-pump assets the value can take the formmac_<lowercase-mac>.- Customer v3: the email field is
contactEmail, notemail(the embeddedSystemCustomerusesemail). - Customer v3:
addressCountryis a plain-name string (e.g."Deutschland"), not an ISO code — differs from/systems/{id}which returns"DE".
Semantic pitfalls
heartbeat-ai/optimizations+heartbeat-ai/self-sufficiency:data.marketPriceis empirically much lower than the spot price fromcharts/market-prices(factor ~4–5). Likely the feed-in / trader-side price rather than the grid-purchase price — not documented by the API.heartbeat-ai/summarytop-level fieldspriceAvoided/batteryChargingCost/gridChargingCostare empirically alwaysnull; the populated values sit inside thepeakPriceAvoidedblock./assets/evsreturns the vehicle-side profile (charging mode, target SoC, schedules)./assets/ev-chargersreturns the physical wallbox hardware. They are complementary — link them viaassignedEvId./sites/{id}/detailsv2 and v3 return byte-identical payloads (verified 2026-08-02). Do NOT includedeviceGateways— use/systems/{id}/detailsfor those.
Unit surprises
- EV
capacity.unitis Wh (not kWh). 77000 Wh = 77 kWh. - EV
manualSoc/targetSoc/defaultSocare decimals in[0, 1]— not percentages. - Weather night-symbol IDs = day-symbol ID + 100.
Response-code surprises
POST /ems/actions/set-manual-overridereturns HTTP 201 on success, not 200.