How we calculate costs

Every dollar on a StackPlan report comes from deterministic math over curated provider pricing data. The LLM writes the explanations — strengths, tradeoffs, clarifying questions — but it never invents a number. If the model is unavailable, you still get stacks and costs; you just lose the prose.

The knowledge base

Provider pricing lives in a D1 database we curate by hand. Each provider has services (compute, database, object storage, and so on). Each service has plan tiers with a base monthly price, included quotas, overage rates, and hard limits — all stored in integer cents to avoid floating-point drift.

Tiers are versioned. When a price changes, we retire the old row and insert a new one with an effective_from date. Nothing is overwritten silently. Published changes show up on the public pricing changelog, and Pro users get alerts when a tier they rely on moves.

You can browse and edit the raw data in /admin (founder-only). The recommendation engine reads only active providers, active services, and non-retired tiers — the same snapshot every report uses.

Daily verification

A scheduled agent reads each service's pricing page once per day, compares what it finds against the current KB tiers, and files evidence-backed proposals when something differs. No-change checks still bump lastCheckedAt, so we know the page was actually read. Proposals land in an admin review queue — agents propose, humans approve. Nothing auto-writes to production pricing.

That check history is what powers the freshness badges on reports and compare pages. A provider only counts as verified when every one of its tracked services has been checked; the badge shows the stalest check, not the freshest. Below is the live state for every provider in the KB right now:

Pricing data:AWS (serverless) · updated Jun 2025Cloudflare · updated Jun 2025Fly.io · updated Jun 2025Hetzner · updated Jun 2025Railway · updated Jun 2025Vercel · updated Jun 2025Pricing changelog

The cost model

Costs start with your questionnaire: expected daily active users (DAU), requests per user per day, storage per user, egress per request, and whether you need a database, file storage, or background jobs. From there the engine derives a monthly usage profile at each DAU level on the scaling chart (100, 1k, 10k, 100k).

Usage estimation

We assume a 30-day month and fixed per-request averages:

  • Requests — DAU × requests/user × 30
  • CPU time — requests × 15 ms average per request
  • Storage — DAU × 10 retained users × storage/user (MB), converted to GB
  • Egress — requests × egress/request (KB), converted to GB
  • Database reads/writes — 5 reads and 1 write per request, only when you need a database
  • Instances — 1 up to 50k DAU, 2 up to 200k, then scaled by traffic

Tier matching

For each service role the stack needs (compute, database, cache, and so on), we consider every active tier for that service in sort order. A tier is disqualified if your usage exceeds any of its hard limits. Among the rest, monthly cost is:

base price + sum of overages, where each overage unit is billed in whole chunks — ceil(usage − included) / rate.per × rate.priceCents. We pick the cheapest non-disqualified tier per role, then sum across the stack.

The ranking engine assembles full stack candidates from those per-role picks, scores them on fit (managed vs DIY preference, app-type tags, budget), and returns up to four options. Same math powers the engineering dial: lower stops drop roles; upper stops re-pick tiers with headroom multipliers (×3 for Robust, ×10 for Overkill) while still pricing at your actual usage.

What the LLM does — and does not do

The language model is a rationale layer only. It receives your questionnaire and the already-computed stack candidates (provider names, tier names, monthly costs in cents) and returns structured prose: a summary, per-stack strengths and tradeoffs, gotchas, and growth notes. It also powers intake helpers — turning a free-text description into questionnaire fields and generating clarifying questions when your input is ambiguous.

It does not:

  • Set or adjust prices, quotas, or tier selections
  • Choose which providers enter the candidate list
  • Override the deterministic ranking score

When no LLM adapter is configured (missing API key, local dev without the Cursor middleware), reports still generate. You get stacks, costs, and the canvas — just without the narrative block.

Where we can be wrong

We would rather show you the seams than pretend the numbers are prophecy.

  • Prices move between checks. A provider can change pricing the hour after our daily agent runs. Freshness badges and the changelog exist so you can see how stale a number is.
  • Your traffic is not average traffic. Requests per user, storage per user, and egress per request are estimates. A photo app and a JSON API with the same DAU look nothing alike in reality.
  • Free tiers shift. Hard limits and promotional allowances change without warning. We model what is in the KB today, not what a blog post promised last year.
  • Egress is hard. CDN caching, compression, and API payload shape can swing transfer costs by an order of magnitude. We use your questionnaire egress estimate as a single multiplier — crude by necessity.
  • One stack, many philosophies. The engineering dial on each report shows the same app at five engineering levels — from bare-minimum Duct tape to headroom-heavy Overkill — so you can see cost spread, not just a single point estimate.

Treat StackPlan numbers as a structured starting point: curated tiers, explicit assumptions, and math you can trace. When a decision matters, check the provider's pricing page and your own metrics.

See it on your app

Run the same pipeline on your questionnaire — stacks, tiers, scaling curve, and freshness badges included.