BoxPower Rates API
Integrate the BoxPower utility rates, tariff data, and cost calculation engines directly into your applications.
Authentication
Learn how to securely authenticate your requests using API keys and keep your credentials safe.
Core Endpoints
Make your first API call to the BoxPower Rates endpoints in minutes with step-by-step guides.
API Console
Explore our interactive console to test endpoints and generate snippets in Python, Node, and cURL.
Overview
The BoxPower Rates API provides programmatic access to a comprehensive database of utility rates, tariffs, and energy costs. It is designed around REST, has predictable resource-oriented URLs, accepts JSON-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes, authentication, and verbs.
The API version tracks the contract; the endpoints, parameters and response shapes documented here. The Console version is the interactive view component of the rate data behind it, and moves independently: a data refresh never changes a shape. The /v1 in the base URL is the path's major version, and changes only on a breaking release.
Authentication
The API uses API keys to authenticate requests. You can generate and manage your API keys in the Rate Library Hub.
Your API keys carry many privileges, so be sure to keep them secure! Do not share your secret API keys in publicly accessible areas such as GitHub, client-side code, and so forth.
Core Endpoints
The Rate API is organized around REST. All endpoints return JSON-encoded responses and use standard HTTP status codes. Rate limits are applied per API key.
List the utility companies in the dataset.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| page | integer | 1-based page number.Minimum 1. Defaults to 1. |
| perPage | integer | Items per page. Values above 100 are rejected, not clamped.Between 1 and 100. Defaults to 25. |
| activeOnly | string | Restrict to active utilities.One of "true" or "false". |
Example Response
{
"data": [
{
"id": 13,
"utility_name": "Alaska",
"utility_full_name": "Alaska Electric",
"utility_logo": null,
"subdomain": "alaska",
"is_active": 0,
"created_at": "2024-09-19T22:02:07.000Z",
"updated_at": "2024-10-28T15:34:27.000Z"
}
],
"meta": { "page": 1, "perPage": 25, "total": 32, "totalPages": 2 }
}Every distinct utility name that appears on a rate record: the values `GET /rates?utility=` matches against. Unpaginated.
Example Response
{
"data": [
"4-County Electric Power Association",
"A & N Electric Cooperative - Virginia",
"Adams-Columbia Electric Cooperative",
"AEP - Kentucky Power Company"
],
"meta": { "total": 878 }
}Retrieve one utility by its internal numeric id.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id* | integer | Internal numeric id of the utility.Minimum 1. |
Example Response
{
"data": {
"id": 1,
"utility_name": "PG&E",
"utility_full_name": "Pacific Gas & Electric",
"utility_logo": null,
"subdomain": "pg-e",
"is_active": 0,
"created_at": "2024-08-23T18:58:52.000Z",
"updated_at": "2024-10-28T15:34:15.000Z"
}
}A shortcut for one utility's rate summaries, equivalent to looking the utility up and then calling /rates?utility=<name>. Only pagination applies; the /rates filters are not accepted. It matches rates by utility name, and the names on rate records do not always match those in /utilities, so this can return an empty page. For dependable results, take a name from /utilities/rate-names and call /rates?utility= directly.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id* | integer | Internal numeric id of the utility.Minimum 1. |
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| page | integer | 1-based page number.Minimum 1. Defaults to 1. |
| perPage | integer | Items per page. Values above 100 are rejected, not clamped.Between 1 and 100. Defaults to 25. |
Example Response
{
"data": [],
"meta": { "page": 1, "perPage": 25, "total": 0, "totalPages": 1 }
}Rate summaries: fourteen fields, no tariff structures. Filters combine with AND; there is no free-text search.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| page | integer | 1-based page number.Minimum 1. Defaults to 1. |
| perPage | integer | Items per page. Values above 100 are rejected, not clamped.Between 1 and 100. Defaults to 25. |
| utility | string | Exact utility name. See /utilities/rate-names for the accepted values. |
| sector | string | Customer sector, e.g. Commercial, Residential, Industrial. |
| rateType | string | Filter by rate type. |
| approved | string | Restrict to approved rates.One of "true" or "false". |
Example Response
{
"data": [
{
"id": 9831,
"name": "D-TOU-L",
"utility": "Anaheim Public Utilities",
"sector": "Commercial",
"rate_type": null,
"usenetmetering": 1,
"fixedmonthlycharge": 743.6,
"minmonthlycharge": null,
"voltagecategory": null,
"startdate": 1764547200,
"enddate": null,
"label": "4081620",
"approved": 1,
"updated_at": "2026-08-26T16:19:17.000Z"
}
],
"meta": { "page": 1, "perPage": 25, "total": 19, "totalPages": 1 }
}One complete rate record, including the full tariff structures.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id* | integer | Internal numeric id of the rate.Minimum 1. |
Example Response
{
"data": {
"id": 9831,
"rate_family_id": 2882,
"variant_key": "6e12d0e673d5c20236817951a4223fe1",
"name": "D-TOU-L",
"utility": "Anaheim Public Utilities",
"utility_abbrev": "APU",
"state": "CA",
"sector": "Commercial",
"usenetmetering": 1,
"energyratestructure": [
[{ "rate": 0.12123, "unit": "kWh" }],
[{ "rate": 0.12052, "unit": "kWh" }]
],
"energyweekdayschedule": [[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0]],
"phasewiring": "3-Phase",
"fixedmonthlycharge": 743.6,
"flatdemandunit": "kW",
"demandrateunit": "kW",
"demandratchetpercentage": [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
"startdate": 1764547200,
"enddate": null,
"label": "4081620",
"type": null,
"approved": 1,
"attributes_json": { "Voltage Level": "11kV - 50kV" },
"created_at": "2026-06-09T14:06:17.000Z",
"updated_at": "2026-08-26T16:19:17.000Z"
}
}One complete rate record, looked up by its OpenEI/URDB label rather than our internal id.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| label* | string | The URDB identifier carried on the rate. An empty label is a 400. |
Example Response
{
"data": {
"id": 7518,
"rate_family_id": 789,
"variant_key": "d67a9ed68d760722fc89335a6533d3fb",
"name": "GSA-1",
"utility": "4-County Electric Power Association",
"utility_abbrev": "4CEPA",
"state": "MS",
"sector": "Commercial",
"usenetmetering": 0,
"fixedmonthlycharge": 65.26,
"minmonthlycharge": null,
"phasewiring": "3-Phase",
"energyratestructure": [
[{"rate": 0.12123, "unit": "kWh"}],
[{"rate": 0.12052, "unit": "kWh"}],
[{"rate": 0.1127, "unit": "kWh"}],
[{"rate": 0.11395, "unit": "kWh"}]
],
"energyweekdayschedule": [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
],
"energyweekendschedule": [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
],
"demandratestructure": null,
"startdate": 1743465600,
"enddate": null,
"label": "4071172",
"approved": 1,
"attributes_json": {"Service Type": "Three-Phase"},
"created_at": "2026-06-09T14:05:42.000Z",
"updated_at": "2026-08-26T16:18:27.000Z"
}
}Data shapes
Four things about the payloads catch people out. Rate structures are stored PHP-serialized upstream and decoded for you, so no special parsing is needed. But the shapes below are not the ones most JSON APIs return.
Dates are Unix epoch seconds
startdate and enddate are integers, not ISO strings. A null enddate means the rate is open-ended. By contrast created_at and updated_at are ISO date-times.
Flags are 0 or 1, not booleans
is_active, approved and usenetmetering are integers. The matching query filters, however, take the strings "true" and "false".
Rate structures are periods of tiers
The outer array is periods; each inner array holds the tiers within that period. A tier carries rate, unit, and optionally adj, sell and max.
[
[{ "rate": 0.12123, "unit": "kWh" }],
[{ "rate": 0.12052, "unit": "kWh" }]
]Schedules are a 12×24 grid
Twelve months, each with twenty-four hourly values. Read it as schedule[month][hour], where month 0 is January and hour 0 is midnight, local to the utility. Each value is an index into the matching rate structure's outer array, so a schedule value of 1 means “use period 1”.
Treat every field as optional
A full rate record carries 66 fields, and most are nullable. A simple residential rate populates a handful; a large commercial tariff may populate most of the demand and coincident groups.
Errors and rate limits
Every failure shares one envelope. The details array is present on validation errors, with one entry per bad parameter.
{
"error": {
"code": "validation_error",
"message": "Invalid request parameters",
"details": [
{ "path": "perPage", "message": "Number must be less than or equal to 100" }
]
}
}| Code | Status | Meaning |
|---|---|---|
| unauthorized | 401 | API key missing or not recognisedSend the key as an x-api-key header. |
| not_found | 404 | No such recordCheck the id: it is our internal numeric id, not the URDB label. |
| validation_error | 400 | A query parameter failed validationThe details list names each offending parameter. |
| rate_limited | 429 | Rate limit exceededEvery response carries RateLimit and RateLimit-Policy headers (IETF draft-7). Read them and back off before you hit this. |
| internal_error | 500 | Unexpected server errorOpaque by design. Quote the request_id if one is present. |
Rate limits
Limits are applied per API key. Every response, not just a 429, carries RateLimit and RateLimit-Policy headers following IETF draft-7, so you can back off before you are throttled rather than after.
