Akashic
1892–2024
akashic / documentation
Akashic Docs

Developers and API

Build on Akashic using the versioned manifest, stable JSON records, long-tail place responses, oEmbed, iframe widgets, and the read-only MCP server.

Akashic exposes public read interfaces over the same records used by the product. Begin with the versioned manifest, follow the returned data-family contracts, and cache records for repeated analysis.

Public manifest

GET https://akashic.app/api/v1
Accept: application/json

The manifest is the discovery endpoint for public data families, examples, licensing, and corpus metadata. Consumers should read it instead of relying on undocumented directory guesses.

Static place records

GET https://akashic.app/data/per_{tier}/{id}.json

Examples:

curl https://akashic.app/data/per_county/10003.json
curl https://akashic.app/data/per_state/IL.json

Common tiers include county, state, cd, sld_upper, sld_lower, cbsa, csa, city, dma, region, division, nation, and amnat. Use the manifest for the current list and examples.

Long-tail places

Census places outside the promoted static tier can be served on demand:

curl -H "Accept: application/json" https://akashic.app/place/{geo_id}

The response and availability depend on the current Place Worker contract. Cache successful responses and retain provenance metadata.

Elections and people

# Election family index and example contest
curl https://akashic.app/data/per_election/index.json
curl https://akashic.app/data/per_election/president-2024.json

# Person and officeholder material
curl https://akashic.app/data/per_person/_index.json
curl https://akashic.app/data/officeholder_ideology.json

The election index is the authoritative inventory for supported offices, cycles, and contest identifiers.

oEmbed

Platforms that support oEmbed can request embed metadata for an Akashic page:

GET https://akashic.app/api/oembed?url=https%3A%2F%2Fakashic.app%2Fcounty%2F17031%2F

Validate the returned type, dimensions, title, provider, and HTML before insertion.

Iframe widgets

<iframe
  src="https://akashic.app/embed/county/17031/stat-card/"
  width="100%"
  height="200"
  style="border:0;border-radius:12px;max-width:560px"
  title="Cook County — Akashic"
  loading="lazy"
></iframe>

Use the embed builder for supported widget kinds and sizes. Attribution is part of the widget contract.

Authentication

Public read endpoints and static data records do not require an API key. User-specific, saved-Place, billing, admin, and other protected actions use the authentication contract of the corresponding product surface and are not part of this public read API.

Do not send Clerk, Stripe, admin, or other private credentials to a public data URL.

CORS and caching

Public JSON interfaces are designed for cross-origin reading. Respect response cache headers, send a descriptive user agent for large automated jobs, and prefer bulk archives over per-record crawling.

Static records are release artifacts and can be cached aggressively. Dynamic or user-specific routes may have different cache and abuse-control behavior.

Rate limits and fair use

There is no published fixed request-per-minute quota for static public records. That is not permission to create avoidable load.

  • Cache records locally.
  • Use bulk archives for corpus-scale analysis.
  • Avoid high-concurrency enumeration of dynamic routes.
  • Back off on 429, 503, or other transient responses.
  • Identify automated clients where the protocol permits.

Schema stability

Public URLs are intended to remain stable and schemas additive. Consumers must:

  • tolerate unknown fields;
  • distinguish null from absent and zero;
  • preserve estimate and provenance flags;
  • use the versioned manifest and retrieval date;
  • avoid assuming every geographic tier has every module.

JavaScript example

const manifestResponse = await fetch('https://akashic.app/api/v1');
if (!manifestResponse.ok) {
  throw new Error(`Manifest request failed: ${manifestResponse.status}`);
}

const manifest = await manifestResponse.json();
const exampleUrl = manifest.data_families?.[0]?.example;
if (!exampleUrl) {
  throw new Error('Manifest did not provide an example record URL');
}

const placeResponse = await fetch(exampleUrl);
if (!placeResponse.ok) {
  throw new Error(`Place request failed: ${placeResponse.status}`);
}

const place = await placeResponse.json();
console.log(place.name, place.elections?.at?.(-1));

MCP server

The repository includes a read-only Model Context Protocol server for place lookup, election history, demographic rollups, and crosswalk-oriented tools with source provenance. Repository users run it with the documented MCP command and configuration. Public remote availability, if offered, must be verified from the current manifest or product documentation before use.

Errors

  • 400 — invalid parameter or unsupported request shape.
  • 401 / 403 — protected surface or insufficient authorization.
  • 404 — unknown route, data family, or identifier.
  • 429 — rate or abuse control; back off.
  • 5xx — transient service or build problem; retry with bounded exponential backoff.

Static hosts may return a generic error body. Do not assume every error is JSON.