llm-models
v1.5.1
Published
Fetch latest LLM models from OpenRouter and models.dev APIs
Maintainers
Readme
llm-models
Fetch latest LLM models from OpenRouter and models.dev APIs.
Validates API responses with Zod schemas to detect breaking changes. Provides smart wrapper functions for model discovery, comparison, cost estimation, recommendations, statistics, and provider info. Features local caching, MCP server integration, multiple output formats, and a fluent query builder API.
Installation
Homebrew
brew install maxgfr/tap/llm-modelsnpm
Requires Node.js >= 22.12.
npm install -g llm-modelsBinary
Download the latest binary from GitHub Releases.
| Platform | Binary |
|----------|--------|
| macOS ARM64 | llm-models-macos-arm64 |
| macOS Intel | llm-models-macos-x64 |
| Linux x86_64 | llm-models-linux-x64 |
| Linux ARM64 | llm-models-linux-arm64 |
| Windows x64 | llm-models-win-x64.exe |
CLI Usage
Smart Commands
Find models
Discover models across both APIs with smart filtering:
# Find cheapest reasoning models
llm-models find -C reasoning --sort cost_input -n 10
# Find models accepting images, sorted by cost
llm-models find -m image --sort cost_input
# Find OpenAI models with large context
llm-models find -p openai --min-context 200000
# Filter by family
llm-models find -f gpt --sort cost_input
# Filter by status
llm-models find --status active -C reasoning
# Search by name
llm-models find -s "claude" --sort cost_input
# Sort by cost-effectiveness (context per dollar)
llm-models find --sort value --desc -n 10
# Count matching models
llm-models find -C reasoning --count
# Output IDs only (for piping)
llm-models find -p openai --ids-only
# Output as CSV or Markdown
llm-models find -p anthropic --format csv
llm-models find -p anthropic --format markdown
# Output as JSON
llm-models find -p google --jsonTable and Markdown output stop at 20 rows (or defaults.limit from the config) and the table ends
with (20 of N models shown, -n 0 to show all). JSON, CSV, --count, --ids-only and --field
return every match unless you pass -n. -m/--modality matches what a model accepts: -m image
finds models that read images, not image generators.
Compare models
Side-by-side model comparison with context, output limit, costs, knowledge cutoff, and capabilities:
llm-models compare openai/gpt-4o anthropic/claude-sonnet-4 google/gemini-2.0-flash
llm-models compare gpt-4o claude-sonnet-4-5 # bare ids work tooModel info
Detailed information about a single model (description, costs, capabilities, parameters, tokenizer):
llm-models info openai/gpt-4o
llm-models info gpt-4oinfo, compare and cost look models up the same way. An exact id, an exact trailing segment
(gpt-4o) or either one case-insensitively wins outright, even when dozens of other ids contain the
text; ties go to the vendor's own entry. OpenRouter's spelling of a merged model
(anthropic/claude-sonnet-4.5) works too. A plain substring of an id or name is accepted only when a
single model matches; otherwise the command lists up to 10 candidates and exits 1.
Resolve a model against its endpoint
The same model id is published under dozens of providers with different limits — OpenRouter routes
deepseek-chat at 163,840 context while DeepSeek's own API serves 1,000,000. resolve takes the
base URL you actually call and answers for that endpoint:
# Z.AI coding plan, not the OpenRouter reseller entry
llm-models resolve glm-5.3 --endpoint https://api.z.ai/api/anthropic
# Scope by provider id instead
llm-models resolve deepseek-chat --provider deepseekThe endpoint is matched against each provider's documented API host first. Vendors that document no
API URL on models.dev (Anthropic, OpenAI, Google, xAI, Mistral, ...) are matched by name in the host
instead, so --endpoint https://api.anthropic.com answers with anthropic/claude-sonnet-5 rather
than a reseller's copy. Without --endpoint or --provider, ties between identical ids go to the
vendor's own entry.
Exits 1 when nothing matches, so it composes in scripts.
Pick the latest model per tier
latest answers "which model should I be on?" for an endpoint: the newest flagship of the current
generation (opus) and its fast sibling (sonnet and haiku), out of the provider's own catalogue.
Batch and free routes, moving ~vendor/… aliases, deprecated entries, models that cannot call tools
and vision / speech / preview / experimental variants are left out:
llm-models latest --endpoint https://api.z.ai/api/anthropic
# Provider: zai
# opus: zai/glm-5.3 (1M context, 131.1K out)
# sonnet: zai/glm-5.3-flash (1M context, 131.1K out)
# haiku: zai/glm-5.3-flash (1M context, 131.1K out)
# One tab-separated line per tier, for scripts
llm-models latest --endpoint https://api.z.ai/api/anthropic \
--field tier,id,context_length,output_limit
# opus zai/glm-5.3 1000000 131072
# sonnet zai/glm-5.3-flash 1000000 131072
# haiku zai/glm-5.3-flash 1000000 131072
# Narrow a multi-vendor catalogue; Claude tiers map literally
llm-models latest --endpoint https://openrouter.ai/api --filter anthropic/ --tier sonnet --field id
# openrouter/anthropic/claude-sonnet-5A fast variant only pairs with a flagship of the same major version, so a generation that ships
no fast model answers with the flagship on every tier. --filter keeps ids containing the token
(or families starting with it); --tier prints one tier; --json prints { provider, picks }.
One of --endpoint or --provider is required, and the command exits 1 when nothing is eligible.
Script-friendly output (--field)
info, find, resolve and latest accept --field to print bare, tab-separated values instead of a
formatted block — no JSON parsing needed. Dotted paths work. A field missing on one model renders
as an empty column; when every requested field is missing the command explains on stderr and exits 1:
llm-models info z-ai/glm-5.3 --field context_length
# 1048576
llm-models resolve glm-5.3 --endpoint https://api.z.ai/api/anthropic \
--field context_length,output_limit
# 1000000 131072
llm-models find -p anthropic --field id,cost.inputCost estimation
Estimate costs for specific token volumes:
# Basic cost estimation
llm-models cost openai/gpt-4o anthropic/claude-sonnet-4 -i 1M -o 100K
# With daily/monthly projections
llm-models cost openai/gpt-4o -i 1M -o 100K --daily 1000
# Using a workload profile (supplies -i/-o; pass either flag to override it)
llm-models cost openai/gpt-4o -P chatbot --daily 5000Costs from $0.01 up show cents; smaller amounts keep two significant digits ($0.0015), so only
a genuinely free request reads $0.00. --daily implies a monthly figure (×30) and --monthly a
daily one (÷30); given both, each is used as stated. JSON and table output always agree.
Cheapest models
Find the cheapest models matching your criteria:
llm-models cheapest -C reasoning --min-context 100000cheapest and recommend never suggest a deprecated model.
Recommendations
Get model recommendations for a specific use case:
# List available use cases
llm-models recommend --list
# Get recommendations
llm-models recommend reasoning
llm-models recommend code-gen --max-cost 5
llm-models recommend vision -n 5Available use cases: code-gen, vision, cheap-chatbot, reasoning, long-context, open-source, audio, tool-use.
Statistics
Get aggregate statistics about the LLM model landscape:
llm-models stats
llm-models stats --jsonShows: total models/providers, top providers, capabilities distribution, modality distribution, cost percentiles, context length stats, newest models.
Diff
Show what changed since the previous diff run:
llm-models diff # first run: saves a baseline
llm-models diff # later: changes since the run aboveShows: new models added, models removed, price changes, status changes. diff keeps its own
snapshot (snapshot.json in the cache directory), so other commands refreshing the API cache never
reset the baseline. With --no-cache it compares but does not save a new snapshot.
Provider info
Get provider details (SDK package, env vars, API endpoint, models):
# List all providers
llm-models provider --list
# Get details for a specific provider
llm-models provider openaiRaw API Commands
OpenRouter
llm-models openrouter # All models (JSON)
llm-models openrouter --provider google # Filter by provider
llm-models openrouter --search "claude" # Search
llm-models openrouter --count # Count onlymodels.dev
llm-models models-dev # All providers (JSON)
llm-models models-dev --provider openai # Filter by provider
llm-models models-dev --count # Count onlyUtility Commands
Cache management
llm-models cache # Show cache info
llm-models cache clear # Clear cacheAPI responses are cached for 1 hour (configurable, see below) in ~/.cache/llm-models/. Use
--no-cache to bypass, and --verbose to see each source's hit/miss and age on stderr. Requests
time out after 15 seconds. When an API is down, an expired cached copy is used if there is one;
otherwise the command carries on with the other source and says so on stderr. A read-only cache
directory never fails a command.
Configuration
llm-models config # Show current config
llm-models config init # Create default config file
llm-models config path # Show config file path
llm-models config profiles # List workload profilesConfig file locations: .llm-modelsrc (project) or ~/.config/llm-models/config.json (global).
The file is validated; an invalid one is reported on stderr and ignored.
{
"cache": { "enabled": true, "ttl": 3600000 },
"defaults": { "format": "table", "limit": 20, "sort": "cost_input", "desc": false },
"profiles": { "batch": { "input": "200K", "output": "20K" } }
}| Key | Effect |
|-----|--------|
| cache.enabled | false disables the cache like --no-cache |
| cache.ttl | Freshness of cached API responses, in milliseconds |
| defaults.format | Output format when neither --json nor --format is given |
| defaults.limit | find rows in table/Markdown output (0 = all) |
| defaults.sort / defaults.desc | find sort when --sort / --desc are absent |
| profiles | Extra workload profiles for cost -P |
Shell completions
# Bash
llm-models completion bash >> ~/.bashrc
# Zsh
llm-models completion zsh >> ~/.zshrc
# Fish
llm-models completion fish > ~/.config/fish/completions/llm-models.fishThe scripts are generated from the CLI definition: every command and option, plus the allowed values of enum options (capabilities, sort fields, statuses, formats, tiers, use cases, profiles).
MCP Server
Start an MCP (Model Context Protocol) server for AI agent integration:
llm-models mcpExposes 11 tools:
| Tool | Purpose |
|------|---------|
| find_models | Filter (provider, capability, modality, cost, context, search, status, family) and sort; 20 results by default |
| get_model | One model's details, looked up like info |
| resolve_model | A model as served by an endpoint or provider, like resolve |
| latest_models | Newest flagship and fast model per tier, like latest |
| compare_models | Side-by-side comparison |
| estimate_cost | Token cost estimates |
| cheapest_models | Cheapest non-deprecated models |
| recommend_models | Non-deprecated recommendations for a use case |
| get_provider | Provider details and models |
| list_providers | Providers with model counts (without their model lists) |
| get_stats | Market statistics |
Enum parameters (capability, status, sort, use case) are validated, so a typo is an error rather than an empty result.
Global Options
| Option | Description |
|--------|-------------|
| --no-cache | Disable cache for this request |
| --quiet | Suppress informational messages |
| --verbose | Show debug info (timing, cache status) |
| -V, --version | Show version |
| -h, --help | Show help |
Find Options
| Option | Description |
|--------|-------------|
| -p, --provider <id> | Filter by provider |
| -C, --capability <cap> | Filter: reasoning, tool_call, structured_output, open_weights, attachment |
| -m, --modality <mod> | Filter by input modality (image, audio, video, pdf); output modalities are ignored |
| --max-cost <n> | Max input cost ($/million tokens) |
| --max-cost-output <n> | Max output cost ($/million tokens) |
| --min-context <n> | Min context window |
| -s, --search <term> | Search by name or ID |
| --status <status> | Filter: active, deprecated, beta, alpha |
| -f, --family <name> | Filter by model family (e.g. gpt, claude, gemini) |
| --sort <field> | Sort: cost_input, cost_output, context_length, release_date, name, knowledge_cutoff, value |
| --desc | Sort descending (models without a value for the field always sort last); needs --sort or defaults.sort |
| -n, --limit <n> | Max results; 0 = all (default: 20 in table/Markdown output, all otherwise) |
| -c, --count | Show model count only |
| --ids-only | Output model IDs only, one per line |
| --field <names> | Output only these fields, tab-separated, one line per model |
| --json | Raw JSON output |
| --format <fmt> | Output format: table, json, csv, markdown |
Resolve Options
| Option | Description |
|--------|-------------|
| -e, --endpoint <url> | Base URL being called — disambiguates resellers by host |
| -p, --provider <id> | Provider ID to scope the lookup to |
| --field <names> | Output only these fields, tab-separated |
| --json | Raw JSON output |
Cost Options
| Option | Description |
|--------|-------------|
| -i, --input <n> | Input tokens (supports K/M suffix); required unless -P is given |
| -o, --output <n> | Output tokens (supports K/M suffix); required unless -P is given |
| --daily <n> | Daily requests for the projection (monthly = ×30 unless --monthly is given) |
| --monthly <n> | Monthly requests for the projection (daily = ÷30 unless --daily is given) |
| -P, --profile <name> | Use workload profile (chatbot, code-gen, rag, summarization, translation) instead of -i/-o |
Library Usage
import {
fetchUnifiedModels,
findModels,
compareModels,
getProvider,
listProviders,
estimateCost,
cheapestModels,
getStats,
recommendModels,
listUseCases,
diffModels,
findModel,
resolveModel,
latestModels,
query,
setCacheEnabled,
clearCache,
} from "llm-models";
// Get all models (unified from both APIs)
const models = await fetchUnifiedModels();
// Find cheapest reasoning models
const cheap = await findModels({
filter: { capability: "reasoning" },
sort: "cost_input",
limit: 5,
});
// Look one model up the way `info` does (throws "not found" / "Ambiguous")
const gpt4o = findModel(models, "gpt-4o"); // openai/gpt-4o
// Resolve a model against the endpoint that actually serves it
const resolved = await resolveModel("glm-5.3", {
endpoint: "https://api.z.ai/api/anthropic",
});
// resolved.model.context_length === 1_000_000, resolved.matchedBy === "endpoint"
// Newest flagship and fast model per tier for that endpoint
const latest = await latestModels({ endpoint: "https://api.z.ai/api/anthropic" });
// latest.picks -> [{ tier: "opus", model: glm-5.3 }, { tier: "sonnet", model: glm-5.3-flash }, ...]
// Fluent query builder
const results = await query()
.provider("openai")
.capability("reasoning")
.maxCost(5)
.sortBy("cost_input")
.limit(10)
.execute();
// Compare models side by side
const comparison = await compareModels(["openai/gpt-4o", "anthropic/claude-sonnet-4"]);
// Get provider info (SDK, env vars, docs, models)
const openai = await getProvider("openai");
console.log(openai.npm); // "@ai-sdk/openai"
// Estimate costs
const costs = await estimateCost({
modelIds: ["openai/gpt-4o"],
inputTokens: 1_000_000,
outputTokens: 100_000,
});
// Get recommendations for a use case
const recs = await recommendModels("code-gen", { maxCost: 5, limit: 5 });
// Get market statistics
const stats = await getStats();
// Detect changes since the previous diffModels() call (saves a new snapshot)
const diff = await diffModels();
// Cache control
setCacheEnabled(false); // Disable caching
clearCache(); // Clear cache directoryRaw API Access
import { fetchOpenRouterModels, fetchModelsDevModels } from "llm-models";
const openrouter = await fetchOpenRouterModels();
const modelsDev = await fetchModelsDevModels();Zod Schemas
All schemas are exported for custom validation:
import {
UnifiedModelSchema,
OpenRouterResponseSchema,
ModelsDevResponseSchema,
LlmModelsConfigSchema, // the config file format
} from "llm-models";fetchOpenRouterModels and fetchModelsDevModels validate entry by entry: an entry that no longer
matches its schema is skipped (counted under --verbose) rather than failing the whole fetch.
Types
All types are inferred from Zod schemas:
import type {
UnifiedModel,
NormalizedCost,
Capabilities,
ProviderInfo,
ModelFilter,
ModelSortField,
CostEstimate,
ModelComparison,
LlmModelsConfig,
} from "llm-models";GitHub Action
Use llm-models in CI/CD to check model costs and status:
- uses: maxgfr/llm-models@v1
with:
command: cost
args: "openai/gpt-4o anthropic/claude-sonnet-4 -i 1M -o 100K"
max-budget: "10"
fail-on-deprecated: "true"Data Sources
- OpenRouter: https://openrouter.ai/api/v1/models
- models.dev: https://models.dev/api.json
The two describe different endpoints for the same model id, so a merged entry takes its
context_length and output_limit as a pair from one source (models.dev first, since it is
provider-documented) rather than mixing them. Use resolve --endpoint when you need the limits of
the endpoint you actually call.
The two APIs sometimes spell the same model differently (OpenRouter's anthropic/claude-opus-4.5
is models.dev's anthropic/claude-opus-4-5). Ids that differ only by case or . versus - are
merged when the pairing is one-to-one. The merged model keeps the models.dev id, the id the
provider's own API accepts, and OpenRouter's id is kept in openrouter_id, which every model
OpenRouter lists carries. Models only OpenRouter lists are dated from its created timestamp
(release_date, UTC), and those with an expiration_date are marked deprecated. On a merged
model, models.dev's date and status win.
License
MIT
