API Documentation
Free, public API for U.S. electoral data. CORS enabled for all origins. No authentication required.
Versioning & stability
This API is v1. Breaking changes (removed fields, renamed parameters, changed response shapes) are announced on this page at least 30 days before they ship. Additive changes — new endpoints, new optional parameters, new response fields — may ship at any time without notice, so parse responses tolerantly. A machine-readable OpenAPI 3.1 spec describes the full public surface.
Rate limits: limits are currently best-effort, per-server-instance approximations (counters are not shared across serverless instances), so the effective ceiling you observe may be higher than documented. Treat documented limits as the contract: general endpoints ~100 req/min, bulk poll export 30 req/min per IP. A 429 means back off.
/api/elections/:yearAll state results for a presidential election year
Parameters
yearpathElection year (1976-2024, divisible by 4)Example
GET /api/elections/2024Response
{ year, national: { winner, ev, popular_vote, ... }, states: [...] }/api/elections/:year/:stateDetailed results for one state in one election
Parameters
yearpathElection yearstatepathState abbreviation (e.g., PA, CA)Example
GET /api/elections/2024/PAResponse
{ year, state: { abbr, name, ev_count, margin, candidates, ... } }/api/states/:abbr/historyAll presidential results for a state since 1976
Parameters
abbrpathState abbreviationExample
GET /api/states/PA/historyResponse
{ abbr, name, region, ev_history, elections: [...] }/api/pollsRecent polls with optional filtering
Parameters
statequeryFilter by state abbreviation or "national"daysqueryOnly polls from last N daysmin_ratingqueryMinimum pollster rating (A+ through C-)pollsterqueryFilter by pollster name (partial match)Example
GET /api/polls?state=PA&days=30Response
{ polls: [...], meta: { count, filters } }/api/polls/averageComputed polling average with adjustable parameters
Parameters
statequeryState abbreviation or "national"half_lifequeryRecency half-life in days (default: 14)min_ratingqueryMinimum pollster rating (default: C-)Example
GET /api/polls/average?state=PA&half_life=21Response
{ average, methodology, polls_included: [...] }/api/ratings/:yearThird-party race ratings for the 2026 cycle (Cook, Sabato, Inside Elections). Values are null until verified by automated sourcing.
Parameters
yearpathElection cycle year (2026)Example
GET /api/ratings/2026Response
{ year, source_status, last_verified, raters, ratings: [{ raceId, abbr, state, office, cook, sabato, inside_elections }] }/api/swingSwing analysis between two election years
Parameters
from_yearqueryStarting year (default: 2020)to_yearqueryEnding year (default: 2024)Example
GET /api/swing?from_year=2016&to_year=2020Response
{ from_year, to_year, national_swing, states: [...] }/api/exportBulk export as CSV or JSON
Parameters
yearqueryElection year (required)statequeryFilter to one stateformatquery"json" (default) or "csv"Example
GET /api/export?year=2024&format=csvResponse
CSV file download or JSON array/api/export/pollsBulk export of ALL 2026 race polls. JSON returns full poll objects with per-candidate results keyed by candidate slug; CSV is tidy format (one row per poll-candidate pair). Cached at the edge for 1 hour; rate limited to 30 req/min per IP.
Parameters
formatquery"json" (default) or "csv"racequeryFilter to one race id, e.g. ca-gov-2026Example
GET /api/export/polls?race=ca-gov-2026&format=csvResponse
{ meta: { count, races, race, generatedAt, license }, polls: [{ raceId, date, pollster, grade, sampleSize, population, marginOfError, partisan, results: { candidateSlug: pct }, sourceUrl }] }Quick start: bulk polls export
All 2026 polls as JSON
curl -s "https://decisionlabs.ai/api/export/polls"One race as CSV (saved to file)
curl -sL -o ca-gov-2026-polls.csv "https://decisionlabs.ai/api/export/polls?race=ca-gov-2026&format=csv"Embeds
Every 2026 race has a compact, server-rendered widget at /embed/race/{raceId} showing the race title, our rating, the current polling average per candidate, and a freshness date. It is designed for a ~360–480px column, follows the visitor's light/dark preference, and sets no cookies and runs no trackers.
Copy-paste snippet
<iframe
src="https://decisionlabs.ai/embed/race/ca-gov-2026"
width="400"
height="320"
loading="lazy"
title="California Governor 2026 — Decision Labs polling average"
></iframe>Valid race ids: any raceId listed in meta.races of the bulk export endpoint, GET /api/export/polls (e.g. ca-gov-2026, nc-sen-2026). Unknown ids return 404. Races without public polling render the rating with an honest “no public polling yet” note.
Attribution: embeds display the data license's required attribution (see below — CC BY 4.0, credited to “Decision Labs (decisionlabs.ai)”) in a built-in footer linking to the race page and methodology. Please don't crop or obscure that footer.
Data license
Poll and election data is aggregated from public sources (pollster releases, MEDSL, and other freely licensed datasets). The Decision Labs compilation — including computed averages, grades mapping, and export formats — is released under CC BY 4.0: use it freely with attribution to “Decision Labs (decisionlabs.ai)”. The platform codebase is MIT-licensed. Underlying source data remains subject to its original licenses.
Changelog
2026-06-10
- Added GET /api/export/polls — bulk export of all 2026 race polls (JSON + CSV, optional ?race= filter).
- Published machine-readable OpenAPI 3.1 spec at /openapi.json.
- Documented v1 versioning/stability policy and data licensing on this page.
- Added embeddable race widgets at /embed/race/{raceId} — copy-paste iframe snippet documented in the Embeds section below.