API
The whole catalogue as static JSON files: no key, no server, no rate limit beyond the host's. Files are rebuilt with each data refresh. Data generated 2026-10-20 03:37 UTC.
Free JSON feed
Stable URLs under /v1/, free to fetch with no key or sign-up. Poll them after each refresh (every 6 hours), or subscribe to the Atom feeds.
- /api/v1/models.json
- Every model with its cheapest headline price, price range and capabilities
- /api/v1/models/<creator>/<model>.json
- One model with every endpoint and price, e.g. anthropic/claude-sonnet-4
- /api/v1/history/<creator>/<model>.json
- Price history: the cheapest direct price at each refresh where it changed, e.g. deepseek/deepseek-v4-pro-0813. Index: /api/v1/history/index.json
- /api/v1/plans.json
- Flat-fee subscription plans, verified by hand
- /feed.xml
- Atom feed of every catalogue change. Per model and per plan: /feeds/models/<creator>/<model>.xml, /feeds/plans/<provider>/<plan>.xml (linked from each page that has changes)
Licence. The data merges third-party sources, each under its own terms, listed per build in manifest.json. Credit models.dev and LiteLLM (MIT) and Epoch AI (CC BY 4.0) when you redistribute; OpenRouter-derived values are marked unverified. Details under Attribution below.
Files
| URL | What it is | Size |
|---|---|---|
| /api/v1/models.json | All models, slim rows | 2.3 MB |
| /api/v1/providers.json | Providers and aggregators | 265 KB |
| /api/v1/creators.json | Creators | 17 KB |
| /api/v1/manifest.json | Sources, licences, counts, checksums | |
| /api/v1/models/<creator>/<slug>.json | One model with all its endpoints and prices. Example: anthropic/claude-sonnet-4 | 2,612 files |
| /api/v1/schema/*.schema.json | JSON Schema (draft 2020-12) for every file type, e.g. model.schema.json | |
| /feed.xml | Atom feed of catalogue changes (see Changes) |
Model detail files are large in total, so fetch the ones you need and use the checksums in manifest.json for caching.
Using it
curl -s /api/v1/models.json | jq '.items[0]'
curl -s /api/v1/models/anthropic/claude-sonnet-4.json \
| jq '.endpoints[].pricing.base'Prepend the site's origin. Unknown is not no: null in a capability, limit or price means no source reports it. Do not render it as “No” or as zero.
Attribution
If you redistribute this data, credit models.dev and LiteLLM (both MIT) and show the “last refreshed” time per source from manifest.sources[].fetched_at. The API marks each source's redistribution status in manifest.sources[].redistribution. OpenRouter-derived values are marked "unverified" because its reuse terms are still under review. Full list: About and sources.
Version policy
The /v1/ prefix is the major version. Within it, fields, files and enum values may be added, so ignore what you do not know. A removal, rename or meaning change ships as /v2/, and v1 stays for an announced period. Questions or bugs: GitHub issues.
Reference
Rendered from docs/API.md in the repository.
Static files generated by pipeline/src/api.ts (npm run api, or npm run derive) into data/api/v1/. There is no server: host the folder as-is. Files are minified, keys are sorted recursively, rows are sorted by id, and generated_at is copied from the merged manifest, so the same input produces byte-identical output. Every file is validated against its zod schema (pipeline/src/schema.ts, pipeline/src/api.ts) before it is written.
| path | contents |
|---|---|
/v1/models.json | {schema_version, generated_at, count, items[]}; slim rows: id, name, creator, type, release_date, context, modalities{input,output}, capabilities, provider_count, cheapest{input_mtok,output_mtok,endpoint_id,provider_id,blended_mtok,ratio}, open_weights, quality (1.7.0) |
/v1/redirects.json | {..., items: [{from, to, reason, since}]}: model ids that no longer exist and the id that replaced them (e.g. folded *-batch models). Clients holding an old id should follow to |
/v1/models/<creator>/<slug>.json | {schema_version, model, endpoints[]}: the full Model and all of its Endpoints (with sources, alts, flags, snapshots, source_refs, performance) |
/v1/providers.json | {..., items: Provider[]} |
/v1/creators.json | {..., items: Creator[]} |
/v1/plans.json | {..., items: Plan[]}: curated subscription plans (docs/PLANS.md). Not token prices; never converted |
/v1/plans/<provider>/<slug>.json | {schema_version, plan} (plan id = <provider>/<slug>) |
/v1/history/<creator>/<slug>.json | (1.7.0) price history of the model's headline price (pipeline/src/series.ts); see "Price history" below |
/v1/history/index.json | (1.7.0) {schema_version, generated_at, boundary, models, changed[{model_id, changes, last_change}]}: models with a series, and those whose cheapest pair moved (newest change first) |
/v1/manifest.json | schema_version, api_version, generated_at, sources[] (id, url, license, fetched_at, status, rows, redistribution, attribution), excluded_sources, counts, files[] (bytes+sha256), model_files{count,bytes,sha256}, plan_files, history_files (1.7.0, same scheme; history/index.json is in files[]) |
/v1/schema/*.schema.json | JSON Schema (draft 2020-12, generated with z.toJSONSchema) for model, endpoint, provider, creator, manifest, model-summary, model-detail, and the three list files, plus plan, plan-detail, plans-list, redirects-list, history, history-index (1.7.0) |
Field meaning is defined in docs/SCHEMA.md; this API publishes the merged records unchanged (only the slim list rows are a projection). provider_count excludes aggregators (gateways); gateway pricing is published separately (see Gateways below). cheapest (schema 1.2.0) is one real endpoint's input/output pair, the headline-eligible endpoint with the lowest blended cost at 3 input : 1 output tokens (blended_mtok, ratio: "3:1"), identified by endpoint_id / provider_id; null when no endpoint is eligible. Before 1.2.0 it was the separate minimum of input and of output, possibly from different providers. creator in list rows is the creator_id. A model's file path is its canonical id (<creator>/<slug>); ids are validated to be path-safe at build time. Dated snapshots and aliases are never separate models; different model versions (for example v4-flash vs v4-pro) are different ids.
Gateways (additive, schema 1.x)
A gateway (aggregator) is a route to a model, not a provider of it. List rows (models.json) additionally carry price_range, cheapest_gateway and provider_count_gateways; model files carry the same on model plus route_kind, routes_to, gateway on each endpoint, and fees, gateway_stats on providers (providers.json). Definitions: docs/SCHEMA.md#gateways. All are nullable/defaulted, so older readers keep working. With --exclude-source, these are recomputed from the endpoints that remain.
Headline eligibility, vetting and batch rows (schema 1.2.0)
Every endpoint carries headline_eligible (boolean) and vetting (lab|cloud|inference|curated|reseller|gateway); model price_range and cheapest are computed over headline-eligible endpoints only. Use headline_eligible for any "from $X" or "cheapest" display; ineligible rows are still real listings, and rows flagged unverified-cheap: ... or price-sanity: ... should be labelled as unverified. Batch-API prices are endpoint rows with variant.service_tier: "batch", price_kind: "batch" and ids ending in batch (<model>@<provider>#batch, #<region>.batch): never compare them with standard prices. Rule details: docs/SCHEMA.md "Headline eligibility", "Price sanity", "Batch API rows", "Redirects".
Plans and price kinds (additive)
manifest.counts.plans and manifest.plan_files ({count, bytes, sha256}, same scheme as model_files; per-plan files are not in files[]). Endpoint files carry price_kind, price_kind_note, retires_in_days; model files plan_ids, retires_in_days; providers plan_count. Treat price_kind != "paid" as "no price": never display it as a bare $0 or compare it. unpriced means no pricing object (including non-token models whose only source price was a $0 token artefact).
Plan fields (schema 1.5.0)
plan.includes.model_ids_inferred: model ids the plan may include, resolved from its free-textmodel_families(a family word such as "Sonnet" -> the creator's newest current model of that family; an explicit name such as "GPT-6.1 Sol" -> that model; retired models never). Not a vendor statement: show it apart frommodel_ids_verified("may include").plan.price:seat_minimum,student {amount_usd, original, note},regional[],tax_inclusive,pricing_model(flat | seat | seat_plus_usage | contact_sales | usage;seat_plus_usage= the price is a seat fee and usage is billed on top, e.g. Claude Enterprise "Seat $20 + usage at API rates"), andfx {rate, currency, date, source_url}whenamount_usdwas converted fromoriginal(indicative; the vendor's price isoriginal).fx: nullmeans the vendor states USD.plan.limits.items[]:reset(rolling | fixed | null),at_limit(overage | throttle | downgrade | blocked | wait | null);plan.limits.overage_rate {amount_usd, unit, text}(amount_usdnull when stated only in words, e.g. "at API rates").plan.also_sold_by[]: the same subscription sold under another provider's name (e.g. xAI's X Subscription on cursor.com);plan.provider_typemay beapp.
Lifecycle and announced price changes (schema 1.3.0)
deprecation_date_kind ("firm" | "not_before" | null) on models and endpoints qualifies deprecation_date: not_before is a floor ("retirement not sooner than <date>", Anthropic): the model is still active, the date never means deprecated, and it should be shown as "not before <date>" even after it passes; firm is the vendor's stated retirement date; null is the source's date as stated. A model-level deprecation_date comes only from the creator's own source (a curated statement or the creator's first-party endpoint) or is the latest endpoint date when every endpoint is deprecated; one provider's retirement is only on that provider's endpoint. Endpoint pricing.scheduled[] ({kind: "promo_end"|"price_change", current_until, effective_from, prices, source_url, verified_on}, absent when none) lists announced price changes: the base price holds through current_until, and the listed components change on effective_from. Endpoints flagged lifecycle-unverified: ... (a first-party row missing from the vendor's current model list) are not headline-eligible. Rules: docs/SCHEMA.md "Lifecycle".
Identity, modalities and creators (schema 1.4.0)
Model ids are URLs: Phase 3 merged verified duplicates (unknown/databricks-gpt-5-5, alibaba/deepseek-r1, anthropic/claude-4-5-sonnet, openai/gpt-5-6, ...) into one id each, and every removed id is listed in /v1/redirects.json (follow to). One (provider, provider_model_id) maps to exactly one model; the few known exceptions stay separate on purpose and are listed in the repository's data/reports/identity.md. model.modalities is filled from the type where no source states it; such a side has model.sources["modalities.input"] / ["modalities.output"] equal to "inferred" (narrow and certainly true, e.g. chat text -> text, possibly narrower than reality); list rows (models.json) carry the same as modalities_inferred: true. A corrected type has sources.type = "inferred". creators.json rows carry website (or null). context / max_output are the creator's own limit when it publishes one.
Price provenance (schema 1.6.0)
- Every endpoint in a model file carries
price_source({source, fetched_at}ornullwhen unpriced): the source whose price the row shows (models.dev,litellm,openrouter,genai-prices, ... orcurated) and when it was fetched (ISO timestamp frommanifest.sources[].fetched_at), or forcuratedthe date a person read the vendor page (YYYY-MM-DD). Show it as "via <source>, fetched <date>"; a fetch time is not a re-check of the price. With--exclude-source, rows whose price came from an excluded source getprice_source: null(and no pricing). providers.jsonrows carrypricing_url(the provider's own price page, or null) andpricing_url_verified_on.- Model files carry
model.vendor_pricing({url, verified_on, input_mtok, output_mtok, match, note}ornull): the creator's list price as read on its own page, for about 80 high-traffic models.matchcompares it with our first-party row on every build (truesame price,falsediffers,nullnothing to compare); show "<creator> list price, verified <date>" with a link tourl, and treatmatch: falseas a data warning. Prices are null when the creator does not price the model (open weights;urlis then the model page). - Flags starting
price-note:are informational (e.g. a sale price with no stated end date); they do not affectheadline_eligible. Rules:docs/SCHEMA.md"Provenance".
Price history (schema 1.7.0, audit B-06)
/v1/history/<creator>/<slug>.json = {schema_version: "1", model_id, since, boundary, starts_because, observed_through, ratio: "3:1", changes, points[]}; each point {at, input_mtok, output_mtok, blended_mtok, endpoint_id, provider_id, range{input{min, median}, output{min, median}} | null} is the model's cheapest pair and price_range (min/median) as published by the run at at, held until the next point. Points are written only when a value changes, so one point means "no change since since"; changes counts moves of the cheapest input/output pair (a provider switch at the same price is not a change). The series is rebuilt from the run diffs in data/history with the pipeline's own headline rule, so sanity-flagged rows never appear in it. It starts at boundary (the first run with today's single-endpoint headline shape, 2026-10-19T14:54:50Z) or later when the model's past cannot be reconstructed (starts_because: model_added, endpoint_removed, deprecated). Only models that had a headline price in the window have a file (about 1,360). The same files are in the repository at data/derived/history/.
Quality index (schema 1.7.0, audit B-07)
quality on list rows and on model in model files: {index: "eci", value, lo, hi, model_group, source_url, retrieved} or null. It is the Epoch Capabilities Index as Epoch AI publishes it (eci_scores.csv of the Benchmarking Hub download), with its confidence interval (lo/hi; null for the scale's two anchor models, GPT-5 = 150 and Claude 3.5 Sonnet = 130). model_group is Epoch's model name, retrieved the download date. null means not evaluated (about 150 of ~2,600 models have a score): never show it as 0 and never rank it. ECI is not a percentage; do not divide it by a price. Credit (CC BY 4.0) is in manifest.sources[] (id: "epoch-benchmarks", attribution) and must be shown with the numbers.
Three-state semantics
Booleans in capabilities, open_weights, data_policy.* and is_free are true / false / null. null means unknown, never "no". Likewise null for quantization, regions, supported_parameters, pricing, limits etc. means the data is not known. Clients must not render null as "No" or treat it as satisfying a filter unless the user opts in. For data_policy.zdr, true only when a source confirms it; otherwise null.
Versioning and compatibility
- The path prefix (
/v1/) is the API major version;schema_version(semver, in every file) tracksSCHEMA_VERSION. - Within
/v1/: new fields, new files and new enum values may be added (minor). Clients must ignore unknown fields and tolerate unknown enum values. - Field removal, rename, type change or semantic change requires a new major (
/v2/);/v1/is then kept for a deprecation period announced in the repository. - Use the JSON Schema files to validate or generate types. Cache with the
sha256values inmanifest.json. - Model detail file names follow canonical ids, which are stable unless an alias in
data/curated/aliases.yamlchanges.
Attribution and licences
Data is a merge of third-party sources; see manifest.sources[] for the exact list, URL and licence per build.
- models.dev (MIT) and LiteLLM (MIT): keep attribution to both projects when redistributing.
- OpenRouter: redistribution terms are unverified (
redistribution: "unverified"). Values whose provenance (sources) isopenrouterare its data (prices, per-provider endpoints, quantization, performance, supported parameters, ZDR). - Show "last refreshed" from
manifest.sources[].fetched_at, per source.
--exclude-source openrouter
npm run api -- --exclude-source openrouter [--out <dir>] (repeatable / comma separated) produces a build without OpenRouter data:
- endpoints with no remaining source reference (OpenRouter-only) are dropped; models and providers that only existed through it are dropped;
- fields whose
sourcesentry is excluded becomenull(unknown, or[]forenv_vars/aliases/modalities) and thesourcesentry is removed;performance,alts,source_refs,snapshots, andflagsthat mention it are removed; - the value of a losing source is never promoted into a field (alt values are partial and not comparable);
provider_count,provider_count_all,cheapest,context,max_output,capabilities,type,endpoint_ids,aliases, and provider/creator counts are recomputed from what remains;manifest.sourcesomits the source andexcluded_sourceslists it.
Known remainder: models.dev lists an *OpenRouter provider* (provider_id: "openrouter", provider_model_id: "openrouter/..."). Those rows come from models.dev, not the OpenRouter API, so they stay (identity strings only; their OpenRouter-sourced prices are nulled). Endpoint ids that embed an OpenRouter-reported region/quantization variant (#...) keep the id for stability.
Other derived products (data/derived/)
changes.json (recent history events by day), feed.xml (Atom), per-model and per-plan Atom feeds feeds/models/<creator>/<slug>.xml and feeds/plans/<provider>/<slug>.xml (+ feeds/index.json), history/ (the price-history series above), stats.json. See "Derived products" in docs/SCHEMA.md.