import type { Metadata } from 'next'; import Link from 'next/link'; import { BreadcrumbLd } from '@/components/meta/breadcrumb-ld'; import { DataTable, Td, Th } from '@/components/ui/data-table'; import { Container, Note, PageHeader, Section } from '@/components/ui/section'; import { Unavailable } from '@/components/ui/unavailable'; import { api, safe } from '@/lib/api'; import { PUBLIC_API_BASE, routes, SITE_NAME } from '@/lib/site'; import type { Stats } from '@/lib/types'; import { RequestBuilder } from './request-builder'; import { ROUTES } from './routes'; export const metadata: Metadata = { title: 'Developers — public API 1.1, request builder, provenance and history recipes', description: 'The AI Atlas public API: JSON over HTTPS, no key for public routes, OpenAPI docs, an interactive request builder with curl / Python / JavaScript snippets, and recipes for pagination, provenance and historical queries.', alternates: { canonical: '/developers' } }; export const revalidate = 3600; /** Compact, honest projection of /stats for the example block (the real payload has more counters). */ function statsExample(s: Stats) { return { entities: s.entities, entities_total: s.entities_total, organizations_total: s.organizations_total, change_events_live_24h: s.change_events_live_24h, claims_current: s.claims_current, prices_current: s.prices_current, last_event_at: s.last_event_at, definitions: s.definitions ? `${Object.keys(s.definitions).length} counter definitions` : undefined, computed_at: s.computed_at, '…': `${Object.keys(s).length} keys in total` }; } function Code({ children }: { children: string }) { return (
      {children}
    
); } export default async function DevelopersPage() { const [health, stats, top] = await Promise.all([safe(api.health()), safe(api.stats()), safe(api.models({ limit: 1, sort: 'quality' }))]); const exA = top?.items[0]?.slug ?? ''; const today = new Date().toISOString().slice(0, 10); const weekAgo = new Date(Date.now() - 7 * 86400000).toISOString().slice(0, 10); return ( API {health.status} · v{health.version}{(health as { api_version?: string }).api_version ? ` · contract ${(health as { api_version?: string }).api_version}` : ''}

:

API status unavailable

} />
{PUBLIC_API_BASE}} hairline={false}>

Interactive OpenAPI documentation: /api/v1/docs. JSON, UTF-8, ISO-8601 UTC timestamps. Errors are {'{ "detail": "…" }'} with 400 / 404 / 409 / 429 / 501 / 503 (422 with errors for parameters rejected by validation). Every response carries x-api-version: 1.1.

Cursor pagination on feeds

{`# /changes and /entities/{slug}/timeline return next_before; pass it back as before= curl -s "${PUBLIC_API_BASE}/changes?importance_min=2&limit=50" | jq '{next_before, n: (.items|length)}' curl -s "${PUBLIC_API_BASE}/changes?importance_min=2&limit=50&before=" # Listings use limit/offset (≤ 200; /sitemap ≤ 5 000). 1.1 feeds are keyed on occurred_at and exclude backfill: curl -s "${PUBLIC_API_BASE}/changes?include_backfill=1&date_field=observed&limit=20" # v1 behaviour`}

Field-level provenance

{`# Every displayed value has a claim behind it curl -s "${PUBLIC_API_BASE}/entities/${exA}/provenance/context_length" | jq '{value, source: .source.name, tier, extractor, observed_at, valid_since, claim_id, conflicts: (.conflicts|length)}' # One claim and its lifecycle (previous, superseding, conflicting) curl -s "${PUBLIC_API_BASE}/claims/" | jq '{property, claim: .claim.value, chain: (.chain | map_values(if type=="array" then length else . end))}' # All current claims of an entity curl -s "${PUBLIC_API_BASE}/entities/${exA}/claims?status=current&limit=50" | jq '.items[] | {property, value, tier, source_name}'`}

Historical queries

{`# The atlas as of a date (reconstructed before the observation history) curl -s "${PUBLIC_API_BASE}/time-machine?date=2025-06-01&scope=models&limit=20" | jq '{reconstructed, first_entity_at, total: .models.total}' # One entity as of a date, and the full claim history of a property curl -s "${PUBLIC_API_BASE}/entities/${exA}/asof?date=${weekAgo}" | jq '{existed, attributes}' curl -s "${PUBLIC_API_BASE}/entities/${exA}/history?property=context_length" | jq '.items[] | {value, status, valid_from, valid_to}' # What changed between two dates (scope: all | models | org: | family:) curl -s "${PUBLIC_API_BASE}/diff?a=${weekAgo}&b=${today}&scope=models" | jq '.counts' # Today in AI 2.0 — grouped sections, backfill excluded curl -s "${PUBLIC_API_BASE}/changes/daily?date=${today}" | jq '{total, backfill_excluded, sections: [.today[] | {key, total}]}'`}

Graph

{`# Typed neighbourhood explorer — never more than limit nodes, truncated says when the API cut curl -s "${PUBLIC_API_BASE}/graph/explore?node=anthropic&mode=company&depth=1&limit=150" | jq '{truncated, counts, predicates}'`}
{stats ? {JSON.stringify(statsExample(stats), null, 2)} : }
Route Group Returns {ROUTES.map((r) => ( {r.method} {r.path} {r.params.length > 0 && {r.params.map((p) => p.name).join(' · ')}} {r.group} {r.returns} ))} Shapes are documented in the OpenAPI schema and in docs/API.md. Admin routes (/admin/*) require the x-aia-admin-token header and are not public. Also: /benchmarks/{'{slug}'}/frontier, /pareto, /cost/context, /hardware/{'{slug}'}/fit, /licenses/{'{key}'}, /compare, /explore/types, /explore/{'{type}'}, POST /views.
  • Numeric aggregates may arrive as string (Postgres decimals): treat every numeric field as number | string | null and coerce.
  • null means the sources did not state it. Never read a missing field as zero, and never average two conflicting claims — the API returns both, flagged.
  • Events: occurred_at = coalesce(effective_at, observed_at); is_backfill marks imported history; group_key folds one release seen in several documents.
  • Universe: /models lists canonical model releases; artifacts and folded variants are excluded unless include=artifacts. Old slugs keep resolving (redirected_from).
  • Benchmarks: results live in comparability groups (metric × config_key); leaderboards are one row per canonical model with a trust_level.
  • Anything marked estimated: true (hardware fit, memory-derived parameter bounds) is derived by a stated formula — the assumptions array is part of the response.
  • Caching: weak ETag + Cache-Control: public, max-age=60, stale-while-revalidate=300; send If-None-Match for a 304.
  • Public routes are free to use without a key. Please cache responses and identify your client with a User-Agent that includes a contact address.
  • Search is rate-limited per IP (HTTP 429 when exceeded). Higher limits and developer keys (x-api-key) are available on request — see contact.
  • Attribution: “Data: AI Atlas (www.ai-atlas.co)” with a link. Every record carries its own upstream sources; please keep them when you redistribute.
  • Agent-readable access (MCP server, bulk exports) is planned, not available yet — the routes above are the only supported surface today.
); }