finetune-floor
Live cost index for managed LLM fine-tuning across 8 providers — normalized to $/M training tokens.
Live URL (after deploy): https://holyai.me/finetune-floor/
Why
Every model release in 2026 ships a fine-tunable variant. Pricing is fragmented across 8+ providers, each using different units (per-token, per-GPU-second, per-step). Picking the wrong provider for your model bucket easily overpays 3–10×.
finetune-floor pulls real prices from every major managed fine-tuning provider on a cron, normalizes everything to a single comparable unit, and lets you punch in your workload to see the cheapest option today.
Data sources (all public, no auth)
| Provider | URL | Refresh |
| ----------------- | ------------------------------------------------------------ | ------- |
| Together AI | https://docs.together.ai/docs/fine-tuning-pricing | 6 h |
| Fireworks AI | https://fireworks.ai/pricing | 6 h |
| OpenAI | https://openai.com/api/pricing | 6 h |
| Google Vertex AI | https://cloud.google.com/vertex-ai/generative-ai/pricing | 12 h |
| Replicate | https://replicate.com/pricing | 12 h |
| Modal | https://modal.com/pricing | 12 h |
| Predibase | https://predibase.com/pricing | 24 h |
| Mistral | https://docs.mistral.ai/capabilities/finetuning/ | 24 h |
No mock data, no seed prices, no fallback hardcodes. If a fetcher fails or the layout changes, the previous snapshot stays valid and a row is written to fetch_errors (surfaced in /api/stats).
Normalization
Token-priced providers (Together, Fireworks, OpenAI, Vertex, Predibase, Mistral) are stored directly as $/M training tokens.
GPU-time providers (Replicate, Modal) are converted using model-size curves in lib/normalize.js. The curve is derived from each provider's own published fine-tune benchmarks and is exposed in the normalization_method field on every row so users can audit it.
Stack
- Node.js 20+
- Express, helmet, compression, morgan
- better-sqlite3 (WAL mode), node-cron
- cheerio for HTML parsing
- Vanilla JS SPA — no framework, dark theme
- No auth. Every endpoint (read + admin trigger) is public.
API
| Method | Path | Description |
| ------ | --------------------------------------------------------------------------------------------- | ------------------------------------ |
| GET | /finetune-floor/health | smoke check (200) |
| GET | /finetune-floor/api/providers | provider list + refresh status |
| GET | /finetune-floor/api/prices?bucket=large&method=lora&sort=cheapest | filterable price table |
| GET | /finetune-floor/api/calculate?bucket=large&tokens=10000000&epochs=3&method=lora | ranked cost calculator |
| GET | /finetune-floor/api/movers?window=24h | recent price changes |
| GET | /finetune-floor/api/history?sku_id=42 | price timeline for one SKU |
| GET | /finetune-floor/api/stats | coverage + freshness + fetch errors |
| POST | /finetune-floor/api/refresh[?provider=together] | manual refresh trigger |
Run locally
npm install
npm start
# open http://localhost:4906/finetune-floor/
A manual refresh:
npm run refresh
Layout
finetune-floor/
├── server.js # Express + cron bootstrap
├── db.js # SQLite schema + prepared statements
├── fetchers/ # one file per provider + orchestrator
├── lib/ # parse, normalize, log
├── routes/ # one file per endpoint
└── public/ # vanilla JS SPA
License
MIT — © 2026 Cowork.