HTML 77.2%
TypeScript 10.5%
Python 9.6%
JavaScript 2.5%
1import type { Metadata } from 'next';2import Link from 'next/link';3import { BreadcrumbLd } from '@/components/meta/breadcrumb-ld';4import { DataTable, Td, Th } from '@/components/ui/data-table';5import { Container, Note, PageHeader, Section } from '@/components/ui/section';6import { Unavailable } from '@/components/ui/unavailable';7import { api, safe } from '@/lib/api';8import { PUBLIC_API_BASE, routes, SITE_NAME } from '@/lib/site';9import type { Stats } from '@/lib/types';10import { RequestBuilder } from './request-builder';11import { ROUTES } from './routes';1213export 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' } };14export const revalidate = 3600;1516/** Compact, honest projection of /stats for the example block (the real payload has more counters). */17function statsExample(s: Stats) {18 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` };19}20function Code({ children }: { children: string }) {21 return (22 <pre className="scrollbar-thin mt-3 overflow-x-auto border border-rule bg-surface p-4 text-[12.5px] leading-relaxed text-ink">23 <code>{children}</code>24 </pre>25 );26}2728export default async function DevelopersPage() {29 const [health, stats, top] = await Promise.all([safe(api.health()), safe(api.stats()), safe(api.models({ limit: 1, sort: 'quality' }))]);30 const exA = top?.items[0]?.slug ?? '<model-slug>';31 const today = new Date().toISOString().slice(0, 10);32 const weekAgo = new Date(Date.now() - 7 * 86400000).toISOString().slice(0, 10);3334 return (35 <Container wide>36 <BreadcrumbLd items={[{ name: SITE_NAME, href: '/' }, { name: 'Developers', href: '/developers' }]} />37 <PageHeader eyebrow="Developers" title="Public API 1.1" lede="Everything on this site comes from the same JSON API. Public routes need no key; responses carry a weak ETag and are cached for 1–10 minutes; search is rate-limited per IP. 1.1 is additive over v1 — no route, field or type was removed." aside={health ? <p className="text-sm text-ink-3">API <span className={health.status === 'ok' ? 'text-positive' : 'text-warning'}>{health.status}</span> · v{health.version}{(health as { api_version?: string }).api_version ? ` · contract ${(health as { api_version?: string }).api_version}` : ''}</p> : <p className="text-sm text-ink-3">API status unavailable</p>} />3839 <Section eyebrow="Base URL" title={<span className="mono text-lg md:text-xl">{PUBLIC_API_BASE}</span>} hairline={false}>40 <p className="text-sm text-ink-2">41 Interactive OpenAPI documentation: <a href="/api/v1/docs" className="link mono">/api/v1/docs</a>. JSON, UTF-8, ISO-8601 UTC timestamps. Errors are <span className="mono">{'{ "detail": "…" }'}</span> with 400 / 404 / 409 / 429 / 501 / 503 (422 with <span className="mono">errors</span> for parameters rejected by validation). Every response carries <span className="mono">x-api-version: 1.1</span>.42 </p>43 </Section>4445 <Section id="builder" eyebrow="Request builder" title="Build a request, try it, copy the snippet" lede="Routes and parameters come from the 1.1 contract; “Try” calls the API from your browser through this site's same-origin proxy.">46 <RequestBuilder />47 </Section>4849 <Section id="recipes" eyebrow="Recipes" title="Pagination, provenance, history">50 <div className="grid gap-8 lg:grid-cols-2">51 <div className="min-w-0">52 <p className="text-sm font-medium text-ink">Cursor pagination on feeds</p>53 <Code>{`# /changes and /entities/{slug}/timeline return next_before; pass it back as before=54curl -s "${PUBLIC_API_BASE}/changes?importance_min=2&limit=50" | jq '{next_before, n: (.items|length)}'55curl -s "${PUBLIC_API_BASE}/changes?importance_min=2&limit=50&before=<next_before>"5657# Listings use limit/offset (≤ 200; /sitemap ≤ 5 000). 1.1 feeds are keyed on occurred_at and exclude backfill:58curl -s "${PUBLIC_API_BASE}/changes?include_backfill=1&date_field=observed&limit=20" # v1 behaviour`}</Code>59 <p className="mt-6 text-sm font-medium text-ink">Field-level provenance</p>60 <Code>{`# Every displayed value has a claim behind it61curl -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)}'6263# One claim and its lifecycle (previous, superseding, conflicting)64curl -s "${PUBLIC_API_BASE}/claims/<claim_id>" | jq '{property, claim: .claim.value, chain: (.chain | map_values(if type=="array" then length else . end))}'6566# All current claims of an entity67curl -s "${PUBLIC_API_BASE}/entities/${exA}/claims?status=current&limit=50" | jq '.items[] | {property, value, tier, source_name}'`}</Code>68 </div>69 <div className="min-w-0">70 <p className="text-sm font-medium text-ink">Historical queries</p>71 <Code>{`# The atlas as of a date (reconstructed before the observation history)72curl -s "${PUBLIC_API_BASE}/time-machine?date=2025-06-01&scope=models&limit=20" | jq '{reconstructed, first_entity_at, total: .models.total}'7374# One entity as of a date, and the full claim history of a property75curl -s "${PUBLIC_API_BASE}/entities/${exA}/asof?date=${weekAgo}" | jq '{existed, attributes}'76curl -s "${PUBLIC_API_BASE}/entities/${exA}/history?property=context_length" | jq '.items[] | {value, status, valid_from, valid_to}'7778# What changed between two dates (scope: all | models | org:<slug> | family:<slug>)79curl -s "${PUBLIC_API_BASE}/diff?a=${weekAgo}&b=${today}&scope=models" | jq '.counts'8081# Today in AI 2.0 — grouped sections, backfill excluded82curl -s "${PUBLIC_API_BASE}/changes/daily?date=${today}" | jq '{total, backfill_excluded, sections: [.today[] | {key, total}]}'`}</Code>83 <p className="mt-6 text-sm font-medium text-ink">Graph</p>84 <Code>{`# Typed neighbourhood explorer — never more than limit nodes, truncated says when the API cut85curl -s "${PUBLIC_API_BASE}/graph/explore?node=anthropic&mode=company&depth=1&limit=150" | jq '{truncated, counts, predicates}'`}</Code>86 </div>87 </div>88 </Section>8990 <Section eyebrow="Live example" title="GET /stats" lede="Fetched from the API when this page was rendered (revalidated hourly) and trimmed for display — not sample data.">91 {stats ? <Code>{JSON.stringify(statsExample(stats), null, 2)}</Code> : <Unavailable what="Live /stats example" className="mt-3" />}92 </Section>9394 <Section eyebrow="Endpoints" title="Public routes (1.1)">95 <DataTable caption="Public endpoints" compact>96 <thead>97 <tr>98 <Th>Route</Th>99 <Th>Group</Th>100 <Th>Returns</Th>101 </tr>102 </thead>103 <tbody>104 {ROUTES.map((r) => (105 <tr key={r.id}>106 <Td primary className="mono text-[12.5px] break-words">107 {r.method} {r.path}108 {r.params.length > 0 && <span className="block text-[11px] text-ink-3">{r.params.map((p) => p.name).join(' · ')}</span>}109 </Td>110 <Td label="Group" className="text-xs text-ink-2">{r.group}</Td>111 <Td label="Returns" wide className="text-ink-2">{r.returns}</Td>112 </tr>113 ))}114 </tbody>115 </DataTable>116 <Note className="mt-3">117 Shapes are documented in the OpenAPI schema and in <span className="mono">docs/API.md</span>. Admin routes (<span className="mono">/admin/*</span>) require the <span className="mono">x-aia-admin-token</span> header and are not public. Also: <span className="mono">/benchmarks/{'{slug}'}/frontier</span>, <span className="mono">/pareto</span>, <span className="mono">/cost/context</span>, <span className="mono">/hardware/{'{slug}'}/fit</span>, <span className="mono">/licenses/{'{key}'}</span>, <span className="mono">/compare</span>, <span className="mono">/explore/types</span>, <span className="mono">/explore/{'{type}'}</span>, <span className="mono">POST /views</span>.118 </Note>119 </Section>120121 <Section eyebrow="Conventions" title="Reading responses">122 <ul className="max-w-3xl list-disc space-y-1.5 pl-5 text-sm leading-relaxed text-ink-2">123 <li>Numeric aggregates may arrive as <span className="mono">string</span> (Postgres decimals): treat every numeric field as <span className="mono">number | string | null</span> and coerce.</li>124 <li><span className="mono">null</span> 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.</li>125 <li>Events: <span className="mono">occurred_at = coalesce(effective_at, observed_at)</span>; <span className="mono">is_backfill</span> marks imported history; <span className="mono">group_key</span> folds one release seen in several documents.</li>126 <li>Universe: <span className="mono">/models</span> lists canonical model releases; artifacts and folded variants are excluded unless <span className="mono">include=artifacts</span>. Old slugs keep resolving (<span className="mono">redirected_from</span>).</li>127 <li>Benchmarks: results live in comparability groups (metric × <span className="mono">config_key</span>); leaderboards are one row per canonical model with a <span className="mono">trust_level</span>.</li>128 <li>Anything marked <span className="mono">estimated: true</span> (hardware fit, memory-derived parameter bounds) is derived by a stated formula — the <span className="mono">assumptions</span> array is part of the response.</li>129 <li>Caching: weak <span className="mono">ETag</span> + <span className="mono">Cache-Control: public, max-age=60, stale-while-revalidate=300</span>; send <span className="mono">If-None-Match</span> for a 304.</li>130 </ul>131 </Section>132133 <Section eyebrow="Terms" title="Fair use">134 <ul className="max-w-3xl list-disc space-y-1.5 pl-5 text-sm leading-relaxed text-ink-2">135 <li>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.</li>136 <li>Search is rate-limited per IP (HTTP 429 when exceeded). Higher limits and developer keys (<span className="mono">x-api-key</span>) are available on request — see <Link href={routes.about()} className="link">contact</Link>.</li>137 <li>Attribution: “Data: AI Atlas (www.ai-atlas.co)” with a link. Every record carries its own upstream sources; please keep them when you redistribute.</li>138 <li>Agent-readable access (MCP server, bulk exports) is planned, not available yet — the routes above are the only supported surface today.</li>139 </ul>140 </Section>141 </Container>142 );143}144