import type { Metadata } from 'next';
import Link from 'next/link';
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 } from '@/lib/site';
import type { EntityDetail, Stats } from '@/lib/types';
export const metadata: Metadata = { title: 'Developers — public API', description: 'The AI Atlas public API: JSON over HTTPS, no key required for public routes, OpenAPI docs, live example responses and curl recipes.', alternates: { canonical: '/developers' } };
export const revalidate = 3600;
const ENDPOINTS: { path: string; returns: string }[] = [
{ path: 'GET /health', returns: 'Service status (db, redis, llm) and version' },
{ path: 'GET /stats', returns: 'Live counters: entities per type, sources, documents, claims, events, prices, archive size — always computed from the database' },
{ path: 'GET /stats/history?days=90', returns: 'Daily counts' },
{ path: 'GET /search?q=&type=&limit=&offset=', returns: 'Natural-language or keyword search → { query: compiled filters, items: ranked entities (+rank), total }' },
{ path: 'GET /search/suggest?q=', returns: '≤ 8 prefix suggestions { id, entity_type, slug, name, organization_name }' },
{ path: 'GET /entities/{slug}', returns: 'Full entity detail: attributes, provenance per attribute, aliases, identifiers, relations (grouped), sources, timeline and type-specific blocks (prices, price_history, results, lineage, hardware_fit, models, papers, repositories)' },
{ path: 'GET /models/{slug} · /companies/{slug} · /papers/{slug} · /providers/{slug} · /benchmarks/{slug} · /hardware/{slug} · /frameworks/{slug} · /datasets/{slug} · /tools/{slug}', returns: 'Same detail, type-checked (404 if the slug is another type)' },
{ path: 'GET /entities/{slug}/timeline?limit=&before=', returns: '{ items: ChangeEvent[], next_before } — the entity’s own events (and, for companies, those of the models it develops)' },
{ path: 'GET /entities/{slug}/history?property=', returns: '{ items: Claim[] } — every claim ever recorded, newest first, with status current | superseded | conflicting | retracted, valid_from → valid_to, source and tier' },
{ path: 'GET /entities/{slug}/asof?date=YYYY-MM-DD', returns: '{ existed, first_seen_at, date, attributes, claims } — the entity as it was known on that date' },
{ path: 'GET /entities/{slug}/graph?depth=1|2&limit=80', returns: '{ root, nodes: { id, slug, name, entity_type, organization_name }[], edges: { source, target, predicate }[] } — capped neighbourhood' },
{ path: 'GET /entities/{slug}/sources · /related?limit=', returns: 'Documents describing the entity (deduped by URL) · related entities (same org / family / shared relations)' },
{ path: 'GET /models?q=&org=&family=&openness=&modality=&status=&min_params=&max_params=&min_context=&year_from=&year_to=&license=&sort=&order=&facets=1', returns: 'Paged models (+facets: organizations, openness, modalities, families, years, licenses, status)' },
{ path: 'GET /companies?q=&country=&kind=&sort=', returns: 'Paged organizations with model_count and paper_count (+facets)' },
{ path: 'GET /papers?q=&category=&org=&since=&until=&sort=', returns: 'Paged papers' },
{ path: 'GET /providers · GET /prices?model=&provider=&sort=¤t=1 · GET /prices/history?model=|provider=', returns: 'Providers with min prices · current price rows (USD / 1M tokens) · every price row incl. closed ones, oldest first' },
{ path: 'GET /prices/index?days=180', returns: '{ days, series: { day, median_input, median_output, min_input, max_input, models, offers }[], movers: ChangeEvent[] (PRICE_CHANGED), note }' },
{ path: 'GET /benchmarks · GET /benchmarks/{slug}/results?config=&history=1&limit=&offset= · GET /benchmarks/{slug}/history?model=', returns: 'Benchmarks with result_count, model_count and current top · leaderboard sorted by score (respecting higher_is_better), current rows only unless history=1, filterable by config key=value · one model’s results over time' },
{ path: 'GET /hardware?kind=&manufacturer=&min_memory=&sort= · GET /hardware/fit?memory_gb=&quant=4bit|8bit|fp16&context=8192&openness=&limit=', returns: 'Hardware listing · ESTIMATED fit: { inputs, estimated: true, assumptions[], counts, items: { model, parameter_count, estimated_memory_gb, headroom_gb, fits, quantization, note }[] }' },
{ path: 'GET /explore/types · GET /explore/{type}?q=&org=&sort=', returns: 'Entity types with live counts; generic paged listing for any type' },
{ path: 'GET /changes?category=&type=&entity_type=&importance_min=&since=&until=&q=&limit=&before= · GET /changes/daily?date= · GET /changes/categories?days=', returns: 'Change-event feed (cursor paged, next_before) · daily digest with sections · category counts' },
{ path: 'GET /timeline?entity=&year=&category=&limit=', returns: '{ items: { month, count, events[] }[], total } — grouped by month' },
{ path: 'GET /compare?ids=a,b[,…6]', returns: '{ entity_type, dimensions: { key, label, unit, kind: number|text|list|bool|date, source }[], items: { entity, values, provenance, prices?, results? }[] } — 2–6 entities of one type; models add best prices and the benchmarks shared by all' },
{ path: 'GET /diff?a=YYYY-MM-DD&b=YYYY-MM-DD&scope=all|models|org:
);
}
export default async function DevelopersPage() {
const [health, stats, top] = await Promise.all([safe(api.health()), safe(api.stats()), safe(api.models({ limit: 2, sort: 'quality' }))]);
const slugA = top?.items[0]?.slug;
const slugB = top?.items[1]?.slug;
const model = slugA ? await safe(api.entityOfType('models', slugA)) : null;
const exA = slugA ?? '{children}
API status unavailable
} />Interactive OpenAPI documentation: /api/v1/docs. JSON, UTF-8, ISO-8601 UTC timestamps. Errors are {'{ "detail": "…" }'} with 400 / 404 / 429 / 503 (422 for parameters rejected by validation).
{`# Live counters
curl -s ${PUBLIC_API_BASE}/stats | jq '.entities'
# Natural-language search → compiled filters + ranked results
curl -s "${PUBLIC_API_BASE}/search?q=open+models+over+100B+released+in+2026" | jq '.query, .items[0:3]'
# A model with provenance, prices, results, lineage
curl -s ${PUBLIC_API_BASE}/models/${exA} | jq '{name, attributes, provenance: (.provenance | keys)}'
# Compare two models on every shared dimension (+ shared benchmarks, best prices)
curl -s "${PUBLIC_API_BASE}/compare?ids=${exA},${exB}" | jq '.dimensions[].key, .items[].values'
# What changed between two dates (scope: all | models | org: | family:)
curl -s "${PUBLIC_API_BASE}/diff?a=${weekAgo}&b=${today}&scope=models" | jq '.counts, .new_entities[0:3]'
# Claim history and the state of a record as of a date
curl -s "${PUBLIC_API_BASE}/entities/${exA}/history?property=context_length" | jq '.items[] | {value, status, valid_from, valid_to, source_url}'
curl -s "${PUBLIC_API_BASE}/entities/${exA}/asof?date=${weekAgo}" | jq '{existed, attributes}'
# Neighbourhood graph, price index, leaderboard by config, estimated hardware fit
curl -s "${PUBLIC_API_BASE}/entities/${exA}/graph?depth=1&limit=80" | jq '{nodes: (.nodes|length), edges: (.edges|length)}'
curl -s "${PUBLIC_API_BASE}/prices/index?days=90" | jq '.series[-1], (.movers|length)'
curl -s "${PUBLIC_API_BASE}/benchmarks//results?config=variant=v2.1&limit=20" | jq '.items[] | {model: .model.name, score, config}'
curl -s "${PUBLIC_API_BASE}/hardware/fit?memory_gb=64&quant=4bit&context=32768&limit=20" | jq '.assumptions, .items[0]'
# What changed today
curl -s "${PUBLIC_API_BASE}/changes?importance_min=2&limit=20" | jq '.items[] | {event_type, summary, source_url}'`}
GET /stats
{stats ?{JSON.stringify(statsExample(stats), null, 2)} : GET /models/{exA}
{model ?{JSON.stringify(modelExample(model), null, 2)} :