Skip to content

Rates

The rates endpoints expose the electricity rate curve the optimizer saves against, plus the metadata to render and edit the user’s pricing selection. Use them to color a schedule timeline by peak / off-peak pricing, or to let the user pick how their electricity is priced.

All values are in cents per kWh. Clients displaying dollars divide by 100; clients submitting dollars multiply by 100 first.

The rate curve has the same 48 × 30-minute shape as schedules — each interval is a half-hour slot of the local day, indexed 0 (00:00–00:30) through 47 (23:30–00:00).

A user’s rates resolve from one of three sources, chosen via PUT /api/v1/rates:

SourceWhat it is
zoneA preset time-of-use plan from the API-served available_pricing_zones catalog (real residential utility plans). The default.
customThe user’s own 24-hour hourly_rates_cents_per_kwh array, expanded to 48 half-hour slots.
dynamicLive day-ahead wholesale pricing for the user’s chosen grid region, plus a flat per-kWh adder for delivery/tax riders. Falls back to the zone curve if data is unavailable — the response then reports source: "zone" while pricing_source still reads "dynamic".

Auth: Required (Bearer JWT)

Terminal window
curl https://api.hungrymachines.io/api/v1/rates \
-H "Authorization: Bearer YOUR_TOKEN"
{
"pricing_location": 1,
"intervals": [0, 1, 2, "...", 47],
"rates_cents_per_kwh": [24.7, 24.7, "... 48 values total ..."],
"source": "zone",
"season": "summer",
"hourly_rates_cents_per_kwh": null,
"pricing_source": "zone",
"dynamic_zone": null,
"pricing_adder_cents_per_kwh": null,
"available_dynamic_zones": [
{ "slug": "comed", "iso": "PJM", "label": "ComEd (Northern Illinois)" },
{ "slug": "ameren", "iso": "MISO", "label": "Ameren Illinois (Power Smart Pricing)" }
],
"available_pricing_zones": [
{ "id": 1, "slug": "sdge-tou-dr1", "utility": "SDG&E", "plan": "TOU-DR1", "region": "San Diego, CA", "label": "SDG&E TOU-DR1 — San Diego", "notes": "peak 4-9pm" },
"... more entries ..."
],
"unit": "cents/kWh"
}

The custom and dynamic responses use the same shape; only source and the source-specific fields change:

  • customsource: "custom", hourly_rates_cents_per_kwh holds the raw 24-element override.
  • dynamicsource: "dynamic", dynamic_zone is the chosen slug, pricing_adder_cents_per_kwh is the per-user adder.
FieldTypeDescription
pricing_locationintegerCatalog id from available_pricing_zones; defaults to 1 when unset
intervalsarray[int]Indices 047 (30-minute slots of the local day)
rates_cents_per_kwharray[float]Exactly 48 cents/kWh values for the resolved source
sourcestringWhat the resolver actually used this call: "zone", "custom", or "dynamic". May differ from pricing_source when dynamic falls back.
seasonstring"summer" or "winter", from the user’s local date. Only some plans differ by season; others report the season but return the same curve.
hourly_rates_cents_per_kwharray[float] | nullThe user’s raw 24-element override, only when source is "custom"
pricing_sourcestringThe user’s stored preference: "zone", "custom", or "dynamic". Reflect this in your UI toggle.
dynamic_zonestring | nullThe user’s chosen dynamic-pricing slug. null when none selected.
pricing_adder_cents_per_kwhnumber | nullPer-user flat adder for dynamic pricing. null = use the global default.
available_dynamic_zonesarray[{slug, iso, label}]Catalog for the dynamic source. Use slug when sending dynamic_zone; render label; iso is informational.
available_pricing_zonesarray[{id, slug, utility, plan, region, label, notes}]Catalog of preset plans. Use id when sending pricing_location; render label.
unitstringAlways "cents/kWh"
StatusDetailCause
401"Not authenticated"Missing or invalid token
503"Database not configured"DB not configured on the server

Set or clear any subset of the user’s pricing configuration. Only fields you explicitly send are updated — omitted fields are left unchanged.

Auth: Required (Bearer JWT)

All fields are optional. What you send depends on the source you want:

  • Custom rates — send hourly_rates_cents_per_kwh (24 values), or null to clear.
  • Dynamic — send pricing_source: "dynamic" plus a dynamic_zone (required here or already stored). pricing_adder_cents_per_kwh is optional.
  • Preset zone — send pricing_source: "zone" plus a pricing_location id.
Terminal window
curl -X PUT https://api.hungrymachines.io/api/v1/rates \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"hourly_rates_cents_per_kwh": [40.0, 40.0, "... 24 values total ..."]}'

Clear the override with {"hourly_rates_cents_per_kwh": null}.

Terminal window
curl -X PUT https://api.hungrymachines.io/api/v1/rates \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"pricing_source": "dynamic", "dynamic_zone": "comed", "pricing_adder_cents_per_kwh": 6.5}'
Terminal window
curl -X PUT https://api.hungrymachines.io/api/v1/rates \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"pricing_source": "zone", "pricing_location": 7}'
FieldTypeNotes
hourly_rates_cents_per_kwharray[float] | nullExactly 24 floats (00:0023:00), each in [0, 200]. null clears the override.
pricing_sourcestring | null"zone", "custom", or "dynamic". null clears the explicit selection.
dynamic_zonestring | nullSlug from available_dynamic_zones (case-insensitive). Required (here or already stored) when pricing_source="dynamic".
pricing_adder_cents_per_kwhnumber | nullFlat per-kWh adder for dynamic pricing, in [0, 50]. null clears the per-user override.
pricing_locationintegerCatalog id from available_pricing_zones. Validated against the live catalog.

Same shape as GET /api/v1/rates, reflecting the updated state.

StatusDetailCause
400"hourly_rates_cents_per_kwh must have exactly 24 values"Array length is not 24
400"rate at hour <i> must be between 0 and 200 cents/kWh"Value non-finite or outside [0, 200]
400"pricing_source must be one of ['zone', 'custom', 'dynamic']"Unknown source
400"pricing_source='dynamic' requires dynamic_zone (one of: [...])"Dynamic selected without a resolvable zone
400"dynamic_zone must be one of: [...]"Unknown dynamic_zone slug
400"pricing_adder_cents_per_kwh must be a number between 0 and 50 cents/kWh"Adder non-numeric or out of range
400"pricing_location must be one of the catalog ids: [...]"pricing_location not in available_pricing_zones
401"Not authenticated"Missing or invalid token
503"Database not configured"DB not configured or write failed