From 19e949fff285cd172f19913e8a00c65dc5611682 Mon Sep 17 00:00:00 2001
From: Alexis Okuwa
Date: Sat, 9 May 2026 12:05:26 -0500
Subject: [PATCH 1/2] feat: add per-provider, model index, and schema API
endpoints
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Generate per-provider JSON files, a lightweight model index (model ID →
providers + capabilities), and a JSON Schema at build time. Update the
Cloudflare Worker to route /api/models.json, /api/schema.json, and
/api/{provider}.json. Document all new endpoints in the site and README.
---
README.md | 16 ++++
packages/function/src/worker.ts | 7 ++
packages/web/script/build.ts | 139 ++++++++++++++++++++++++++++++++
packages/web/src/render.tsx | 34 ++++++++
4 files changed, 196 insertions(+)
diff --git a/README.md b/README.md
index da26c6fdb..3c95deda2 100644
--- a/README.md
+++ b/README.md
@@ -24,6 +24,22 @@ 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/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..df4d0b6dc 100644
--- a/packages/function/src/worker.ts
+++ b/packages/function/src/worker.ts
@@ -75,6 +75,13 @@ 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.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..b4406eec6 100755
--- a/packages/web/script/build.ts
+++ b/packages/web/script/build.ts
@@ -48,3 +48,142 @@ 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));
diff --git a/packages/web/src/render.tsx b/packages/web/src/render.tsx
index a1bdfcc2f..82ad8908c 100644
--- a/packages/web/src/render.tsx
+++ b/packages/web/src/render.tsx
@@ -519,6 +519,40 @@ export const Rendered = renderToString(
.
+ Model Index
+
+ 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.
+
+
+ Per-Provider
+
+ Full data for a single provider. Replace {`{provider}`}{" "}
+ with the Provider ID.
+
+
+ Schema
+ JSON Schema describing the full API data shape.
+
Logos
Provider logos are available at /logos/{`{provider}`}.svg{" "}
From 83c2ddda8c5b070f5797536d19405a69396ceb5f Mon Sep 17 00:00:00 2001
From: Alexis Okuwa
Date: Sat, 9 May 2026 12:21:20 -0500
Subject: [PATCH 2/2] feat: add /api/providers.json endpoint
Lightweight provider index (20 KB, 118 providers) with id, name, doc,
model_count, npm, and api fields. Helps users find the correct Provider ID
when providers have similar names. Update site modal and README.
---
README.md | 1 +
packages/function/src/worker.ts | 2 ++
packages/web/script/build.ts | 13 +++++++++++++
packages/web/src/render.tsx | 14 ++++++++++++++
4 files changed, 30 insertions(+)
diff --git a/README.md b/README.md
index 3c95deda2..c43d9e45a 100644
--- a/README.md
+++ b/README.md
@@ -30,6 +30,7 @@ The API also provides several specialized endpoints for faster access:
|---|---|
| `/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
diff --git a/packages/function/src/worker.ts b/packages/function/src/worker.ts
index df4d0b6dc..ee52efc30 100644
--- a/packages/function/src/worker.ts
+++ b/packages/function/src/worker.ts
@@ -79,6 +79,8 @@ export default {
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`;
diff --git a/packages/web/script/build.ts b/packages/web/script/build.ts
index b4406eec6..2e6d5f02e 100755
--- a/packages/web/script/build.ts
+++ b/packages/web/script/build.ts
@@ -187,3 +187,16 @@ const schema = {
},
};
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 82ad8908c..2a287ad00 100644
--- a/packages/web/src/render.tsx
+++ b/packages/web/src/render.tsx
@@ -545,6 +545,20 @@ export const Rendered = renderToString(
+ Providers
+
+ Lightweight list of all providers with their display name, model
+ count, and documentation link. Useful for finding the right{" "}
+ Provider ID.
+
+
Schema
JSON Schema describing the full API data shape.