HTML 77.2%
TypeScript 10.5%
Python 9.6%
JavaScript 2.5%
1import type { Metadata } from 'next';2import Link from 'next/link';3import { Fragment } from 'react';4import { ChangeRow } from '@/components/changes/change-row';5import { BTN_GHOST, CTRL, DistBars, Field, Methodology, SortTh } from '@/components/intelligence/bits';6import { ExpandableOfferRow } from '@/components/intelligence/expand-price-row';7import { PriceIndexChart } from '@/components/intelligence/price-index-chart';8import { DataStrip, type StripItem, TerminalLayout } from '@/components/layout/terminal';9import { PriceMovers } from '@/components/prices/movers';10import { Chip } from '@/components/ui/badges';11import { DataTable, EmptyRow, Td, Th } from '@/components/ui/data-table';12import { EntityLink } from '@/components/ui/entity';13import { Pagination, withParams } from '@/components/ui/pagination';14import { SourceCell } from '@/components/ui/provenance';15import { Note, PageHeader, Section } from '@/components/ui/section';16import { EmptyState, Unavailable } from '@/components/ui/unavailable';17import { api, ApiError, intel, safe } from '@/lib/api';18import { fmtAgo, fmtDate, fmtInt, fmtTokens, fmtUsdPerM, num } from '@/lib/format';19import { routes, SITE_NAME, SITE_URL } from '@/lib/site';20import type { CheapestFrontier, Page, Price } from '@/lib/types';2122export const revalidate = 300;2324type SP = Record<string, string | undefined>;25const LIMIT = 100;26const DAYS = [30, 90, 180, 365];27const SORTS = ['input', 'output', 'cheapest_frontier', 'model', 'provider', 'observed'] as const;28type Sort = (typeof SORTS)[number];29const KEYS = ['days', 'scale', 'sort', 'provider', 'org', 'family', 'modality', 'model', 'offset'] as const;30const SORT_LABELS: Record<Sort, string> = { input: 'Cheapest input', output: 'Cheapest output', cheapest_frontier: 'Cheapest frontier output', model: 'Model', provider: 'Provider', observed: 'Recently observed' };3132function pick(sp: SP) {33 const cur: Record<string, string | undefined> = {};34 for (const k of KEYS) if (sp[k]) cur[k] = sp[k];35 const days = DAYS.includes(Number(cur.days)) ? Number(cur.days) : 180;36 const sort: Sort = (SORTS as readonly string[]).includes(cur.sort ?? '') ? (cur.sort as Sort) : 'input';37 return { cur, days, sort, offset: Math.max(0, Number(cur.offset) || 0), scale: cur.scale === 'log' ? ('log' as const) : ('linear' as const) };38}3940const TITLE = 'AI Price Index — USD per 1M tokens, every provider, every day';41export async function generateMetadata({ searchParams }: { searchParams: Promise<SP> }): Promise<Metadata> {42 const { cur, days } = pick(await searchParams);43 const filtered = cur.provider || cur.model || cur.org || cur.family || cur.modality;44 const title = filtered ? `AI prices — ${[cur.provider, cur.org, cur.family, cur.modality, cur.model].filter(Boolean).join(' · ')}` : TITLE;45 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.`;46 return {47 title,48 description,49 alternates: { canonical: routes.prices() },50 openGraph: { title: `${title} | ${SITE_NAME}`, description, url: `${SITE_URL}${routes.prices()}`, type: 'website', siteName: SITE_NAME },51 twitter: { card: 'summary_large_image', title, description },52 robots: filtered || cur.offset ? { index: false, follow: true } : undefined,53 };54}5556/** `/prices` 404s when a slug filter is unknown — keep that distinction instead of a generic "unavailable". */57async function loadPrices(query: Record<string, string | number | undefined>): Promise<{ page: (Page<Price> & { methodology?: string }) | null; notFound: boolean; detail?: string | null }> {58 try {59 return { page: await intel.prices(query), notFound: false };60 } catch (e) {61 return { page: null, notFound: e instanceof ApiError && e.notFound, detail: e instanceof ApiError ? e.detail : null };62 }63}6465function CheapestStrip({ c, label }: { c: CheapestFrontier | null | undefined; label: string }) {66 return (67 <div className="min-w-0 py-3">68 <p className="eyebrow">{label}</p>69 {c ? (70 <>71 <p className="mt-1 flex flex-wrap items-baseline gap-x-2">72 <EntityLink e={c.model} className="text-[15px] font-medium" />73 {c.model.organization && <span className="text-xs text-ink-3">{c.model.organization.name}</span>}74 </p>75 <p className="tnum mt-1 text-[22px] font-semibold leading-none text-accent-2">76 {fmtUsdPerM(c.output)} <span className="text-xs font-normal text-ink-3">output / 1M</span>77 </p>78 <p className="tnum mt-1 text-xs text-ink-3">79 input {fmtUsdPerM(c.input)} · context {fmtTokens(c.context_length)} · via <EntityLink e={c.provider} className="text-ink-2" />80 </p>81 </>82 ) : (83 <p className="mt-1 text-sm text-ink-3">No frontier model with a current priced offer in this response.</p>84 )}85 </div>86 );87}8889export default async function PricesPage({ searchParams }: { searchParams: Promise<SP> }) {90 const sp = await searchParams;91 const { cur, days, sort, offset, scale } = pick(sp);92 const [index, providers, facets, priced, listed, delisted] = await Promise.all([93 safe(intel.priceIndex(days)),94 safe(intel.providers()),95 safe(api.models({ limit: 1, facets: 1 })),96 loadPrices({ sort, provider: cur.provider, model: cur.model, org: cur.org, family: cur.family, modality: cur.modality, limit: LIMIT, offset }),97 safe(api.changes({ type: 'PROVIDER_LISTED', limit: 8 })),98 safe(api.changes({ type: 'PROVIDER_DELISTED', limit: 8 })),99 ]);100 const href = (patch: Record<string, string | number | undefined | null>) => withParams('/prices', cur, patch);101 const providerHref = (name: string) => {102 const p = (providers?.items ?? []).find((x) => x.name === name);103 return p ? href({ provider: p.slug, offset: undefined }) : undefined;104 };105106 // ---- index107 const series = index?.series ?? [];108 const populated = series.filter((p) => num(p.median_input) !== null || num(p.median_output) !== null);109 const latest = populated.at(-1) ?? null;110 const first = populated[0] ?? null;111 const delta = (field: 'median_input' | 'median_output' | 'median_frontier_output' | 'median_open_output' | 'median_embedding_input') => {112 const a = num(first?.[field]);113 const b = num(latest?.[field]);114 if (a === null || b === null || a === 0 || first === latest) return undefined;115 const pct = ((b - a) / a) * 100;116 if (Math.abs(pct) < 0.05) return undefined;117 return { value: `${pct > 0 ? '+' : ''}${pct.toFixed(1)}%`, tone: pct < 0 ? ('positive' as const) : pct > 0 ? ('negative' as const) : ('neutral' as const) };118 };119 const money = (v: unknown) => <span className="text-accent-2">{fmtUsdPerM(v)}</span>;120 const strip: StripItem[] | null = latest121 ? [122 { 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)}` },123 { 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') },124 { 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)}` },125 { 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)}` },126 { 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)}` },127 { label: 'Models priced', value: fmtInt(latest.models), definition: 'Canonical models with at least one live offer on the latest day.', hint: fmtDate(latest.day) },128 { 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 },129 ]130 : null;131132 // ---- offers133 const page = priced.page;134 const rows = page?.items ?? [];135 const minByModel = new Map<string, number>();136 const providersByModel = new Map<string, Set<string>>();137 for (const r of rows) {138 const v = num(r.input_per_mtok);139 if (v !== null) {140 const m = minByModel.get(r.model.slug);141 if (m === undefined || v < m) minByModel.set(r.model.slug, v);142 }143 const s = providersByModel.get(r.model.slug) ?? new Set<string>();144 s.add(r.provider.slug);145 providersByModel.set(r.model.slug, s);146 }147 const providerOptions = (providers?.items ?? []).slice().sort((a, b) => a.name.localeCompare(b.name));148 const providerName = providerOptions.find((o) => o.slug === cur.provider)?.name;149 const orgOptions = ((facets?.facets as { organizations?: { slug: string; name: string; count?: unknown }[] } | undefined)?.organizations ?? []).slice(0, 60);150 const familyOptions = ((facets?.facets as { families?: { value: string; label?: string; count?: unknown }[] } | undefined)?.families ?? []).slice(0, 60);151 const modalityOptions = ((facets?.facets as { modalities?: { value: string; count?: unknown }[] } | undefined)?.modalities ?? []);152 const filterCount = ['provider', 'org', 'family', 'modality', 'model'].filter((k) => cur[k]).length + (days !== 180 ? 1 : 0);153 const newListings = index?.new_listings_30d;154 const delistings = index?.delistings_30d;155 const changes30 = index?.price_changes_30d;156 const asCount = (v: unknown) => (Array.isArray(v) ? v.length : num(v));157158 const filters = (159 <form action="/prices" method="get" className="space-y-3" data-price-filters>160 <Field label="Provider">161 <select name="provider" defaultValue={cur.provider ?? ''} className={CTRL}>162 <option value="">Any provider</option>163 {providerOptions.map((p) => (164 <option key={p.slug} value={p.slug}>165 {p.name}166 </option>167 ))}168 </select>169 </Field>170 <Field label="Organization">171 <select name="org" defaultValue={cur.org ?? ''} className={CTRL}>172 <option value="">Any organization</option>173 {orgOptions.map((o) => (174 <option key={o.slug} value={o.slug}>175 {o.name}176 </option>177 ))}178 {cur.org && !orgOptions.some((o) => o.slug === cur.org) && <option value={cur.org}>{cur.org}</option>}179 </select>180 </Field>181 <Field label="Family">182 <select name="family" defaultValue={cur.family ?? ''} className={CTRL}>183 <option value="">Any family</option>184 {familyOptions.map((f) => (185 <option key={f.value} value={f.value}>186 {f.label ?? f.value}187 </option>188 ))}189 {cur.family && !familyOptions.some((f) => f.value === cur.family) && <option value={cur.family}>{cur.family}</option>}190 </select>191 </Field>192 <Field label="Modality">193 <select name="modality" defaultValue={cur.modality ?? ''} className={CTRL}>194 <option value="">Any modality</option>195 {modalityOptions.map((m) => (196 <option key={m.value} value={m.value}>197 {m.value}198 </option>199 ))}200 </select>201 </Field>202 <Field label="Model slug" hint="Slugs only — the API 404s on free text.">203 <input name="model" defaultValue={cur.model ?? ''} placeholder="e.g. claude-opus-5" className={CTRL} />204 </Field>205 <Field label="Index window">206 <select name="days" defaultValue={String(days)} className={CTRL}>207 {DAYS.map((d) => (208 <option key={d} value={d}>209 Last {d} days210 </option>211 ))}212 </select>213 </Field>214 <Field label="Sort offers">215 <select name="sort" defaultValue={sort} className={CTRL}>216 {SORTS.map((s) => (217 <option key={s} value={s}>218 {SORT_LABELS[s]}219 </option>220 ))}221 </select>222 </Field>223 {cur.scale && <input type="hidden" name="scale" value={cur.scale} />}224 <div className="flex gap-2">225 <button type="submit" className="inline-flex h-11 flex-1 items-center justify-center bg-ink lg:h-10 px-3 text-sm font-medium text-canvas hover:opacity-90">226 Apply227 </button>228 <Link href="/prices" className={BTN_GHOST}>229 Reset230 </Link>231 </div>232 </form>233 );234235 const inspector = (236 <div className="space-y-4 text-xs leading-relaxed text-ink-2">237 <div>238 <p className="eyebrow mb-1">Index method</p>239 <p>{index?.methodology ?? index?.note ?? 'Unavailable.'}</p>240 </div>241 {latest?.sample && (242 <div>243 <p className="eyebrow mb-1">Sample · {fmtDate(latest.day)}</p>244 <dl className="tnum grid grid-cols-[auto_1fr] gap-x-3 gap-y-0.5">245 <dt className="text-ink-3">offers</dt>246 <dd>{fmtInt(latest.sample.offers)}</dd>247 <dt className="text-ink-3">models</dt>248 <dd>{fmtInt(latest.sample.models)}</dd>249 <dt className="text-ink-3">frontier</dt>250 <dd>251 {fmtInt(latest.sample.frontier_models)} models · {fmtInt(latest.sample.frontier_offers)} offers252 </dd>253 <dt className="text-ink-3">open</dt>254 <dd>{fmtInt(latest.sample.open_models)} models</dd>255 <dt className="text-ink-3">embedding</dt>256 <dd>{fmtInt(latest.sample.embedding_models)} models</dd>257 </dl>258 </div>259 )}260 {index?.frontier?.composition && (261 <div>262 <p className="eyebrow mb-1">Frontier composition</p>263 <dl className="tnum grid grid-cols-[auto_1fr] gap-x-3 gap-y-0.5">264 {Object.entries(index.frontier.composition).map(([k, v]) => (265 <Fragment key={k}>266 <dt className="text-ink-3">{k.replace(/_/g, ' ')}</dt>267 <dd>{typeof v === 'string' && /^\d{4}-/.test(v) ? fmtDate(v) : fmtInt(v)}</dd>268 </Fragment>269 ))}270 </dl>271 </div>272 )}273 <div>274 <p className="eyebrow mb-1">30-day counters</p>275 <dl className="tnum grid grid-cols-[auto_1fr] gap-x-3 gap-y-0.5">276 <dt className="text-ink-3">new listings</dt>277 <dd>{fmtInt(asCount(newListings))}</dd>278 <dt className="text-ink-3">delistings</dt>279 <dd>{fmtInt(asCount(delistings))}</dd>280 <dt className="text-ink-3">price changes</dt>281 <dd>{fmtInt(asCount(changes30))}</dd>282 </dl>283 </div>284 <p>285 <Link href="/developers" className="link">GET /prices/index</Link> · <Link href="/methodology" className="link">methodology</Link>286 </p>287 </div>288 );289290 return (291 <TerminalLayout filters={filters} inspector={inspector} filtersTitle="Filters" inspectorTitle="Method" storageKey="aia-inspector-prices" filterCount={filterCount}>292 <PageHeader eyebrow="Price history terminal" title="AI Price Index" lede="What a million tokens costs, day by day, across every provider offer the atlas tracks — medians for the whole market, the frontier, open weights and embeddings; the distribution of current offers; movers, listings and every offer with its history." aside={latest ? <p className="tnum text-sm text-ink-3">{fmtInt(latest.offers)} offers · {fmtInt(latest.models)} models · {fmtDate(latest.day)}</p> : undefined} className="pt-4 md:pt-6" />293294 {/* ------------------------------------------------------------------------------------------------ index */}295 <Section eyebrow="AI Price Index" title={<>Daily medians · last {days} days</>} hairline={false} className="pt-0">296 {!index ? (297 <Unavailable what="Price index" />298 ) : !latest ? (299 <EmptyState title="No priced offers in this window">Prices appear once a provider pricing page has been crawled. Try a longer window.</EmptyState>300 ) : (301 <>302 {strip && <DataStrip items={strip} dense />}303 <PriceIndexChart series={series} initialScale={scale} className="mt-5" />304 <p className="tnum mt-2 text-xs text-ink-3">305 {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.306 </p>307 <Methodology text={index.methodology ?? index.note} />308 </>309 )}310 </Section>311312 {/* ------------------------------------------------------------------------------------- distribution + frontier */}313 {index && (314 <Section eyebrow="Current offers" title="Distribution and the cheapest frontier">315 <div className="grid grid-cols-[minmax(0,1fr)] gap-x-8 gap-y-4 md:grid-cols-[minmax(0,1.3fr)_minmax(0,1fr)_minmax(0,1fr)]">316 <div>317 <p className="eyebrow">Current offers by output price</p>318 <DistBars d={index.distribution} className="mt-2" />319 </div>320 <CheapestStrip c={index.cheapest_frontier} label="Cheapest frontier output" />321 <CheapestStrip c={index.cheapest_frontier_1m_context} label="Cheapest frontier with ≥ 1M context" />322 </div>323 <Methodology text={index.frontier?.methodology} />324 </Section>325 )}326327 {/* ------------------------------------------------------------------------------------------------ movers */}328 <Section eyebrow="Movers" title="Recent price changes" lede="Provider offers whose published price moved, newest first, with the relative change." action={{ href: `${routes.changes()}?type=PRICE_CHANGED`, label: 'All pricing events' }}>329 {!index ? <Unavailable what="Price changes" /> : <PriceMovers movers={index.movers ?? []} providerHref={providerHref} />}330 </Section>331332 {/* ---------------------------------------------------------------------------------- listings / delistings */}333 <Section eyebrow="Listings · 30 days" title="New listings and delistings">334 <div className="grid grid-cols-[minmax(0,1fr)] gap-8 md:grid-cols-2">335 <div>336 <p className="flex items-baseline justify-between">337 <span className="text-sm font-medium text-ink">New listings</span>338 <span className="tnum text-sm text-ink-3">{fmtInt(asCount(newListings))} in 30 d</span>339 </p>340 <p className="mt-0.5 text-xs text-ink-3">PROVIDER_LISTED events that occurred in the last 30 days (a model × provider offer first opened).</p>341 {Array.isArray(newListings) && newListings.length ? (342 <ul className="mt-2 border-t border-rule">{newListings.slice(0, 8).map((e) => <ChangeRow key={e.id} e={e} dense live={false} showDate />)}</ul>343 ) : listed?.items.length ? (344 <ul className="mt-2 border-t border-rule">{listed.items.map((e) => <ChangeRow key={e.id} e={e} dense live={false} showDate />)}</ul>345 ) : (346 <p className="mt-2 text-sm text-ink-3">No listing event in the window.</p>347 )}348 <p className="mt-2 text-xs">349 <Link href={`${routes.changes()}?type=PROVIDER_LISTED`} className="link">All listing events →</Link>350 </p>351 </div>352 <div>353 <p className="flex items-baseline justify-between">354 <span className="text-sm font-medium text-ink">Delistings</span>355 <span className="tnum text-sm text-ink-3">{fmtInt(asCount(delistings))} in 30 d</span>356 </p>357 <p className="mt-0.5 text-xs text-ink-3">PROVIDER_DELISTED events that occurred in the last 30 days (an offer closed).</p>358 {Array.isArray(delistings) && delistings.length ? (359 <ul className="mt-2 border-t border-rule">{delistings.slice(0, 8).map((e) => <ChangeRow key={e.id} e={e} dense live={false} showDate />)}</ul>360 ) : delisted?.items.length ? (361 <ul className="mt-2 border-t border-rule">{delisted.items.map((e) => <ChangeRow key={e.id} e={e} dense live={false} showDate />)}</ul>362 ) : (363 <p className="mt-2 text-sm text-ink-3">No delisting recorded in the window — offers that disappear from a pricing page are closed (valid_to) and emit PROVIDER_DELISTED.</p>364 )}365 </div>366 </div>367 </Section>368369 {/* ------------------------------------------------------------------------------------------------ offers */}370 <Section eyebrow="Current offers" title={providerName ? `Every current offer from ${providerName}` : cur.model ? `Current offers for ${cur.model}` : 'Every current offer'} lede="One row per model × provider, as published. Expand a row to load its price history (sparklines). Sort with the column headers or the rail.">371 {priced.notFound ? (372 <EmptyState title={`No ${cur.model ? 'model' : cur.provider ? 'provider' : 'entity'} with slug “${cur.model ?? cur.provider ?? cur.org ?? cur.family}”`}>373 Slugs are the last part of an entity URL (<span className="mono">/models/<slug></span>). <Link href={cur.model ? routes.search(cur.model, 'model') : routes.providers()} className="link">Search instead →</Link>374 </EmptyState>375 ) : !page ? (376 <Unavailable what="Current prices" reason={priced.detail ?? undefined} />377 ) : (378 <>379 {page.methodology && sort === 'cheapest_frontier' && <Methodology text={page.methodology} className="mb-3 mt-0" />}380 <DataTable scroll compact>381 <thead>382 <tr>383 <SortTh active={sort === 'model'} href={href({ sort: 'model', offset: undefined })}>Model</SortTh>384 <SortTh active={sort === 'provider'} href={href({ sort: 'provider', offset: undefined })}>Provider</SortTh>385 <SortTh active={sort === 'input'} href={href({ sort: undefined, offset: undefined })} num>Input / 1M</SortTh>386 <Th num>Cached in</Th>387 <SortTh active={sort === 'output' || sort === 'cheapest_frontier'} href={href({ sort: 'output', offset: undefined })} num>Output / 1M</SortTh>388 <Th num>Batch in / out</Th>389 <Th num>Context</Th>390 <SortTh active={sort === 'observed'} href={href({ sort: 'observed', offset: undefined })} dir="desc">Observed</SortTh>391 <Th>Source</Th>392 <Th className="w-24" aria-label="History" />393 </tr>394 </thead>395 <tbody>396 {rows.length === 0 && <EmptyRow cols={10}>{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.'}</EmptyRow>}397 {rows.map((p) => {398 const v = num(p.input_per_mtok);399 const cheapest = v !== null && minByModel.get(p.model.slug) === v && (providersByModel.get(p.model.slug)?.size ?? 0) > 1;400 return (401 <ExpandableOfferRow key={p.id} model={p.model.slug} provider={p.provider.slug} colSpan={9}>402 <Td primary>403 <EntityLink e={p.model} />404 {p.model.organization && <span className="ml-2 text-xs text-ink-3">{p.model.organization.name}</span>}405 {p.provider_model_id && p.provider_model_id !== p.model.slug && <span className="mono block text-[11px] text-ink-3">{p.provider_model_id}</span>}406 </Td>407 <Td label="Provider" className="text-ink-2">408 <Link href={href({ provider: p.provider.slug, offset: undefined })} className="hover:text-accent" title="Filter by this provider">409 {p.provider.name}410 </Link>411 <EntityLink e={p.provider} className="ml-1.5 text-xs text-ink-3">↗</EntityLink>412 </Td>413 <Td num label="Input / 1M" className={cheapest ? 'tnum font-semibold text-accent-2' : 'tnum text-accent-2'}>414 {fmtUsdPerM(p.input_per_mtok)}415 {cheapest && <Chip tone="accent" className="ml-1.5 align-middle">cheapest</Chip>}416 </Td>417 <Td num label="Cached in" className="tnum text-ink-2">{fmtUsdPerM(p.cached_input_per_mtok)}</Td>418 <Td num label="Output / 1M" className="tnum text-accent-2">{fmtUsdPerM(p.output_per_mtok)}</Td>419 <Td num label="Batch" className="tnum text-ink-2">420 {num(p.batch_input_per_mtok) === null && num(p.batch_output_per_mtok) === null ? <span className="text-ink-3">—</span> : `${fmtUsdPerM(p.batch_input_per_mtok)} / ${fmtUsdPerM(p.batch_output_per_mtok)}`}421 </Td>422 <Td num label="Context" className="tnum text-ink-2">{num(p.context_length) === null ? <span className="text-ink-3">—</span> : fmtTokens(p.context_length)}</Td>423 <Td label="Observed" className="text-ink-2" title={p.observed_at}>{fmtAgo(p.observed_at)}</Td>424 <Td label="Source"><SourceCell url={p.source_url} tier={p.tier} /></Td>425 </ExpandableOfferRow>426 );427 })}428 </tbody>429 </DataTable>430 <Pagination total={page.total} limit={LIMIT} offset={offset} makeHref={(o) => href({ offset: o || undefined })} className="mt-4" />431 <Note className="mt-3">432 “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; <Link href={routes.providers()} className="link">providers overview</Link> · <Link href={routes.calculator()} className="link">cost calculator</Link> · <Link href="/methodology" className="link">methodology</Link>.433 </Note>434 </>435 )}436 </Section>437 </TerminalLayout>438 );439}440