import type { Metadata } from 'next'; import Link from 'next/link'; import { Fragment } from 'react'; import { ChangeRow } from '@/components/changes/change-row'; import { BTN_GHOST, CTRL, DistBars, Field, Methodology, SortTh } from '@/components/intelligence/bits'; import { ExpandableOfferRow } from '@/components/intelligence/expand-price-row'; import { PriceIndexChart } from '@/components/intelligence/price-index-chart'; import { DataStrip, type StripItem, TerminalLayout } from '@/components/layout/terminal'; import { PriceMovers } from '@/components/prices/movers'; import { Chip } from '@/components/ui/badges'; import { DataTable, EmptyRow, Td, Th } from '@/components/ui/data-table'; import { EntityLink } from '@/components/ui/entity'; import { Pagination, withParams } from '@/components/ui/pagination'; import { SourceCell } from '@/components/ui/provenance'; import { Note, PageHeader, Section } from '@/components/ui/section'; import { EmptyState, Unavailable } from '@/components/ui/unavailable'; import { api, ApiError, intel, safe } from '@/lib/api'; import { fmtAgo, fmtDate, fmtInt, fmtTokens, fmtUsdPerM, num } from '@/lib/format'; import { routes, SITE_NAME, SITE_URL } from '@/lib/site'; import type { CheapestFrontier, Page, Price } from '@/lib/types'; export const revalidate = 300; type SP = Record; const LIMIT = 100; const DAYS = [30, 90, 180, 365]; const SORTS = ['input', 'output', 'cheapest_frontier', 'model', 'provider', 'observed'] as const; type Sort = (typeof SORTS)[number]; const KEYS = ['days', 'scale', 'sort', 'provider', 'org', 'family', 'modality', 'model', 'offset'] as const; const SORT_LABELS: Record = { input: 'Cheapest input', output: 'Cheapest output', cheapest_frontier: 'Cheapest frontier output', model: 'Model', provider: 'Provider', observed: 'Recently observed' }; function pick(sp: SP) { const cur: Record = {}; for (const k of KEYS) if (sp[k]) cur[k] = sp[k]; const days = DAYS.includes(Number(cur.days)) ? Number(cur.days) : 180; const sort: Sort = (SORTS as readonly string[]).includes(cur.sort ?? '') ? (cur.sort as Sort) : 'input'; return { cur, days, sort, offset: Math.max(0, Number(cur.offset) || 0), scale: cur.scale === 'log' ? ('log' as const) : ('linear' as const) }; } const TITLE = 'AI Price Index — USD per 1M tokens, every provider, every day'; export async function generateMetadata({ searchParams }: { searchParams: Promise }): Promise { const { cur, days } = pick(await searchParams); const filtered = cur.provider || cur.model || cur.org || cur.family || cur.modality; const title = filtered ? `AI prices — ${[cur.provider, cur.org, cur.family, cur.modality, cur.model].filter(Boolean).join(' · ')}` : TITLE; const description = `Daily medians of published input, output, frontier, open-weight and embedding prices per 1M tokens across every provider offer AI Atlas tracks (last ${days} days), the distribution of current offers, the cheapest frontier model, price movers, new listings and delistings, and every current offer with its source and history.`; return { title, description, alternates: { canonical: routes.prices() }, openGraph: { title: `${title} | ${SITE_NAME}`, description, url: `${SITE_URL}${routes.prices()}`, type: 'website', siteName: SITE_NAME }, twitter: { card: 'summary_large_image', title, description }, robots: filtered || cur.offset ? { index: false, follow: true } : undefined, }; } /** `/prices` 404s when a slug filter is unknown — keep that distinction instead of a generic "unavailable". */ async function loadPrices(query: Record): Promise<{ page: (Page & { methodology?: string }) | null; notFound: boolean; detail?: string | null }> { try { return { page: await intel.prices(query), notFound: false }; } catch (e) { return { page: null, notFound: e instanceof ApiError && e.notFound, detail: e instanceof ApiError ? e.detail : null }; } } function CheapestStrip({ c, label }: { c: CheapestFrontier | null | undefined; label: string }) { return (

{label}

{c ? ( <>

{c.model.organization && {c.model.organization.name}}

{fmtUsdPerM(c.output)} output / 1M

input {fmtUsdPerM(c.input)} · context {fmtTokens(c.context_length)} · via

) : (

No frontier model with a current priced offer in this response.

)}
); } export default async function PricesPage({ searchParams }: { searchParams: Promise }) { const sp = await searchParams; const { cur, days, sort, offset, scale } = pick(sp); const [index, providers, facets, priced, listed, delisted] = await Promise.all([ safe(intel.priceIndex(days)), safe(intel.providers()), safe(api.models({ limit: 1, facets: 1 })), loadPrices({ sort, provider: cur.provider, model: cur.model, org: cur.org, family: cur.family, modality: cur.modality, limit: LIMIT, offset }), safe(api.changes({ type: 'PROVIDER_LISTED', limit: 8 })), safe(api.changes({ type: 'PROVIDER_DELISTED', limit: 8 })), ]); const href = (patch: Record) => withParams('/prices', cur, patch); const providerHref = (name: string) => { const p = (providers?.items ?? []).find((x) => x.name === name); return p ? href({ provider: p.slug, offset: undefined }) : undefined; }; // ---- index const series = index?.series ?? []; const populated = series.filter((p) => num(p.median_input) !== null || num(p.median_output) !== null); const latest = populated.at(-1) ?? null; const first = populated[0] ?? null; const delta = (field: 'median_input' | 'median_output' | 'median_frontier_output' | 'median_open_output' | 'median_embedding_input') => { const a = num(first?.[field]); const b = num(latest?.[field]); if (a === null || b === null || a === 0 || first === latest) return undefined; const pct = ((b - a) / a) * 100; if (Math.abs(pct) < 0.05) return undefined; return { value: `${pct > 0 ? '+' : ''}${pct.toFixed(1)}%`, tone: pct < 0 ? ('positive' as const) : pct > 0 ? ('negative' as const) : ('neutral' as const) }; }; const money = (v: unknown) => {fmtUsdPerM(v)}; const strip: StripItem[] | null = latest ? [ { label: 'Median input', value: money(latest.median_input), definition: 'Median of live input prices (USD / 1M tokens) across every provider offer valid at the end of the day; zero or missing prices excluded.', delta: delta('median_input'), hint: `n=${fmtInt(latest.sample?.offers ?? latest.offers)}` }, { label: 'Median output', value: money(latest.median_output), definition: 'Median of live output prices across every provider offer valid at the end of the day.', delta: delta('median_output') }, { label: 'Median frontier output', value: money(latest.median_frontier_output), definition: index?.frontier?.methodology ?? 'Frontier models = recent releases by active organizations or top-10 on a benchmark; no composite score.', delta: delta('median_frontier_output'), hint: `n=${fmtInt(latest.sample?.frontier_offers)}` }, { label: 'Median open output', value: money(latest.median_open_output), definition: 'Median output price over models with openness open-weights / open-source.', delta: delta('median_open_output'), hint: `n=${fmtInt(latest.sample?.open_models)}` }, { label: 'Median embedding input', value: money(latest.median_embedding_input), definition: 'Median input price over models whose modalities include embedding.', delta: delta('median_embedding_input'), hint: `n=${fmtInt(latest.sample?.embedding_models)}` }, { label: 'Models priced', value: fmtInt(latest.models), definition: 'Canonical models with at least one live offer on the latest day.', hint: fmtDate(latest.day) }, { label: 'Cheapest input', value: fmtUsdPerM(latest.min_input), definition: 'Lowest positive live input price on the latest day.', hint: num(latest.max_input) !== null ? `dearest ${fmtUsdPerM(latest.max_input)}` : undefined }, ] : null; // ---- offers const page = priced.page; const rows = page?.items ?? []; const minByModel = new Map(); const providersByModel = new Map>(); for (const r of rows) { const v = num(r.input_per_mtok); if (v !== null) { const m = minByModel.get(r.model.slug); if (m === undefined || v < m) minByModel.set(r.model.slug, v); } const s = providersByModel.get(r.model.slug) ?? new Set(); s.add(r.provider.slug); providersByModel.set(r.model.slug, s); } const providerOptions = (providers?.items ?? []).slice().sort((a, b) => a.name.localeCompare(b.name)); const providerName = providerOptions.find((o) => o.slug === cur.provider)?.name; const orgOptions = ((facets?.facets as { organizations?: { slug: string; name: string; count?: unknown }[] } | undefined)?.organizations ?? []).slice(0, 60); const familyOptions = ((facets?.facets as { families?: { value: string; label?: string; count?: unknown }[] } | undefined)?.families ?? []).slice(0, 60); const modalityOptions = ((facets?.facets as { modalities?: { value: string; count?: unknown }[] } | undefined)?.modalities ?? []); const filterCount = ['provider', 'org', 'family', 'modality', 'model'].filter((k) => cur[k]).length + (days !== 180 ? 1 : 0); const newListings = index?.new_listings_30d; const delistings = index?.delistings_30d; const changes30 = index?.price_changes_30d; const asCount = (v: unknown) => (Array.isArray(v) ? v.length : num(v)); const filters = (
{cur.scale && }
Reset
); const inspector = (

Index method

{index?.methodology ?? index?.note ?? 'Unavailable.'}

{latest?.sample && (

Sample · {fmtDate(latest.day)}

offers
{fmtInt(latest.sample.offers)}
models
{fmtInt(latest.sample.models)}
frontier
{fmtInt(latest.sample.frontier_models)} models · {fmtInt(latest.sample.frontier_offers)} offers
open
{fmtInt(latest.sample.open_models)} models
embedding
{fmtInt(latest.sample.embedding_models)} models
)} {index?.frontier?.composition && (

Frontier composition

{Object.entries(index.frontier.composition).map(([k, v]) => (
{k.replace(/_/g, ' ')}
{typeof v === 'string' && /^\d{4}-/.test(v) ? fmtDate(v) : fmtInt(v)}
))}
)}

30-day counters

new listings
{fmtInt(asCount(newListings))}
delistings
{fmtInt(asCount(delistings))}
price changes
{fmtInt(asCount(changes30))}

GET /prices/index · methodology

); return ( {fmtInt(latest.offers)} offers · {fmtInt(latest.models)} models · {fmtDate(latest.day)}

: undefined} className="pt-4 md:pt-6" /> {/* ------------------------------------------------------------------------------------------------ index */}
Daily medians · last {days} days} hairline={false} className="pt-0"> {!index ? ( ) : !latest ? ( Prices appear once a provider pricing page has been crawled. Try a longer window. ) : ( <> {strip && }

{fmtInt(populated.length)} populated day{populated.length === 1 ? '' : 's'} in the window · sample sizes per day in the tooltip · a null median means no offer in that universe on that day.

)}
{/* ------------------------------------------------------------------------------------- distribution + frontier */} {index && (

Current offers by output price

)} {/* ------------------------------------------------------------------------------------------------ movers */}
{!index ? : }
{/* ---------------------------------------------------------------------------------- listings / delistings */}

New listings {fmtInt(asCount(newListings))} in 30 d

PROVIDER_LISTED events that occurred in the last 30 days (a model × provider offer first opened).

{Array.isArray(newListings) && newListings.length ? (
    {newListings.slice(0, 8).map((e) => )}
) : listed?.items.length ? (
    {listed.items.map((e) => )}
) : (

No listing event in the window.

)}

All listing events →

Delistings {fmtInt(asCount(delistings))} in 30 d

PROVIDER_DELISTED events that occurred in the last 30 days (an offer closed).

{Array.isArray(delistings) && delistings.length ? (
    {delistings.slice(0, 8).map((e) => )}
) : delisted?.items.length ? (
    {delisted.items.map((e) => )}
) : (

No delisting recorded in the window — offers that disappear from a pricing page are closed (valid_to) and emit PROVIDER_DELISTED.

)}
{/* ------------------------------------------------------------------------------------------------ offers */}
{priced.notFound ? ( Slugs are the last part of an entity URL (/models/<slug>). Search instead → ) : !page ? ( ) : ( <> {page.methodology && sort === 'cheapest_frontier' && } Model Provider Input / 1M Cached in Output / 1M Batch in / out Context Observed Source {rows.length === 0 && {sort === 'cheapest_frontier' ? 'No frontier model has a current priced offer in the API response (see the methodology above).' : 'No current offers match these filters.'}} {rows.map((p) => { const v = num(p.input_per_mtok); const cheapest = v !== null && minByModel.get(p.model.slug) === v && (providersByModel.get(p.model.slug)?.size ?? 0) > 1; return ( {p.model.organization && {p.model.organization.name}} {p.provider_model_id && p.provider_model_id !== p.model.slug && {p.provider_model_id}} {p.provider.name} ↗ {fmtUsdPerM(p.input_per_mtok)} {cheapest && cheapest} {fmtUsdPerM(p.cached_input_per_mtok)} {fmtUsdPerM(p.output_per_mtok)} {num(p.batch_input_per_mtok) === null && num(p.batch_output_per_mtok) === null ? — : `${fmtUsdPerM(p.batch_input_per_mtok)} / ${fmtUsdPerM(p.batch_output_per_mtok)}`} {num(p.context_length) === null ? — : fmtTokens(p.context_length)} {fmtAgo(p.observed_at)} ); })} href({ offset: o || undefined })} className="mt-4" /> “Cheapest” marks the lowest input price for a model among the rows on this page (models served by several providers). USD per 1M tokens as published; providers overview · cost calculator · methodology. )}
); }