model-prices 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- model_prices-0.0.1/.gitignore +17 -0
- model_prices-0.0.1/LICENSE +21 -0
- model_prices-0.0.1/PKG-INFO +128 -0
- model_prices-0.0.1/README.md +116 -0
- model_prices-0.0.1/pyproject.toml +23 -0
- model_prices-0.0.1/src/model_prices/__init__.py +606 -0
- model_prices-0.0.1/src/model_prices/__main__.py +3 -0
- model_prices-0.0.1/src/model_prices/backfill.py +211 -0
- model_prices-0.0.1/src/model_prices/checks.py +419 -0
- model_prices-0.0.1/src/model_prices/data/LICENSE.models.dev +21 -0
- model_prices-0.0.1/src/model_prices/data/aliases.toml +19 -0
- model_prices-0.0.1/src/model_prices/data/corrections.toml +186 -0
- model_prices-0.0.1/src/model_prices/data/plans.toml +122 -0
- model_prices-0.0.1/src/model_prices/data/prices.json +5345 -0
- model_prices-0.0.1/src/model_prices/data/research_corrections.toml +632 -0
- model_prices-0.0.1/src/model_prices/data/schedules.toml +65 -0
- model_prices-0.0.1/src/model_prices/genai_prices.py +103 -0
- model_prices-0.0.1/tests/fixtures/LICENSE.genai-prices +21 -0
- model_prices-0.0.1/tests/fixtures/anthropic.md +19 -0
- model_prices-0.0.1/tests/fixtures/deepseek-zh.html +18 -0
- model_prices-0.0.1/tests/fixtures/deepseek.html +18 -0
- model_prices-0.0.1/tests/fixtures/genai-prices.json +2242 -0
- model_prices-0.0.1/tests/fixtures/google.md +60 -0
- model_prices-0.0.1/tests/fixtures/openai.md +17 -0
- model_prices-0.0.1/tests/fixtures/xai.md +12 -0
- model_prices-0.0.1/tests/fixtures/zai.md +22 -0
- model_prices-0.0.1/tests/test_checks.py +152 -0
- model_prices-0.0.1/tests/test_data.py +70 -0
- model_prices-0.0.1/tests/test_prices.py +388 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
.venv/
|
|
3
|
+
dist/
|
|
4
|
+
/.aop/
|
|
5
|
+
# Copies of third-party pages cited by price research; kept locally, not published.
|
|
6
|
+
/research/sources/
|
|
7
|
+
/research/price-dates/findings/*/sources/
|
|
8
|
+
/research/price-dates/tasks/
|
|
9
|
+
/research/price-dates/batch.toml
|
|
10
|
+
/research/price-dates/*.log
|
|
11
|
+
/research/price-dates/run-when-archive-up.sh
|
|
12
|
+
/research/price-dates/unit.*
|
|
13
|
+
/research/price-dates/extract-deps/
|
|
14
|
+
/research/price-dates/run-reviews.sh
|
|
15
|
+
/research/price-dates/*.out
|
|
16
|
+
/research/price-dates/review-batch.toml
|
|
17
|
+
/research/price-dates/review-tasks/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 wakamex
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: model-prices
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: LLM API prices, current and historical, from models.dev with sourced corrections
|
|
5
|
+
Project-URL: Homepage, https://github.com/wakamex/model-prices
|
|
6
|
+
Project-URL: Source, https://github.com/wakamex/model-prices
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
License-File: src/model_prices/data/LICENSE.models.dev
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# model-prices
|
|
14
|
+
|
|
15
|
+
LLM API prices, today's or as they were at any point in time. Pricing past usage at today's rates misstates it whenever a price changed: GPT-5.6 Sol cost $5/$30 per million input/output tokens until August 21, 2026, and $4/$20 for the three months after. This package answers "what did this request cost at API prices when it ran?"
|
|
16
|
+
|
|
17
|
+
Status: work in progress. Versions before 0.1 may change the data format and API without notice.
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
import model_prices
|
|
21
|
+
|
|
22
|
+
model_prices.cost(
|
|
23
|
+
"gpt-5.6-sol",
|
|
24
|
+
input_tokens=12_000, # uncached input only
|
|
25
|
+
cache_read_tokens=180_000,
|
|
26
|
+
output_tokens=900,
|
|
27
|
+
at="2026-08-20T14:00:00Z",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
model_prices.rates("claude-opus-5-5") # current rates, per million tokens
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
model-prices rate gpt-5.6-sol --at 2026-08-20
|
|
35
|
+
model-prices rate "Gemini 3.5 Flash (High)" --json
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Install
|
|
39
|
+
|
|
40
|
+
Requires Python 3.11 or newer.
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
uv add model-prices
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Where the prices come from
|
|
47
|
+
|
|
48
|
+
Prices come from the git history of [models.dev](https://github.com/sst/models.dev), an open catalog of model metadata that records each model's price in one TOML file per provider. Every commit on its main branch that changed a tracked model's price becomes an entry effective from that commit's time. The tracked providers are the labs that sell their own models: Alibaba, Anthropic, DeepSeek, Google, Moonshot AI, OpenAI, xAI, and Z.ai.
|
|
49
|
+
|
|
50
|
+
models.dev sometimes records a change days after the provider made it, or lists a wrong price for a while. `corrections.toml` overrides those periods, and every correction cites its source. `research_corrections.toml` adds corrections generated from agent research into every recorded change, each verified against quotes from its sources (see [research/README.md](https://github.com/wakamex/model-prices/blob/main/research/README.md)); hand corrections take precedence. A correction without an end date names the models.dev values it replaces and stops applying as soon as models.dev records anything else, so it cannot outlive a later fix or price change.
|
|
51
|
+
|
|
52
|
+
`Rates.valid_from_basis` says how a start date was found: `models.dev commit` dates usually lag the provider's real change by days, while `documented` dates come from a correction's cited source.
|
|
53
|
+
|
|
54
|
+
Usage from before a model's first recorded price is priced at that first price, because models.dev often adds a model after its launch. `Rates.valid_from` is then later than the requested time, which a caller can check to reject implausible times, such as a zero timestamp. Usage after `data_as_of()`, the date of the models.dev commit the data was built from, assumes no price changed since.
|
|
55
|
+
|
|
56
|
+
Pass the time the provider billed the request as `at`. Providers do not document whether a request that spans a peak boundary bills at its start or its end; agent harnesses usually report completion times, which is a reasonable choice.
|
|
57
|
+
|
|
58
|
+
Each entry keeps input, output, cache read, and cache write rates, long-context tiers, and alternate modes such as fast mode. A tier applies when one request's whole prompt, meaning uncached input plus cache reads and writes, exceeds the tier size. A missing cache price falls back to the input price.
|
|
59
|
+
|
|
60
|
+
## Time-of-day pricing
|
|
61
|
+
|
|
62
|
+
Some providers price by time of day. `schedules.toml` records each version of a provider's rules with the dates it applied: either peak windows that multiply the recorded rate, or off-peak windows that discount it for listed models. `Rates.period` says which applied: `"peak"` or `"off_peak"`, `"standard"` outside a discount window, or `None` when no time-of-day rule applies.
|
|
63
|
+
|
|
64
|
+
Holiday calendars record the last date they cover, and `model-prices check` fails 45 days before that date so the next year's holidays are added in time. DeepSeek is the one provider with such pricing so far. From 16:30 UTC on February 26, 2025 until 16:00 UTC on September 5, 2025, it took 50% off DeepSeek-V3 (`deepseek-chat`) and 75% off DeepSeek-R1 (`deepseek-reasoner`) from 16:30 to 00:30 UTC daily. Since 16:00 UTC on August 16, 2026, its peak hours have been 01:00-04:00 and 06:00-10:00 UTC on weekdays other than Chinese public holidays, at twice the off-peak rate. The pricing page added the weekday and holiday exceptions in late August and mid-September without an announcement; model-prices applies the current rule from the start of peak pricing. [DeepSeek pricing history](https://github.com/wakamex/model-prices/blob/main/docs/deepseek-pricing-history.md) records the evidence for each 2026 date. DeepSeek decides the period by when a request completes, so pass the completion time as `at` where it is known.
|
|
65
|
+
|
|
66
|
+
## Official price checks
|
|
67
|
+
|
|
68
|
+
`model-prices check` fetches each supported provider's official pricing page, reads it with a parser written for that page's table layout, and compares every price with the rate model-prices uses today. It covers Anthropic, DeepSeek, Google, OpenAI, xAI, and Z.ai. Each price is reported as:
|
|
69
|
+
|
|
70
|
+
- `ok`: models.dev and the official page agree.
|
|
71
|
+
- `corrected`: models.dev differs, but a correction in `corrections.toml` or `research_corrections.toml` supplies the official price.
|
|
72
|
+
- `mismatch`: model-prices differs from the official page. The command exits with status 1.
|
|
73
|
+
- `untracked`: the page lists a model that models.dev does not price.
|
|
74
|
+
|
|
75
|
+
Page model names must match models.dev ids exactly, with no suffix removal, so a dated model is never compared with a different undated one. For DeepSeek, peak and off-peak prices are checked at the next hours the schedule classifies as each, and the peak-hour rule on both the English and Chinese pricing pages must match the wording recorded in `schedules.toml`, so a change to the hours, multiplier, or exceptions fails the check. Google's dated future prices are read for the check date. A page that yields no prices fails the check, because its format has changed.
|
|
76
|
+
|
|
77
|
+
`model-prices check --genai-prices` also scores [Pydantic's genai-prices](https://github.com/pydantic/genai-prices) against the same official prices, reading its `data.json` from GitHub, or from a file or checkout given as `--genai-prices PATH`. It lists each price genai-prices gets wrong and ends with how many official prices each source lists correctly, wrongly, or not at all. Its disagreements never change the exit status. genai-prices is read by exact model id only: it also matches names by prefix, which would give a model it lacks, such as Claude Opus 5.5, an older model's prices. Z.ai is not compared, because genai-prices' Zhipu entry is the mainland China API. On 2026-10-01, models.dev listed 342 official prices correctly and 7 wrongly; genai-prices listed 173 correctly and 40 wrongly, almost all from missing price cuts such as GPT-5.6's.
|
|
78
|
+
|
|
79
|
+
## Not modeled
|
|
80
|
+
|
|
81
|
+
model-prices prices standard synchronous requests. It does not model batch, flex, or priority service tiers; Anthropic's separate 1-hour cache-write price, which it treats as the 5-minute price; regional or data-residency surcharges; storage charges for cached context; or tool fees such as web search. Modes listed by models.dev, such as fast mode, are available through `mode`. While a correction applies, a mode's price is the corrected price scaled by the mode's ratio to models.dev's standard price at that time, since corrections record standard prices only.
|
|
82
|
+
|
|
83
|
+
Where a model has no cache-write price, cache writes are priced as input. That matches providers whose caching is automatic and bills the first, cache-filling request as normal input, such as Google's implicit caching, DeepSeek, xAI, and Z.ai. Providers that charge more for writing a cache list a cache-write price in models.dev.
|
|
84
|
+
|
|
85
|
+
Long-context tiers apply when a request's prompt exceeds the tier size. Providers word the boundary differently, such as Google's "> 200k" and xAI's "≥ 200k", so a prompt of exactly the tier size may price one tier off.
|
|
86
|
+
|
|
87
|
+
`Rates.breakdown()` returns a request's cost by token type and the tier it reached, for callers that report either.
|
|
88
|
+
|
|
89
|
+
## Model names
|
|
90
|
+
|
|
91
|
+
`resolve()` maps the names that logs and agent harnesses use to models.dev ids. It lowercases the name and normalizes display names such as `Gemini 3.5 Flash (High)` and dotted Claude versions such as `claude-sonnet-4.5`. It reads a `provider/` prefix or the `provider` argument as a hint, and when the full name is unknown it removes context markers such as `[1m]`, the `-build` suffix that xAI's subscription endpoint adds to Grok versions such as `grok-4.6-build`, effort suffixes such as `-high`, and date suffixes such as `-20251001` or `-2026-04-23`. `Rates.removed_suffix` records any text removed this way, and `resolve_details()` returns it, so a caller can tell when a price belongs to a related name: `-max` is both an effort level and part of some model names, such as `qwen3.8-max`. A model is looked up at its own lab before other labs that also serve it. A lab model name resolves to the first-party API id that serves it, using models.dev's `base_model` links and preferring ids that are not deprecated: DeepSeek serves `deepseek-v4.1-flash` as `deepseek-flash`, so that name gets `deepseek-flash`'s price. `aliases.toml` holds the few names that need an explicit mapping. Unknown models return `None` rather than a guessed price.
|
|
92
|
+
|
|
93
|
+
## Current prices for past requests
|
|
94
|
+
|
|
95
|
+
`rates()` and `cost()` take `prices_at` to price a request from a different date's price list. The time-of-day rule in force at `at`, the time the request ran, still applies: a request made in a 2025 DeepSeek discount hour keeps its discount on today's prices. Pricing every past request with `prices_at` set to today compares usage across weeks without price changes appearing as usage changes.
|
|
96
|
+
|
|
97
|
+
## Prices over a span
|
|
98
|
+
|
|
99
|
+
`rates_between(model, start, end)` returns each distinct rate in effect over a span with the time it begins. Use it for usage known only to fall within a span, such as a run with a start and finish but no per-request times: a single entry means one price covered the whole span. It finds changes from price history, corrections, and time-of-day schedules exactly, including peak windows and holidays. `Rates.price_key()` compares prices without provenance, so a correction and the models.dev entry that later records the same prices compare equal.
|
|
100
|
+
|
|
101
|
+
## Subscription plans
|
|
102
|
+
|
|
103
|
+
`plan(provider, plan_id, at=None)` returns a subscription plan's monthly price from `plans.toml`, keyed by the plan name each lab's usage tools report, such as `max_20x` for Claude or `lite` for the GLM Coding Plan. A plan sold in several price tiers gets one entry per tier, with the price in its id and name, such as `ultra_200` for Google AI Ultra $200 or `pro_500` for ChatGPT Pro $500. When a usage tool reports only the untiered name, that name has no entry, except that ChatGPT's `pro` keeps its original $200 price.
|
|
104
|
+
|
|
105
|
+
## Recording what was used
|
|
106
|
+
|
|
107
|
+
`pricing_basis()` returns an identifier such as `model-prices-0.0.1+models.dev@e2bf2e470a1b+synced@2026-09-30+data@3f1c09a2b7de`, naming the package version, the models.dev commit its data came from and that commit's date, and a hash of all its data files, so any change to prices, corrections, schedules, or aliases changes it. Store it next to computed costs.
|
|
108
|
+
|
|
109
|
+
## Updating prices
|
|
110
|
+
|
|
111
|
+
A daily workflow clones models.dev, rebuilds `src/model_prices/data/prices.json` with `model-prices update`, and opens a pull request when a tracked price changed. It then runs `model-prices check` and fails when an official price disagrees; fix that with a correction, or with a models.dev pull request when models.dev is wrong. Before merging, check each changed model's effective date against the provider's announcement and add a correction when models.dev recorded the change late.
|
|
112
|
+
|
|
113
|
+
To rebuild locally:
|
|
114
|
+
|
|
115
|
+
```sh
|
|
116
|
+
git clone https://github.com/sst/models.dev.git /tmp/models.dev
|
|
117
|
+
uv run --locked model-prices update /tmp/models.dev
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Development
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
uv run --locked python -m unittest discover -s tests
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
The code and the corrections are MIT licensed. `src/model_prices/data/prices.json` is derived from [models.dev](https://github.com/sst/models.dev), whose MIT license is in `src/model_prices/data/LICENSE.models.dev`. The test fixture `tests/fixtures/genai-prices.json` is an excerpt of [genai-prices](https://github.com/pydantic/genai-prices), whose MIT license is in `tests/fixtures/LICENSE.genai-prices`.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# model-prices
|
|
2
|
+
|
|
3
|
+
LLM API prices, today's or as they were at any point in time. Pricing past usage at today's rates misstates it whenever a price changed: GPT-5.6 Sol cost $5/$30 per million input/output tokens until August 21, 2026, and $4/$20 for the three months after. This package answers "what did this request cost at API prices when it ran?"
|
|
4
|
+
|
|
5
|
+
Status: work in progress. Versions before 0.1 may change the data format and API without notice.
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
import model_prices
|
|
9
|
+
|
|
10
|
+
model_prices.cost(
|
|
11
|
+
"gpt-5.6-sol",
|
|
12
|
+
input_tokens=12_000, # uncached input only
|
|
13
|
+
cache_read_tokens=180_000,
|
|
14
|
+
output_tokens=900,
|
|
15
|
+
at="2026-08-20T14:00:00Z",
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
model_prices.rates("claude-opus-5-5") # current rates, per million tokens
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
model-prices rate gpt-5.6-sol --at 2026-08-20
|
|
23
|
+
model-prices rate "Gemini 3.5 Flash (High)" --json
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
Requires Python 3.11 or newer.
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
uv add model-prices
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Where the prices come from
|
|
35
|
+
|
|
36
|
+
Prices come from the git history of [models.dev](https://github.com/sst/models.dev), an open catalog of model metadata that records each model's price in one TOML file per provider. Every commit on its main branch that changed a tracked model's price becomes an entry effective from that commit's time. The tracked providers are the labs that sell their own models: Alibaba, Anthropic, DeepSeek, Google, Moonshot AI, OpenAI, xAI, and Z.ai.
|
|
37
|
+
|
|
38
|
+
models.dev sometimes records a change days after the provider made it, or lists a wrong price for a while. `corrections.toml` overrides those periods, and every correction cites its source. `research_corrections.toml` adds corrections generated from agent research into every recorded change, each verified against quotes from its sources (see [research/README.md](https://github.com/wakamex/model-prices/blob/main/research/README.md)); hand corrections take precedence. A correction without an end date names the models.dev values it replaces and stops applying as soon as models.dev records anything else, so it cannot outlive a later fix or price change.
|
|
39
|
+
|
|
40
|
+
`Rates.valid_from_basis` says how a start date was found: `models.dev commit` dates usually lag the provider's real change by days, while `documented` dates come from a correction's cited source.
|
|
41
|
+
|
|
42
|
+
Usage from before a model's first recorded price is priced at that first price, because models.dev often adds a model after its launch. `Rates.valid_from` is then later than the requested time, which a caller can check to reject implausible times, such as a zero timestamp. Usage after `data_as_of()`, the date of the models.dev commit the data was built from, assumes no price changed since.
|
|
43
|
+
|
|
44
|
+
Pass the time the provider billed the request as `at`. Providers do not document whether a request that spans a peak boundary bills at its start or its end; agent harnesses usually report completion times, which is a reasonable choice.
|
|
45
|
+
|
|
46
|
+
Each entry keeps input, output, cache read, and cache write rates, long-context tiers, and alternate modes such as fast mode. A tier applies when one request's whole prompt, meaning uncached input plus cache reads and writes, exceeds the tier size. A missing cache price falls back to the input price.
|
|
47
|
+
|
|
48
|
+
## Time-of-day pricing
|
|
49
|
+
|
|
50
|
+
Some providers price by time of day. `schedules.toml` records each version of a provider's rules with the dates it applied: either peak windows that multiply the recorded rate, or off-peak windows that discount it for listed models. `Rates.period` says which applied: `"peak"` or `"off_peak"`, `"standard"` outside a discount window, or `None` when no time-of-day rule applies.
|
|
51
|
+
|
|
52
|
+
Holiday calendars record the last date they cover, and `model-prices check` fails 45 days before that date so the next year's holidays are added in time. DeepSeek is the one provider with such pricing so far. From 16:30 UTC on February 26, 2025 until 16:00 UTC on September 5, 2025, it took 50% off DeepSeek-V3 (`deepseek-chat`) and 75% off DeepSeek-R1 (`deepseek-reasoner`) from 16:30 to 00:30 UTC daily. Since 16:00 UTC on August 16, 2026, its peak hours have been 01:00-04:00 and 06:00-10:00 UTC on weekdays other than Chinese public holidays, at twice the off-peak rate. The pricing page added the weekday and holiday exceptions in late August and mid-September without an announcement; model-prices applies the current rule from the start of peak pricing. [DeepSeek pricing history](https://github.com/wakamex/model-prices/blob/main/docs/deepseek-pricing-history.md) records the evidence for each 2026 date. DeepSeek decides the period by when a request completes, so pass the completion time as `at` where it is known.
|
|
53
|
+
|
|
54
|
+
## Official price checks
|
|
55
|
+
|
|
56
|
+
`model-prices check` fetches each supported provider's official pricing page, reads it with a parser written for that page's table layout, and compares every price with the rate model-prices uses today. It covers Anthropic, DeepSeek, Google, OpenAI, xAI, and Z.ai. Each price is reported as:
|
|
57
|
+
|
|
58
|
+
- `ok`: models.dev and the official page agree.
|
|
59
|
+
- `corrected`: models.dev differs, but a correction in `corrections.toml` or `research_corrections.toml` supplies the official price.
|
|
60
|
+
- `mismatch`: model-prices differs from the official page. The command exits with status 1.
|
|
61
|
+
- `untracked`: the page lists a model that models.dev does not price.
|
|
62
|
+
|
|
63
|
+
Page model names must match models.dev ids exactly, with no suffix removal, so a dated model is never compared with a different undated one. For DeepSeek, peak and off-peak prices are checked at the next hours the schedule classifies as each, and the peak-hour rule on both the English and Chinese pricing pages must match the wording recorded in `schedules.toml`, so a change to the hours, multiplier, or exceptions fails the check. Google's dated future prices are read for the check date. A page that yields no prices fails the check, because its format has changed.
|
|
64
|
+
|
|
65
|
+
`model-prices check --genai-prices` also scores [Pydantic's genai-prices](https://github.com/pydantic/genai-prices) against the same official prices, reading its `data.json` from GitHub, or from a file or checkout given as `--genai-prices PATH`. It lists each price genai-prices gets wrong and ends with how many official prices each source lists correctly, wrongly, or not at all. Its disagreements never change the exit status. genai-prices is read by exact model id only: it also matches names by prefix, which would give a model it lacks, such as Claude Opus 5.5, an older model's prices. Z.ai is not compared, because genai-prices' Zhipu entry is the mainland China API. On 2026-10-01, models.dev listed 342 official prices correctly and 7 wrongly; genai-prices listed 173 correctly and 40 wrongly, almost all from missing price cuts such as GPT-5.6's.
|
|
66
|
+
|
|
67
|
+
## Not modeled
|
|
68
|
+
|
|
69
|
+
model-prices prices standard synchronous requests. It does not model batch, flex, or priority service tiers; Anthropic's separate 1-hour cache-write price, which it treats as the 5-minute price; regional or data-residency surcharges; storage charges for cached context; or tool fees such as web search. Modes listed by models.dev, such as fast mode, are available through `mode`. While a correction applies, a mode's price is the corrected price scaled by the mode's ratio to models.dev's standard price at that time, since corrections record standard prices only.
|
|
70
|
+
|
|
71
|
+
Where a model has no cache-write price, cache writes are priced as input. That matches providers whose caching is automatic and bills the first, cache-filling request as normal input, such as Google's implicit caching, DeepSeek, xAI, and Z.ai. Providers that charge more for writing a cache list a cache-write price in models.dev.
|
|
72
|
+
|
|
73
|
+
Long-context tiers apply when a request's prompt exceeds the tier size. Providers word the boundary differently, such as Google's "> 200k" and xAI's "≥ 200k", so a prompt of exactly the tier size may price one tier off.
|
|
74
|
+
|
|
75
|
+
`Rates.breakdown()` returns a request's cost by token type and the tier it reached, for callers that report either.
|
|
76
|
+
|
|
77
|
+
## Model names
|
|
78
|
+
|
|
79
|
+
`resolve()` maps the names that logs and agent harnesses use to models.dev ids. It lowercases the name and normalizes display names such as `Gemini 3.5 Flash (High)` and dotted Claude versions such as `claude-sonnet-4.5`. It reads a `provider/` prefix or the `provider` argument as a hint, and when the full name is unknown it removes context markers such as `[1m]`, the `-build` suffix that xAI's subscription endpoint adds to Grok versions such as `grok-4.6-build`, effort suffixes such as `-high`, and date suffixes such as `-20251001` or `-2026-04-23`. `Rates.removed_suffix` records any text removed this way, and `resolve_details()` returns it, so a caller can tell when a price belongs to a related name: `-max` is both an effort level and part of some model names, such as `qwen3.8-max`. A model is looked up at its own lab before other labs that also serve it. A lab model name resolves to the first-party API id that serves it, using models.dev's `base_model` links and preferring ids that are not deprecated: DeepSeek serves `deepseek-v4.1-flash` as `deepseek-flash`, so that name gets `deepseek-flash`'s price. `aliases.toml` holds the few names that need an explicit mapping. Unknown models return `None` rather than a guessed price.
|
|
80
|
+
|
|
81
|
+
## Current prices for past requests
|
|
82
|
+
|
|
83
|
+
`rates()` and `cost()` take `prices_at` to price a request from a different date's price list. The time-of-day rule in force at `at`, the time the request ran, still applies: a request made in a 2025 DeepSeek discount hour keeps its discount on today's prices. Pricing every past request with `prices_at` set to today compares usage across weeks without price changes appearing as usage changes.
|
|
84
|
+
|
|
85
|
+
## Prices over a span
|
|
86
|
+
|
|
87
|
+
`rates_between(model, start, end)` returns each distinct rate in effect over a span with the time it begins. Use it for usage known only to fall within a span, such as a run with a start and finish but no per-request times: a single entry means one price covered the whole span. It finds changes from price history, corrections, and time-of-day schedules exactly, including peak windows and holidays. `Rates.price_key()` compares prices without provenance, so a correction and the models.dev entry that later records the same prices compare equal.
|
|
88
|
+
|
|
89
|
+
## Subscription plans
|
|
90
|
+
|
|
91
|
+
`plan(provider, plan_id, at=None)` returns a subscription plan's monthly price from `plans.toml`, keyed by the plan name each lab's usage tools report, such as `max_20x` for Claude or `lite` for the GLM Coding Plan. A plan sold in several price tiers gets one entry per tier, with the price in its id and name, such as `ultra_200` for Google AI Ultra $200 or `pro_500` for ChatGPT Pro $500. When a usage tool reports only the untiered name, that name has no entry, except that ChatGPT's `pro` keeps its original $200 price.
|
|
92
|
+
|
|
93
|
+
## Recording what was used
|
|
94
|
+
|
|
95
|
+
`pricing_basis()` returns an identifier such as `model-prices-0.0.1+models.dev@e2bf2e470a1b+synced@2026-09-30+data@3f1c09a2b7de`, naming the package version, the models.dev commit its data came from and that commit's date, and a hash of all its data files, so any change to prices, corrections, schedules, or aliases changes it. Store it next to computed costs.
|
|
96
|
+
|
|
97
|
+
## Updating prices
|
|
98
|
+
|
|
99
|
+
A daily workflow clones models.dev, rebuilds `src/model_prices/data/prices.json` with `model-prices update`, and opens a pull request when a tracked price changed. It then runs `model-prices check` and fails when an official price disagrees; fix that with a correction, or with a models.dev pull request when models.dev is wrong. Before merging, check each changed model's effective date against the provider's announcement and add a correction when models.dev recorded the change late.
|
|
100
|
+
|
|
101
|
+
To rebuild locally:
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
git clone https://github.com/sst/models.dev.git /tmp/models.dev
|
|
105
|
+
uv run --locked model-prices update /tmp/models.dev
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Development
|
|
109
|
+
|
|
110
|
+
```sh
|
|
111
|
+
uv run --locked python -m unittest discover -s tests
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## License
|
|
115
|
+
|
|
116
|
+
The code and the corrections are MIT licensed. `src/model_prices/data/prices.json` is derived from [models.dev](https://github.com/sst/models.dev), whose MIT license is in `src/model_prices/data/LICENSE.models.dev`. The test fixture `tests/fixtures/genai-prices.json` is an excerpt of [genai-prices](https://github.com/pydantic/genai-prices), whose MIT license is in `tests/fixtures/LICENSE.genai-prices`.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "model-prices"
|
|
3
|
+
version = "0.0.1"
|
|
4
|
+
description = "LLM API prices, current and historical, from models.dev with sourced corrections"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE", "src/model_prices/data/LICENSE.models.dev"]
|
|
8
|
+
requires-python = ">=3.11"
|
|
9
|
+
dependencies = []
|
|
10
|
+
|
|
11
|
+
[project.scripts]
|
|
12
|
+
model-prices = "model_prices:main"
|
|
13
|
+
|
|
14
|
+
[project.urls]
|
|
15
|
+
Homepage = "https://github.com/wakamex/model-prices"
|
|
16
|
+
Source = "https://github.com/wakamex/model-prices"
|
|
17
|
+
|
|
18
|
+
[build-system]
|
|
19
|
+
requires = ["hatchling"]
|
|
20
|
+
build-backend = "hatchling.build"
|
|
21
|
+
|
|
22
|
+
[tool.hatch.build.targets.sdist]
|
|
23
|
+
include = ["/src", "/tests", "/README.md", "/LICENSE"]
|