diff --git a/README.md b/README.md index da26c6fdb..c43d9e45a 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,23 @@ curl https://models.dev/api.json Use the **Model ID** field to do a lookup on any model; it's the identifier used by [AI SDK](https://ai-sdk.dev/). +The API also provides several specialized endpoints for faster access: + +| Endpoint | Description | +|---|---| +| `/api/models.json` | Lightweight index of all model IDs with capabilities and provider list | +| `/api/{provider}.json` | Full data for a single provider (e.g., `/api/anthropic.json`) | +| `/api/providers.json` | Lightweight list of all providers with name, model count, and docs link | +| `/api/schema.json` | JSON Schema describing the full data shape | + +```bash +# Find which providers offer a specific model +curl https://models.dev/api/models.json | jq '."claude-sonnet-4-5".providers' + +# Get full pricing for one provider +curl https://models.dev/api/anthropic.json +``` + ### Logos Provider logos are available as SVG files: diff --git a/packages/function/src/worker.ts b/packages/function/src/worker.ts index beb8477b7..ee52efc30 100644 --- a/packages/function/src/worker.ts +++ b/packages/function/src/worker.ts @@ -75,6 +75,15 @@ export default { if (url.pathname === "/api.json") { url.pathname = "/_api.json"; + } else if (url.pathname === "/api/models.json") { + url.pathname = "/_api/models.json"; + } else if (url.pathname === "/api/schema.json") { + url.pathname = "/_api/schema.json"; + } else if (url.pathname === "/api/providers.json") { + url.pathname = "/_api/providers.json"; + } else if (url.pathname.match(/^\/api\/[a-z0-9][a-z0-9._-]+\.json$/)) { + const providerId = url.pathname.replace("/api/", "").replace(".json", ""); + url.pathname = `/_api/${providerId}.json`; } else if ( url.pathname === "/" || url.pathname === "/index.html" || diff --git a/packages/web/script/build.ts b/packages/web/script/build.ts index daad95cc7..2e6d5f02e 100755 --- a/packages/web/script/build.ts +++ b/packages/web/script/build.ts @@ -48,3 +48,155 @@ await Bun.write("./dist/api.json", JSON.stringify(Providers)); await $`mv ./dist/index.html ./dist/_index.html`; await $`mv ./dist/api.json ./dist/_api.json`; + +// Build per-provider API files +await fs.mkdir("./dist/_api", { recursive: true }); +const providerEntries = Object.entries(Providers); +for (const [providerId, provider] of providerEntries) { + await Bun.write( + `./dist/_api/${providerId}.json`, + JSON.stringify({ [providerId]: provider }), + ); +} + +// Build model index: model ID → { providers[], capabilities } +const modelIndex: Record< + string, + { + name: string; + providers: string[]; + tool_call: boolean; + reasoning: boolean; + modalities: { input: string[]; output: string[] }; + open_weights: boolean; + structured_output?: boolean; + } +> = {}; +for (const [providerId, provider] of providerEntries) { + for (const [modelId, model] of Object.entries(provider.models)) { + if (!modelIndex[modelId]) { + modelIndex[modelId] = { + name: model.name, + providers: [], + tool_call: model.tool_call, + reasoning: model.reasoning, + modalities: model.modalities, + open_weights: model.open_weights, + structured_output: model.structured_output, + }; + } + // sanity check: conflicting capabilities across providers + const entry = modelIndex[modelId]; + if (entry.name !== model.name) { + console.warn( + `Model name mismatch for "${modelId}": "${entry.name}" vs "${model.name}" (${providerId})`, + ); + } + entry.providers.push(providerId); + } +} +await Bun.write("./dist/_api/models.json", JSON.stringify(modelIndex)); + +// Build JSON Schema for the full API shape +const providerNames = providerEntries + .sort(([, a], [, b]) => a.name.localeCompare(b.name)) + .map(([id]) => id); + +const schema = { + $schema: "https://json-schema.org/draft/2020-12/schema", + $id: "https://models.dev/api/schema.json", + title: "Models.dev", + description: "Open-source database of AI model specifications and pricing", + type: "object", + additionalProperties: { + $ref: "#/$defs/Provider", + }, + $defs: { + Provider: { + type: "object", + properties: { + id: { type: "string", enum: providerNames }, + name: { type: "string" }, + env: { type: "array", items: { type: "string" } }, + npm: { type: "string" }, + api: { type: "string" }, + doc: { type: "string", format: "uri" }, + models: { + type: "object", + additionalProperties: { $ref: "#/$defs/Model" }, + }, + }, + required: ["id", "name", "env", "npm", "doc", "models"], + }, + Model: { + type: "object", + properties: { + id: { type: "string" }, + name: { type: "string" }, + family: { type: "string" }, + attachment: { type: "boolean" }, + reasoning: { type: "boolean" }, + tool_call: { type: "boolean" }, + structured_output: { type: "boolean" }, + temperature: { type: "boolean" }, + open_weights: { type: "boolean" }, + release_date: { type: "string" }, + last_updated: { type: "string" }, + knowledge: { type: "string" }, + modalities: { + type: "object", + properties: { + input: { + type: "array", + items: { type: "string", enum: ["text", "audio", "image", "video", "pdf"] }, + }, + output: { + type: "array", + items: { type: "string", enum: ["text", "audio", "image", "video", "pdf"] }, + }, + }, + }, + cost: { $ref: "#/$defs/Cost" }, + limit: { $ref: "#/$defs/Limit" }, + status: { type: "string", enum: ["alpha", "beta", "deprecated"] }, + }, + required: ["id", "name", "modalities", "limit"], + }, + Cost: { + type: "object", + properties: { + input: { type: "number" }, + output: { type: "number" }, + reasoning: { type: "number" }, + cache_read: { type: "number" }, + cache_write: { type: "number" }, + input_audio: { type: "number" }, + output_audio: { type: "number" }, + context_over_200k: { $ref: "#/$defs/Cost" }, + }, + }, + Limit: { + type: "object", + properties: { + context: { type: "integer" }, + input: { type: "integer" }, + output: { type: "integer" }, + }, + required: ["context", "output"], + }, + }, +}; +await Bun.write("./dist/_api/schema.json", JSON.stringify(schema, null, 2)); + +// Build provider list: lightweight index of all providers +const providerList = providerEntries + .sort(([, a], [, b]) => a.name.localeCompare(b.name)) + .map(([id, provider]) => ({ + id, + name: provider.name, + doc: provider.doc, + model_count: Object.keys(provider.models).length, + npm: provider.npm, + api: provider.api, + })); +await Bun.write("./dist/_api/providers.json", JSON.stringify(providerList)); diff --git a/packages/web/src/render.tsx b/packages/web/src/render.tsx index a1bdfcc2f..2a287ad00 100644 --- a/packages/web/src/render.tsx +++ b/packages/web/src/render.tsx @@ -519,6 +519,54 @@ export const Rendered = renderToString( .
++ Lightweight index of all model IDs with their capabilities and which + providers offer each model. Useful for finding which providers carry a + model without downloading the full dataset. +
+
+ curl{" "}
+ https://models.dev/api/models.json
+ {" | jq '.\"claude-sonnet-4-5\".providers'"}
+
+
+ Full data for a single provider. Replace {`{provider}`}{" "}
+ with the Provider ID.
+
+ curl{" "}
+
+ https://models.dev/api/anthropic.json
+
+
+ + Lightweight list of all providers with their display name, model + count, and documentation link. Useful for finding the right{" "} + Provider ID. +
+
+ curl{" "}
+
+ https://models.dev/api/providers.json
+
+
+ JSON Schema describing the full API data shape.
+
+ curl{" "}
+ https://models.dev/api/schema.json
+
+
Provider logos are available at /logos/{`{provider}`}.svg{" "}