API
The app's server function returns the current model catalogue from models.dev (MIT licence) as one compact JSON table. It is free, uses no credits, and needs a SkillSafe app token for this app. The bundled snapshot is a plain static file anyone can fetch.
1. Get a token
Exchange your SkillSafe API key for an app token (valid 30 days). Guest tokens are refused by the live endpoint.
curl -s -X POST https://api.skillsafe.ai/v1/app-api/authorize \
-H "Authorization: Bearer YOUR_SKILLSAFE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"slug":"model-prices"}'
# -> {"ok":true,"data":{"token":"aut_...","expires_at":"..."}}
import requests
r = requests.post("https://api.skillsafe.ai/v1/app-api/authorize",
headers={"Authorization": "Bearer YOUR_SKILLSAFE_API_KEY"},
json={"slug": "model-prices"})
token = r.json()["data"]["token"]
const r = await fetch("https://api.skillsafe.ai/v1/app-api/authorize", {
method: "POST",
headers: { Authorization: "Bearer YOUR_SKILLSAFE_API_KEY", "Content-Type": "application/json" },
body: JSON.stringify({ slug: "model-prices" })
});
const token = (await r.json()).data.token;
2. Fetch the live catalogue
GET https://model-prices.skillsafe.ai/api/models with Authorization: Bearer <token>. About 1.5 MB of JSON (much less compressed); cache it for a few minutes rather than calling it per request.
curl -s https://model-prices.skillsafe.ai/api/models \ -H "Authorization: Bearer YOUR_APP_TOKEN" -o models.json
import requests
cat = requests.get("https://model-prices.skillsafe.ai/api/models",
headers={"Authorization": "Bearer " + token}).json()
# cost of one request with 1,500 input and 400 output tokens, per listing
for row in cat["rows"]:
host, model, pin, pout = row[0], row[1], row[6], row[7]
if pin is not None and pout is not None:
print(host, model, (1500 * pin + 400 * pout) / 1e6)
const res = await fetch("https://model-prices.skillsafe.ai/api/models", {
headers: { Authorization: "Bearer " + token }
});
const cat = await res.json();
const cheapest = cat.rows
.filter((r) => r[6] != null && r[7] != null && r[20] === "")
.map((r) => ({ host: r[0], model: r[1], usd: (1500 * r[6] + 400 * r[7]) / 1e6 }))
.sort((a, b) => a.usd - b.usd)
.slice(0, 10);
No token? The bundled snapshot has the same shape: GET https://model-prices.skillsafe.ai/snapshot.json.
3. The response
{
"v": 1,
"source": "models.dev",
"license": "MIT",
"fetched_at": "2026-10-31T06:16:00.000Z",
"mods": ["text", "image", "audio", "video", "pdf"],
"providers": { "anthropic": "Anthropic", "openai": "OpenAI", ... },
"rows": [ [host, id, name, maker, canonical_id, family,
input, output, cache_read, cache_write, input_over_200k, output_over_200k,
context, max_output, input_mods, output_mods, flags,
released, knowledge, status, kind], ... ]
}
| Index | Field | Meaning |
|---|---|---|
| 0, 1, 2 | host, id, name | The API host's id (a key of providers), its model id, and display name. |
| 3, 4, 5 | maker, canonical_id, family | Who made the model, a cross-host id such as anthropic/claude-sonnet-4-5 (empty when unknown), and the model family. |
| 6 to 11 | prices | USD per million tokens: input, output, cached-input read, cache write, and the input and output prices above 200K prompt tokens. null when the host lists none. |
| 12, 13 | context, max_output | Token limits; null when unknown. |
| 14, 15 | input_mods, output_mods | Bit masks over mods: bit 0 text, 1 image, 2 audio, 3 video, 4 pdf. |
| 16 | flags | 1 reasoning, 2 tool calls, 4 open weights, 8 structured output, 16 file attachments, 32 sold by its own maker. |
| 17, 18, 19 | released, knowledge, status | Release date and knowledge cutoff (YYYY-MM or YYYY-MM-DD), and "", beta, alpha or deprecated. |
| 20 | kind | "" for language models; otherwise image, video, speech, transcription, embedding, reranking or realtime. |
4. Errors
| HTTP | Body | What to do |
|---|---|---|
| 401 | sign-in required | Missing, expired or guest token. Get a new token (step 1). |
| 429 | {"error":{"code":"upstream_429"}} | The catalogue source is rate limiting. Wait a minute. |
| 502 | upstream_unreachable, upstream_<status>, upstream_parse, upstream_empty | The source did not answer usefully. Retry later or use snapshot.json. |
| 404 | not_found | Only /api/models and /api/health exist. |