Denowatts API v3
Overview
The Denowatts API v3 is a read-only REST API for pulling device and site telemetry out of Denowatts: raw channel readings, curated rollups, and daily site summaries. It's intended for customers and integration partners who need to bring Denowatts data into their own systems (BI tools, historians, custom dashboards, billing pipelines, etc.).
By using this API, all Users, including Site Owners and Providers, agree to Denowatts' Terms of Use and Privacy Policy.
Base URL
https://data.denowatts.com/api/v3
All endpoint paths in this reference are relative to this base URL.
Authentication
Each Company is issued an API key, visible on any Site Channel Map it has access to. Send it as an HTTP header on every request:
x-api-key: YOUR_API_KEY
Header names are case-insensitive, but x-api-key is the canonical form.
Quickstart
curl "https://data.denowatts.com/api/v3/site-rollup?site=655c9473931af4bce4f457bc&start=2026-07-01T00:00:00Z&end=2026-07-02T00:00:00Z" \
-H "x-api-key: YOUR_API_KEY"
Conventions
Timestamps. All timestamps in requests and responses are UTC, in ISO 8601 format (for example 2026-07-01T14:00:00Z). The start and end bounds are both inclusive.
Time buckets. Each endpoint labels its records differently:
channel-rawuses an ending timestamp: the record is labeled with the time the reading was taken.channel-rollupandsite-rollupuse a beginning timestamp: a record labeled14:00covers14:00–14:05.site-daily-rollupbuckets records by the site's local calendar day, not the UTC day. Each record carries adatefield, andstart/endfilter against thatdaterather than againsttimestamp.
Identifiers. site is a 24-character hexadecimal ID. channel is a dotted device-channel address (see Finding Site and Channel IDs).
History window. Each endpoint limits how far into the past start may reach, measured back from the start of the current UTC day. This caps the age of the data you can request, not the width of a single request: a start older than the window is rejected with 400 Bad Request even when the start–end span is small. See History Windows.
Numeric values. All numeric fields are rounded to 4 decimal places.
Field names. Metric field names (nrgNetPmtr, irrPoaRef, tempBom, …) are shared across the platform. Definitions and units for each are listed on the portal's Metrics List.
Endpoint Overview
There are four endpoints, each covering a different level of curation and update latency:
| Endpoint | Description | Interval | Timestamp convention | Typical latency |
|---|---|---|---|---|
channel-raw | Raw device-level readings, as reported by the device | As read | Ending timestamp | ~1 minute |
channel-rollup | Curated device-level rollups | 5 minute | Beginning timestamp | ~20 minutes |
site-rollup | Curated, site-level aggregate rollups | 5 minute | Beginning timestamp | ~20 minutes |
site-daily-rollup | Site-rollup records aggregated by day | 1 day | Site-local calendar day | Next day* |
Query Parameters
These parameters are shared across all four endpoints unless the Applies to column says otherwise.
| Parameter | Required | Type | Applies to | Description |
|---|---|---|---|---|
site | Yes | string (24-char hex ID) | All | The site to query, e.g. 655c9473931af4bce4f457bc. Sites are Denowatts' top-level project unit, each with its own name, energy model, and meters. |
channel | Yes | string | channel-raw, channel-rollup | A single device channel, formatted <deviceType>.<deviceMajor>.<deviceMinor>.<channelId>, e.g. 1.1.1.9032. See Device Types. |
start | Yes | ISO 8601 UTC datetime | All | Start of the record range, inclusive. |
end | Yes | ISO 8601 UTC datetime | All | End of the record range, inclusive. |
metrics | No | comma-separated string | All | Restrict the response to specific fields, e.g. metrics=irrPoaRef,tempBom. The timestamp field is always returned, whether or not you list it. Omit this parameter to return every stored field for that endpoint. |
order | No | reverse | All | Sort order by timestamp. Omit for ascending (oldest first); pass order=reverse for descending (newest first). |
page | No | integer | All | Page number, 1-indexed. Defaults to 1. |
limit | No | integer | All | Records per page. Defaults to 1000 and is capped at 1000; any larger value is clamped down to 1000. |
Finding Site and Channel IDs
site and channel values, along with the metrics each device reports, are listed on each site's Channel Map in the Denowatts Portal:

Endpoints
GET /channel-raw
Raw, per-channel device telemetry. Field names vary by device and channel type; a raw record only ever contains whatever that device reports (irradiance sensors, inverters, and weather stations all report different fields), so use metrics to pin down the fields you need.
Parameters
site, channel, start, end, metrics, order, page, limit. See Query Parameters.
Example request
curl "https://data.denowatts.com/api/v3/channel-raw?site=66b60e72e0b91615654f0731&channel=1.1.1.9032&metrics=irrPoaRef,irrHorRef,tempBom&start=2026-07-01T14:18:11.687Z&end=2026-07-01T15:18:11.687Z" \
-H "x-api-key: YOUR_API_KEY"const url = new URL("https://data.denowatts.com/api/v3/channel-raw");
url.search = new URLSearchParams({
site: "66b60e72e0b91615654f0731",
channel: "1.1.1.9032",
metrics: "irrPoaRef,irrHorRef,tempBom",
start: "2026-07-01T14:18:11.687Z",
end: "2026-07-01T15:18:11.687Z",
});
const res = await fetch(url, { headers: { "x-api-key": "YOUR_API_KEY" } });
const data = await res.json();import requests
response = requests.get(
"https://data.denowatts.com/api/v3/channel-raw",
params={
"site": "66b60e72e0b91615654f0731",
"channel": "1.1.1.9032",
"metrics": "irrPoaRef,irrHorRef,tempBom",
"start": "2026-07-01T14:18:11.687Z",
"end": "2026-07-01T15:18:11.687Z",
},
headers={"x-api-key": "YOUR_API_KEY"},
)
data = response.json()Example response
{
"docs": [
{ "timestamp": "2026-07-01T14:19:00.000Z", "irrPoaRef": 612.4, "irrHorRef": 588.1, "tempBom": 31.2 },
{ "timestamp": "2026-07-01T14:24:00.000Z", "irrPoaRef": 618.9, "irrHorRef": 594.7, "tempBom": 31.6 }
],
"totalDocs": 24,
"limit": 1000,
"totalPages": 1,
"page": 1,
"pagingCounter": 1,
"hasPrevPage": false,
"hasNextPage": false,
"prevPage": null,
"nextPage": null
}
GET /channel-rollup
Curated 5-minute device-level rollups: the cleaned-up counterpart to channel-raw, with a fixed field set rather than raw per-device fields.
Parameters
site, channel, start, end, metrics, order, page, limit. See Query Parameters.
Example request
curl "https://data.denowatts.com/api/v3/channel-rollup?site=66b60e72e0b91615654f0731&channel=1.1.1.9032&start=2026-07-01T00:00:00Z&end=2026-07-01T06:00:00Z" \
-H "x-api-key: YOUR_API_KEY"const url = new URL("https://data.denowatts.com/api/v3/channel-rollup");
url.search = new URLSearchParams({
site: "66b60e72e0b91615654f0731",
channel: "1.1.1.9032",
start: "2026-07-01T00:00:00Z",
end: "2026-07-01T06:00:00Z",
});
const res = await fetch(url, { headers: { "x-api-key": "YOUR_API_KEY" } });
const data = await res.json();import requests
response = requests.get(
"https://data.denowatts.com/api/v3/channel-rollup",
params={
"site": "66b60e72e0b91615654f0731",
"channel": "1.1.1.9032",
"start": "2026-07-01T00:00:00Z",
"end": "2026-07-01T06:00:00Z",
},
headers={"x-api-key": "YOUR_API_KEY"},
)
data = response.json()Example response
{
"docs": [
{
"timestamp": "2026-07-01T00:00:00.000Z",
"dbRssi": -62,
"insDeno": 0,
"irrDeno1": 0,
"irrDeno2": 0,
"irrDeno3": 0,
"nrgCompared": 0,
"nrgDenoExpected": 0,
"statAcrylic": 0,
"statTeflon": 0,
"statWeighting1": 1,
"statWeighting2": 0,
"tempBom": 18.4,
"tempCellDeno": 18.1,
"vDenoBat": 3.31,
"vDenoCap": 5.02
}
],
"totalDocs": 72,
"limit": 1000,
"totalPages": 1,
"page": 1,
"pagingCounter": 1,
"hasPrevPage": false,
"hasNextPage": false,
"prevPage": null,
"nextPage": null
}
GET /site-rollup
Curated 5-minute rollups aggregated across an entire site (all channels combined), rather than a single device channel.
Parameters
site, start, end, metrics, order, page, limit. See Query Parameters. This endpoint takes no channel; it is site-wide by design.
Example request
curl "https://data.denowatts.com/api/v3/site-rollup?site=655c9473931af4bce4f457bc&start=2026-07-01T00:00:00Z&end=2026-07-01T06:00:00Z" \
-H "x-api-key: YOUR_API_KEY"const url = new URL("https://data.denowatts.com/api/v3/site-rollup");
url.search = new URLSearchParams({
site: "655c9473931af4bce4f457bc",
start: "2026-07-01T00:00:00Z",
end: "2026-07-01T06:00:00Z",
});
const res = await fetch(url, { headers: { "x-api-key": "YOUR_API_KEY" } });
const data = await res.json();import requests
response = requests.get(
"https://data.denowatts.com/api/v3/site-rollup",
params={
"site": "655c9473931af4bce4f457bc",
"start": "2026-07-01T00:00:00Z",
"end": "2026-07-01T06:00:00Z",
},
headers={"x-api-key": "YOUR_API_KEY"},
)
data = response.json()Example response
{
"docs": [
{
"timestamp": "2026-07-01T00:00:00.000Z",
"nrgNetInv": 0,
"nrgNetPmtr": 0,
"pwrAcActiveInv": 0,
"pwrAcActivePmtr": 0,
"pwrExpected": 0,
"tempBom": 18.4
}
],
"totalDocs": 72,
"limit": 1000,
"totalPages": 1,
"page": 1,
"pagingCounter": 1,
"hasPrevPage": false,
"hasNextPage": false,
"prevPage": null,
"nextPage": null
}
GET /site-daily-rollup
Site-rollup data aggregated by calendar day, one record per site per day, including loss attribution once it's been confirmed (see the note under Endpoint Overview).
Days are the site's local calendar day, not UTC days. Records carry a date field, and start/end filter against that date rather than against timestamp.
Parameters
site, start, end, metrics, order, page, limit. See Query Parameters. This endpoint takes no channel.
Example request
curl "https://data.denowatts.com/api/v3/site-daily-rollup?site=655c9473931af4bce4f457bc&start=2026-06-01T00:00:00Z&end=2026-07-01T00:00:00Z" \
-H "x-api-key: YOUR_API_KEY"const url = new URL("https://data.denowatts.com/api/v3/site-daily-rollup");
url.search = new URLSearchParams({
site: "655c9473931af4bce4f457bc",
start: "2026-06-01T00:00:00Z",
end: "2026-07-01T00:00:00Z",
});
const res = await fetch(url, { headers: { "x-api-key": "YOUR_API_KEY" } });
const data = await res.json();import requests
response = requests.get(
"https://data.denowatts.com/api/v3/site-daily-rollup",
params={
"site": "655c9473931af4bce4f457bc",
"start": "2026-06-01T00:00:00Z",
"end": "2026-07-01T00:00:00Z",
},
headers={"x-api-key": "YOUR_API_KEY"},
)
data = response.json()Example response
{
"docs": [
{
"date": "2026-06-30T00:00:00.000Z",
"nrgAcActivePmtr": 4123.7,
"nrgNetPmtr": 4108.2,
"nrgExpected": 4300.5,
"nrgExpectedCloud": 4290.1,
"nrgCommissioned": 4450,
"nrgCommissionedAged": 4402.3,
"nrgLearned": 4310.9,
"nrgLssOutage": 12.4,
"nrgLssShade": 3.1,
"nrgLssSnow": 0,
"nrgLssSystemic": 8.9,
"nrgProducedPred": 4300.5,
"insHorRef": 6.21,
"insPoaRef": 6.55,
"dptPrecip24h": 0,
"dptSnow24h": 0,
"tempAmb": 21.3,
"tempBom": 24.8,
"tempCell": 25.1,
"tempCellBom": 24.9,
"tempCellDeno": 24.6
}
],
"totalDocs": 30,
"limit": 1000,
"totalPages": 1,
"page": 1,
"pagingCounter": 1,
"hasPrevPage": false,
"hasNextPage": false,
"prevPage": null,
"nextPage": null
}
Response Format
All four endpoints return the same paginated envelope:
| Field | Type | Description |
|---|---|---|
docs | array | The matching records for this page. |
totalDocs | integer | Total number of records matching the query, across all pages. |
limit | integer | Records per page for this response. |
totalPages | integer | Total number of pages available. |
page | integer | The current page number. |
pagingCounter | integer | The index of the first record on this page. |
hasPrevPage | boolean | Whether a previous page exists. |
hasNextPage | boolean | Whether a next page exists. |
prevPage | integer | null | The previous page number, or null if none. |
nextPage | integer | null | The next page number, or null if none. |
Step through a large result set with page, or shrink it with narrower start/end windows and a smaller metrics selection.
Rate Limits
History Windows
Each endpoint limits how far into the past start may reach, measured back from the start of the current UTC day:
| Endpoint | Oldest start allowed |
|---|---|
channel-raw | 30 days ago |
channel-rollup | 90 days ago |
site-rollup | 365 days ago |
site-daily-rollup | No limit |
This is a limit on the age of the data, not on the size of a single request. Splitting an old range into smaller windows will not get around it: a start outside the window is rejected however narrow the request is. For history older than a window, use a coarser endpoint (site-daily-rollup has no limit), and use page to walk through large result sets.
Errors
| Code | Meaning |
|---|---|
200 OK | Successful request. |
400 Bad Request | The request was malformed: a required parameter is missing, a parameter has the wrong shape (e.g. site isn't a valid ID, start/end isn't a valid date), or start is older than that endpoint's history window. |
403 Forbidden | The API key is missing or invalid, your IP isn't whitelisted, or your Company doesn't have access to the requested site. |
429 Too Many Requests | You've exceeded the rate limit for this API key + site pair. |
5xx Server Error | An error occurred on the Denowatts side. If this persists, contact support. |
All error responses share the same shape. message is a string for most errors, but parameter-validation failures return an array of strings, one per invalid parameter:
{
"statusCode": 403,
"timestamp": "2026-07-23T14:18:11.687Z",
"path": "/api/v3/site-rollup?site=655c9473931af4bce4f457bc&start=2026-07-01T00:00:00Z&end=2026-07-02T00:00:00Z",
"message": "IP not whitelisted"
}
Support
For access requests, IP whitelisting, or any other questions, contact support@denowatts.com.
Last updated on