Introduction
tou.tools resolves a U.S. address or ZIP code to its electric utility, then reports the active Time-of-Use (TOU) period — on-peak, off-peak, and everything between — along with when the next period begins.
The scope is deliberately narrow: period names, boundaries, and schedules only. tou.tools does not return ¢/kWh prices — those vary by tariff rider, customer class, and baseline allowance, and are out of scope. What you get is the timing layer: which period is active, and exactly when it changes.
Two ways to integrate
- REST API — plain HTTP
GETrequests returning JSON. Best for apps, dashboards, and backend services. - MCP server — a Model Context Protocol endpoint that AI agents can call as a native tool, with no integration code. See MCP integration.
Base URL
All REST endpoints are served under a single versioned base path:
https://tou.tools/api/v1
Authentication
Requests authenticate with an API key passed as a bearer token in the
Authorization header. Keys are issued from your dashboard
under API keys, and are prefixed tou_.
Authorization: Bearer tou_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
The raw key is shown once, at creation — store it
somewhere safe. Every /query/* request and every MCP
call must carry a valid key; requests without one are rejected
with 401 Unauthorized.
Quickstart
Ask whether power is peak or off-peak at a ZIP code right now. This is the single most common call:
# current period for ZIP 94103 (San Francisco) curl https://tou.tools/api/v1/query/current?zip_code=94103 \ -H "Authorization: Bearer tou_YOUR_API_KEY"
The response lists every active TOU rate plan for that location, each with its current period and the timestamp of the next change. See Current period for the full shape.
Current period
Returns the live TOU period for every active rate plan serving a location. If the ZIP maps to more than one utility, every candidate is returned — tou.tools never silently guesses.
Query parameters
| Parameter | Type | Description |
|---|---|---|
| zip_code | string · required | 5-digit U.S. ZIP code. |
| customer_class | string | One of residential, commercial, industrial, agricultural, ev. Defaults to residential. |
| at | string | ISO 8601 timestamp, must include a timezone offset. Defaults to the current time (UTC). Use it to ask "what period applies at this moment?" |
| street | string | Street address. When supplied, triggers address-level disambiguation if the ZIP alone is ambiguous (see Location resolution). |
| city | string | Optional address component, used alongside street. |
| state | string | Two-letter U.S. state code, used alongside street. |
Example response
{
"zip_code": "94103",
"confidence": "unique",
"resolved_by": "zip",
"customer_class": "residential",
"results": [
{
"rate_plan": {
"id": "8f3a2c10-4b6d-4e9a-9f12-7c5e1d8a0b34",
"utility_number": 14328,
"utility_name": "Pacific Gas & Electric Co",
"code": "E-TOU-C",
"name": "Residential Time-of-Use",
"customer_class": "residential",
"timezone": "America/Los_Angeles",
"is_closed": false,
"effective_from": "2024-01-01",
"effective_to": null,
"last_verified_at": "2026-04-30T18:22:00Z"
},
"period": "off_peak",
"next_change_at": "2026-05-16T16:00:00-07:00",
"as_of": "2026-05-16T11:30:00-07:00"
}
]
}
Response fields
| Field | Type | Description |
|---|---|---|
| confidence | string | unique, ambiguous, or no_coverage — see Location resolution. |
| resolved_by | string | zip or address. address only when a point lookup actually ran. |
| results[] | array | One entry per active rate plan. An empty array is normal when no candidate utility has a seeded plan yet. |
| period | string | The active period name — see TOU periods. |
| next_change_at | string | Timestamp when the period next changes, or null if the schedule has no further boundary. |
| last_verified_at | string | When the schedule was last cross-checked against the live tariff — a freshness signal. |
Full schedule
Returns the complete rule set for a single rate plan — every period window, season bound, and weekday mask. Use it to render a calendar or precompute upcoming periods client-side.
Query parameters
| Parameter | Type | Description |
|---|---|---|
| rate_plan_id | uuid · required | The rate plan's id, as returned in any rate_plan object from /query/current. |
Example response
{
"rate_plan": { /* same shape as in /query/current */ },
"rules": [
{
"period_name": "on_peak",
"season_start_md": "06-01",
"season_end_md": "09-30",
"days_of_week": 31,
"time_start": "16:00:00",
"time_end": "21:00:00",
"excluded_holidays": ["Independence Day"],
"holidays_as_dow": null,
"priority": 0
}
]
}
Rule fields
| Field | Type | Description |
|---|---|---|
| period_name | string | The period this rule defines. Any time not covered by a rule is off_peak. |
| season_start_md season_end_md | string | MM-DD season bounds. If the end falls before the start, the season wraps the year-end. |
| days_of_week | integer | Weekday bitmask — bit 0 = Monday … bit 6 = Sunday. 31 = Mon–Fri. |
| time_start time_end | string | Local time window (utility timezone). If the end is ≤ the start, the window wraps midnight. |
| excluded_holidays | array | Holiday names treated as off-peak; resolved to observed dates at query time. |
| priority | integer | When two rules both match a moment, the one with the higher priority wins. Defaults to 0. |
Coverage
Lists every utility with at least one active rate plan, the states it serves, and which customer classes are covered. Public — no key required.
{
"summary": {
"total_utilities": 1207,
"total_states": 51,
"total_rate_plans": 0,
"last_verified_at": "2026-04-30T18:22:00Z"
},
"utilities": [ /* one object per covered utility */ ]
}
Health
A liveness probe — no auth, no parameters.
{ "status": "ok" }
TOU periods
Every period in a response is one of the names below.
off_peak is the implicit default — it is returned whenever
no schedule rule matches the queried moment.
| Period | Meaning |
|---|---|
| off_peak | Lowest-demand window, and the implicit default returned whenever no schedule rule matches the queried moment. |
| mid_peak | Shoulder period between off-peak and on-peak, used by tariffs with a three-tier schedule. |
| on_peak | Highest-demand window, typically late afternoon into evening. |
| super_off_peak | Deepest discount window — often overnight or midday solar hours. |
| critical_peak | Event-based critical-peak window declared on a limited number of days. |
Customer classes
A single ZIP may have different TOU schedules for a home, a storefront,
and a factory. The customer_class parameter selects which
set of rate plans to evaluate.
residential— homes and apartments. The default.commercial— offices, retail, and other small-to-medium businesses.industrial— large-load industrial sites.agricultural— farms and irrigation accounts.ev— dedicated electric-vehicle charging rate plans.
Location resolution
U.S. electric service territories do not follow ZIP boundaries. A ZIP
can fall entirely inside one utility, straddle two, or land in an area
with no mapped retail utility. tou.tools reports this honestly with the
confidence field:
unique— the ZIP maps to exactly one utility.ambiguous— the ZIP overlaps multiple utilities; every candidate is returned.no_coverage— no mapped retail utility for that ZIP.
When a ZIP is ambiguous, pass street (plus
optionally city and state) and tou.tools will
geocode the address to narrow candidates to the territory that actually
contains the point. The result's resolved_by becomes
address when that lookup ran. If geocoding fails or the
point lands outside every candidate territory, the full ambiguous
result is returned unchanged — coverage is never silently dropped.
Errors
Errors use standard HTTP status codes with a JSON body carrying a
detail message.
| Status | Meaning |
|---|---|
| 400 | Bad request — e.g. an unknown customer_class, or an at value missing its timezone offset. |
| 401 | Unauthorized — the Authorization header is missing, or the API key is invalid or revoked. |
| 404 | Not found — e.g. a rate_plan_id that does not exist. |
| 422 | Validation error — a malformed parameter, such as a non-5-digit zip_code. |
| 429 | Quota exceeded — you've reached your plan's monthly call cap, or a query to a new location would exceed your location cap. The detail names the limit and the date your billing cycle resets. |
{ "detail": "`at` must include a timezone offset" }
Plan limits are counted per billing cycle and reset on your
signup anniversary — see plans & limits.
Over MCP there is no HTTP status: a quota-blocked tool call
returns the same message in an { "error": … } object.
MCP integration
tou.tools ships as a Model Context Protocol server, so any MCP-capable AI client — Claude Code, Cursor, Windsurf, Cline, Continue, Zed, Claude Desktop, ChatGPT, and others — can query TOU data as a native tool with no SDK or glue code. The server speaks streamable HTTP at:
https://tou.tools/mcp
Available tools
| Tool | Arguments | Returns |
|---|---|---|
| get_current_period | zip_code | Current period and next-change time for each rate plan serving the ZIP. |
| get_schedule | rate_plan_id | Full schedule — all periods, ranges, and seasons. |
| list_utilities | state | Utilities with TOU rate plans in that state. |
All three tools are live, backed by the same resolver as the REST API. Two ways to authenticate — pick whichever your client supports.
Option A — API key (desktop and CLI clients)
For local clients — Claude Code, Cursor, Windsurf, Cline,
Continue, Zed, Claude Desktop, and similar — connect with an
Authorization: Bearer tou_… header (the same API
key you use for REST). Mint one on the
API Keys page,
then add tou.tools to your client's MCP server config.
Most clients accept the standard mcpServers JSON
shape below:
{
"mcpServers": {
"tou": {
"url": "https://tou.tools/mcp",
"headers": { "Authorization": "Bearer tou_…" }
}
}
}
Where this config lives differs by client — check your client's
MCP docs for the right file or UI (e.g.
~/.cursor/mcp.json for Cursor,
claude_desktop_config.json for Claude Desktop, the
in-app Settings panel for Windsurf, an
.mcp.json in your project root for Claude Code).
The block above is portable across all of them.
Option B — OAuth 2.1 (browser-hosted clients)
Browser-hosted clients like Claude Web and ChatGPT only accept MCP servers behind OAuth 2.1 with discovery. Add tou.tools as a custom connector and the client handles the full flow — discovery, dynamic client registration, PKCE, the consent screen, token refresh. You just point and sign in.
- In your client (Claude Web, ChatGPT, etc.), open the connector / custom integration settings and choose Add custom connector (or the equivalent).
- Server URL:
https://tou.tools/mcp - The client redirects you to tou.tools to sign in (use your dashboard email + password). You'll see a consent screen showing the three tools — click Allow.
- Done. The three tools appear in your client's tool list and bill against your account just like REST calls do.
Access tokens are 1-hour JWTs with audience bound to
https://tou.tools/mcp; refresh tokens are 30 days and
rotate on every use. The discovery documents live at
/.well-known/oauth-protected-resource and
/.well-known/oauth-authorization-server.
Ready to build? Get a free API key or review plans & limits.