AtlasDirectoryAPI
Search atlas
LLocal atlas
Election Atlas API
Canonical election records, live results detail, and import workflows.
Reference
OverviewAuthenticationUse in another projectHeaders and cachingList electionsLive resultsCanonical modelImports and freshness
Endpoints
/api/health/api/elections/api/elections/today/api/elections/upcoming/api/elections/[id]/api/elections/[id]/results/api/elections/[id]/map/api/admin/import
Snapshot auto-refresh
Election Atlas API

API Reference

Election Atlas serves one canonical election model for discovery, filtering, and detail pages, with live extensions for race results and geography when detailed reporting is available.

The important split is simple: use the canonical routes for stable records, and call the live results or map routes only when you want current reporting.

Every public read route is also available under /api/v1 for consumers that want an explicit versioned base path.

Examples below reflect the current API shape as served by the running app, trimmed to the fields most useful to consumers of Election Atlas.

/api basecanonical json1788 onwardcors-ready readslive results detail

Authentication

The public read routes are open by default and are safe to call from other applications. They return CORS headers and support OPTIONS preflight requests.

The write route POST /api/admin/import is not part of the public integration surface. It is environment-gated in production and should be treated as an internal admin endpoint.

Imports also require a reachable write database. If the app is running in read-only snapshot mode, the route returns a structured error instead of crashing.

Use in another project

Point your app at the deployed Election Atlas base URL and call the read routes directly. The same JSON surface works for server-rendered apps, background jobs, and browser clients.

Use list routes to discover atlas ids first, then request /[id], /[id]/results, or /[id]/map when you need a fuller surface.

External project examplejavascript
const baseUrl = "https://your-domain.com/api";
const response = await fetch(
`${baseUrl}/elections?year=2020&date=2020-11-03&type=presidential`
);
if (!response.ok) {
throw new Error("Election Atlas request failed");
}
const payload = await response.json();
console.log(payload.data);
Health examplejavascript
fetch("https://your-domain.com/api/health")
.then((response) => response.json())
.then((payload) => {
console.log(payload.ok);
console.log(payload.snapshot);
});

Headers and caching

Public read routes return Access-Control-Allow-Origin, X-Request-Id, and X-Election-Atlas-Version headers.

Canonical list and detail routes use a longer shared-cache window, while live results and map routes use shorter cache windows so reporting updates can move through faster.

GET /api/elections supports limit and offset. The response includes a meta.pagination block so consumers can page deterministically.

Start with the list route

Use GET /api/elections to query the canonical ledger. This is the same read surface used by the homepage, calendar, and race directory.

Hold onto the returned atlas ids, then use them with the detail, results, or map endpoints.

Request examplejavascript
fetch("/api/elections?year=2020&date=2020-11-03&type=presidential")
.then((response) => response.json())
.then((payload) => {
console.log(payload.data);
console.log(payload.meta);
});
Response examplejson
{
"data": [
{
"id": "2020-11-03-29107",
"slug": "2020-11-03-29107",
"title": "Arizona US President",
"date": "2020-11-03",
"endDate": null,
"timezone": null,
"countryCode": "US",
"countryName": "United States",
"region": "AZ",
"locality": null,
"jurisdictionName": "AZ",
"electionType": "presidential",
"status": "unofficial_results",
"description": "Arizona US President in AZ. Current reporting shows 100% reporting, 3 listed candidates.",
"lastSyncedAt": "2026-03-09T01:49:56.880Z",
"liveMapAvailable": true
}
],
"meta": {
"total": 5,
"filters": {
"year": "2020",
"date": "2020-11-03",
"type": "presidential"
},
"countsByStatus": {
"unofficial_results": 5
},
"countsByType": {
"presidential": 5
},
"pagination": {
"limit": 50,
"offset": 0,
"returned": 5
}
}
}
GET

/api/health

Return a lightweight health payload for the public API surface.

Useful for uptime checks, deployment verification, and snapshot visibility.

GET

/api/elections

List canonical Election Atlas records from the normalized read model.

limit
Page size. Defaults to 50 and is capped at 250.
offset
Starting index for deterministic paging. Defaults to 0.
year
Historical year such as 1788, 2020, or 2026.
month
Month key in YYYY-MM format.
date
Exact day in YYYY-MM-DD format.
from / to
Open date window in YYYY-MM-DD format.
country
Two-letter uppercase country code such as US, CH, or PT.
type
Election type enum including presidential, referendum, primary, runoff, special, legislative, mayoral, regional, and more.
status
scheduled, polls_open, unofficial_results, or certified.
search
Case-insensitive search across title, jurisdiction, country, region, and locality.
GET

/api/elections/today

Return elections happening on the server's current local date.

country
Optional country filter.
type
Optional election type filter.
status
Optional status filter.
search
Optional title or jurisdiction search.
GET

/api/elections/upcoming

Return upcoming normalized records from today onward.

limit
Positive integer. Defaults to 8.
country
Optional country filter.
type
Optional election type filter.
status
Optional status filter.
search
Optional title or jurisdiction search.
GET

/api/elections/[id]

Fetch one canonical election record by Election Atlas id.

id
Atlas id returned by Election Atlas list endpoints.

Atlas ids are stable record identifiers and currently mirror the canonical slug.

Detail requestjavascript
fetch("/api/elections/2020-11-03-29107")
.then((response) => response.json())
.then((payload) => {
console.log(payload.title);
console.log(payload.jurisdiction);
});
Detail responsejson
{
"id": "2020-11-03-29107",
"slug": "2020-11-03-29107",
"title": "Arizona US President",
"date": "2020-11-03",
"endDate": null,
"timezone": null,
"countryCode": "US",
"countryName": "United States",
"region": "AZ",
"locality": null,
"jurisdictionName": "AZ",
"electionType": "presidential",
"status": "unofficial_results",
"description": "Arizona US President in AZ. Current reporting shows 100% reporting, 3 listed candidates.",
"lastSyncedAt": "2026-03-09T23:35:52.645Z",
"liveMapAvailable": true,
"createdAt": "2026-03-09T23:35:52.645Z",
"updatedAt": "2026-03-09T23:35:52.645Z",
"jurisdiction": {
"id": "jurisdiction:us-az-az",
"slug": "us-az-az",
"displayPath": "AZ, AZ, United States"
}
}

Live results and map detail

The live routes sit beside the canonical model. They are the reporting edge of Election Atlas, not the stable core record, and they can return 404 when live reporting or a renderable map is unavailable.

Live results requestjavascript
fetch("/api/elections/2020-11-03-29107/results")
.then((response) => response.json())
.then((payload) => {
console.log(payload.percentReporting);
console.log(payload.candidates);
console.log(payload.regions);
});
Live results responsejson
{
"mapAvailable": true,
"mapName": "Arizona",
"lastUpdated": "2026-03-08T21:54:00.000Z",
"percentReporting": 99.3,
"candidates": [
{
"name": "Joe Biden",
"party": "Democratic",
"votes": 1672143,
"percent": 49.36,
"winner": true,
"color": "#4874a3"
},
{
"name": "Donald J. Trump",
"party": "Republican",
"votes": 1661686,
"percent": 49.06,
"winner": false,
"color": "#c6606b"
}
],
"regions": [
{
"key": "maricopa",
"name": "Maricopa",
"type": "county",
"fill": "#4D91FF",
"percentReporting": 100,
"totalVotes": 2060523,
"candidates": []
}
]
}
Map requestjavascript
fetch("/api/elections/2020-11-03-29107/map")
.then((response) => response.json())
.then((payload) => {
console.log(payload.topCandidates);
console.log(payload.mapSvg);
});
Map responsejson
{
"mapSvg": "<svg ... />",
"topCandidates": [
{
"name": "Joe Biden",
"party": "Democratic",
"votes": 1672143,
"percent": 49.36,
"winner": true,
"color": "#4874a3"
},
{
"name": "Donald J. Trump",
"party": "Republican",
"votes": 1661686,
"percent": 49.06,
"winner": false,
"color": "#c6606b"
}
]
}
GET

/api/elections/[id]/results

Fetch live results for one race when reporting exists.

id
Atlas id returned by Election Atlas list endpoints.

Returns 404 when the selected race does not expose live reporting.

GET

/api/elections/[id]/map

Fetch a compact rendered map preview plus the top two candidates.

id
Atlas id returned by Election Atlas list endpoints.

Returns 404 when the selected race does not expose a renderable map.

Canonical model

Every imported record is normalized into the Election Atlas schema before it reaches the main read routes. The public surface stays narrow on purpose so filters, ids, and route contracts remain stable over time.

idslugtitledateendDatetimezonecountryCodecountryNameregionlocalityjurisdictionNameelectionTypestatusdescriptionlastSyncedAtliveMapAvailable

Imports and freshness

The canonical read model is imported through adapters, normalized once, and then served through a snapshot-backed layer for local stability.

Read requests can trigger background snapshot refresh when the current snapshot is stale. Imports also refresh the served snapshot after ingest, so the directory and the API stay aligned.

Live results and map routes fetch reporting detail on demand. They stay separate because reporting can change faster than the normalized record itself.

The import body must use a real adapter key accepted by the route.

Import requestjavascript
fetch("/api/admin/import", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ source: "local-static" })
});
POST

/api/admin/import

Trigger an adapter import run and return the import summary.

source
Adapter key for a configured import source.

Internal admin route. Environment-gated in production and requires a reachable write database connection.

Reference
OverviewAuthenticationList electionsLive resultsCanonical modelFreshness
Notes

Atlas ids come from list responses and can be reused across detail, results, and map routes.

Results and map routes are live extensions and can return 404.

Snapshot-backed reads keep the local API stable while live detail stays fresh separately.