gen-floor
Live $/image and $/second floor tracker for generative-media APIs. Token-floor for pixels and frames — cheapest model per category right now, biggest weekly movers, full price history per model. Real data only, no auth, no mocks.
Live: https://holyai.me/gen-floor/
Port: 4829
Base path: /gen-floor
---
What it does
gen-floor answers three questions for anyone shopping for generative-media inference:
- What's the cheapest provider right now for text-to-image / image-to-image / text-to-video / image-to-video?
- What dropped this week? Which models cut their per-image or per-second price in the last 7 / 14 / 30 days?
- How has a specific model's price moved? Every model has a full snapshot history with an inline sparkline.
The dashboard is dark-themed, framework-free vanilla JS, and entirely public (no login, no admin panel).
Data sources
Every price on the dashboard is pulled live from a public source. No seed data, no mocks, no random jitter. If a fetcher fails, the run is logged in the Fetch log tab and the dashboard shows "awaiting data" instead of filling in fakes.
| Source | Endpoint | Refresh | What we extract |
|------------------|-----------------------------------------------------------------------------------|-----------|-----------------|
| fal.ai | GET https://fal.ai/api/models?page=N&size=40 (paginated, ~34 pages, ~1,350 models) | every 15 min | id, title, category, modelLab, modelFamily, pricingInfoOverride markdown, and the URL/thumb/license metadata |
| Replicate | GET https://replicate.com/<owner>/<slug> for ~30 curated high-signal model slugs | every 60 min | Pricing parsed from the rendered HTML's natural-language phrases ("333 images for $1", "$0.40 per second", etc.) |
Curated Replicate slug list lives in fetchers/replicate-slugs.js — add or remove models freely; bad slugs 404 and the run logs them then carries on.
Normalization
Because providers price in different units, we normalize each snapshot to two comparable scalars:
price_per_image_norm— direct forper_image; forper_megapixelwe treat the rate as equivalent to per-image at 1 MP (most demo configs default near 1 MP).price_per_second_norm— direct forper_second; forper_videowe divide by 5 (5 s is the modal default-clip length across Kling, Veo, Pixverse, Wan).
These assumptions are documented in the in-app About tab and are intentionally honest about being approximations.
The Movers view ignores any model whose pricing unit changed between snapshots — a $0.05/sec → $5/video isn't a real "spike", it's a unit shift.
Stack
- Node.js 20+ (ESM)
- Express 4 with
helmet(CSP for inline scripts) andcompression - better-sqlite3 in WAL mode — single
data.dbfile - node-cron for the 15-minute and hourly fetchers, plus a daily prune job
- Vanilla JS SPA — no React, no build step, no Tailwind. Single CSS file, single JS file, single HTML shell. Dark theme.
Local development
npm install
cp .env.example .env
npm start
# → http://localhost:4829/gen-floor/
The first fal.ai fetch fires ~5 seconds after boot and the first Replicate fetch ~25 seconds after boot, so the dashboard populates without manual intervention.
API
All endpoints are mounted under BASE_PATH=/gen-floor. None require auth.
| Method | Path | Returns |
|--------|--------------------------------------------|---------|
| GET | /gen-floor/health | { ok, models, last_fetch_at, last_fetch_iso, uptime_s } — 200 always |
| GET | /gen-floor/api/floors[?limit=N] | Cheapest N models per canonical category |
| GET | /gen-floor/api/models[?category=&source=&q=] | Latest snapshot per model, sortable / filterable |
| GET | /gen-floor/api/models/<id>/history[?limit=N] | Snapshot history for one model |
| GET | /gen-floor/api/movers[?days=7&limit=10] | Top-N drops + spikes over the last N days |
| GET | /gen-floor/api/categories | Per-category tracked counts + cheapest model |
| GET | /gen-floor/api/fetch-log[?limit=50] | Per-run audit log for cron health checks |
Schema
snapshots(
id, source, model_id, model_title, model_family, model_lab,
category, unit, price_usd,
price_per_image_norm, price_per_second_norm,
raw_pricing_text, meta_json, fetched_at
)
fetch_log(
id, source, started_at, finished_at, status,
models_seen, models_priced, error
)
```
Snapshots are append-only. A daily maintenance cron at 00:05 UTC prunes rows older than 90 days while always keeping the most-recent snapshot per model so the "latest" view never goes empty.
What's intentionally not here
- Hardware-second pricing (e.g. fal.ai's $1.89/h H100). Not comparable to output-priced models.
- Per-token text pricing — that's
token-floor's job. - Training prices and dataset / storage fees.
- Anything LLM-judged. Every number on this dashboard came from a JSON or HTML response in the last 90 days, with the source row stored for forensics.
License
UNLICENSED. Code is published for transparency, not redistribution.