# TexasElectricity.org comparison API v2
HTML guide: https://texaselectricity.org/developers
POST https://texaselectricity.org/api/v2/compare
Content-Type: application/json
No account or API key. Server-to-server access; POST responses are not cached.
## Request
{"zip":"75001","monthly_usage_kwh":{"january":800,"july":1600,"august":1700}}
zip is a five-digit string. monthly_usage_kwh has one to twelve full lowercase
month names and non-negative whole-kWh numbers. Omit unavailable months. Zero
is real usage, not missing data. Null, fractions, negative values, unknown fields,
unknown month names and empty usage are rejected. Maximum body: 4096 UTF-8 bytes.
Optional limit: integer 1–100, default 5. Set 100 for a broader comparison.
coverage.returned_plans and has_more describe truncation; renewable_pct is the
recorded percentage (0–100), or null when unavailable. Null is not zero.
Optional territory: Oncor, CenterPoint, AEP Central, AEP North, TNMP, or LPL.
When provided, it must match the ZIP lookup. Never infer a territory from a ZIP
that has several matches: ask which delivery company appears on the bill.
## Results
Every JSON response has schema_version "2.1".
- estimates_available: eligible plans ranked by unrounded annual cost (five by default).
- no_eligible_plans: the ZIP resolves but there are no eligible plans.
- territory_required: no prices; use territory_options to ask the customer and
resend the same ZIP and usage with territory set to their choice.
- zip_not_supported: no prices; check the ZIP/provider. This means no match in
our lookup, not proof that a particular address lacks retail choice.
All four outcomes use HTTP 200. HTTP 400 rejects invalid input or a territory
that does not match the ZIP; 413 rejects oversized bodies; 415 requires JSON;
503 means data is unavailable or incomplete. Retry 503 later. GET on the compare
endpoint returns 405; use POST. A tool unable to POST is not an API outage.
## Usage and scope
The same website estimator preserves supplied months and fills omitted months
using Texas-wide seasonal factors, not ZIP-specific or North Texas factors.
usage.supplied_months and estimated_months identify which is which; each monthly
bill also has usage_estimated. At least one actual
month is required. More supplied months can improve the estimate.
All six supported territories; residential fixed-rate plans with exactly 12-month
terms. No whole-market or per-address eligibility guarantee. coverage reports
the catalog size and exclusions before the requested results are selected.
website_unfiltered_plans = website_featured_plans + website_other_plans; the
website shows its top three separately from the other-plans section. Do not
compare that other-plans count alone with active_territory_plans_read.
Annual costs use the supplied/estimated January-December profile, not a contract
starting on a particular date. Current delivery charges are held constant for
all months. Round prices to cents only for display. The annual cost uses unrounded
months, so displayed monthly amounts can sum to a few cents different.
## Pricing and freshness
We refresh our plan catalog daily using PowerToChoose. Unchanged listings retain
previously validated pricing. Lead with freshness.summary for returned listings;
refresh_status_explanation separates overall run status from plan checks.
Use each plan's freshness.summary and listing_check:
within_24_hours, older_than_24_hours, or unknown. The check is backed by an exact
persisted plan/run link and completed run timestamps captured at build time.
A different stage failing does not erase a plan's completed listing check.
run_evidence.run_status describes the entire run, not each returned plan.
Times use the run start as a conservative proxy, not the exact per-plan fetch.
Unknown means the API lacks matching check evidence; do not call a plan stale
solely on that basis. pricing_validated_at is separate from listing freshness.
An older pricing-validation date alone does not mean the offer is outdated.
current_efl_fetch_status is not_established_by_this_api: pricing passed validation,
but the API does not establish a new EFL fetch at request time. generated_at is
response time, not a source-check time. Confirm current provider terms to enroll.
Costs include modeled energy, provider base fees, delivery charges, minimum-usage
fees and setup fees spread over twelve months. Conditional credits assume their
requirements are met. Inspect credit_windows, bill_credit_thresholds,
cliff_warnings and special_terms. Missing terms do not establish no conditions.
Excluded: claim-required rebates, cancellation charges, deposits, separately
assessed taxes and unmodeled ancillary fees. Future delivery changes, eligibility
and provider billing/rounding remain unknown. A zero provider base fee does not
mean there is no fixed delivery charge. Compare cancellation fees at the same
number of months remaining: $20/month remaining exceeds $150 at eight or more
months. Equal estimated costs do not establish identical contract terms. No enrollment or provider contact occurs.
## Privacy and compatibility
Send only ZIP, usage, optional territory and limit; no name, address, account number or
billing document. Request bodies are not logged or persisted by the application;
hosting/network infrastructure may retain ordinary metadata. Server-side GA
counts API requests by version, outcome, territory, result count and response
time. No ZIP, usage figures, caller IP or caller identifier is sent by API
analytics; no browser cookies are used. Returned plan URLs
contain usage figures. There is no browser CORS contract or availability SLA.
Version 1 remains unchanged at /api/v1/compare for Oncor with twelve supplied months.