Cost and scaling
Per-user cost today and where bills hit at 1K / 10K / 100K monthly active users
Numbers reflect public pricing as of 2026-05-26. Re-verify before quoting.
Per-user cost today
Assume a moderately engaged user: opens the app 3× per week, browses 10 schools per session, opens 5 college detail pages per session, saves 2.
| Cost driver | Per session | Per month |
|---|---|---|
| Convex bandwidth + function calls | $0 (free tier) | $0 |
| OpenAI blurb (5 detail opens × first-open cost ~$0.0003) | ~$0.0015 | ~$0.018 |
| Subsequent blurb opens (cache hit) | $0 | $0 |
| Scorecard API (cache hit on warmed schools) | $0 | $0 |
| logo.dev | $0 (free tier) | $0 |
| Serper hero images (cache hit on warmed schools) | $0 | $0 |
| Push notifications (FCM / APNS) | $0 | $0 |
| PostHog ingest | $0 (free tier) | $0 |
| WorkOS active user | — | included in tier |
Estimated marginal cost per moderately engaged MAU: ~$0.02/month.
This is the AI blurb + cache hit on data. New cold-fetched colleges (first time anyone in the system encounters them) carry a small one-time cost for logo + hero image; amortized across the user base it's negligible.
Where the bills hit
Convex
- Free tier: up to 1M function calls, 1GB DB storage, 1GB bandwidth/month.
- Pro tier ($25/month/team member): 25M function calls, 8GB storage, 50GB bandwidth.
- Linear pricing above pro: see convex.dev/pricing.
At ~1K MAU the free tier covers everything. At ~10K MAU you're solidly in Pro tier. At ~100K MAU you're paying for incremental function calls + storage; budget ~$300–600/month depending on access patterns.
The dominant cost grower is useQuery subscriptions — every active session holds a live subscription. Closing unused subscriptions (useQuery outside view boundaries) is the lever.
OpenAI
gpt-5.4-nanoinput: ~$0.20/M tokens. Output: ~$0.80/M tokens (verify at openai.com/api/pricing).- Each blurb: ~500 input tokens (structured user + college facts) + ~150 output tokens = ~$0.00022 per blurb.
- 30-day cache means each
(user, school)pair generates at most one blurb per month.
At 10K MAU, ~5 detail opens/session × 12 sessions/month = 60 unique-or-cached opens × cache hit rate ~80% = ~12 new blurbs/month/user. ~12K × $0.00022 = ~$2.64/month for 1K MAU. ~$26/month for 10K MAU. ~$264/month for 100K MAU.
Scorecard API
Public, free, no quota declared. NXT hits Scorecard:
- Once per school per cold fetch (cached in
collegestable). - Once per school per monthly refresh (no-op for unchanged docs).
At 6,000 active institutions, monthly refresh = 6K calls. Fully sustainable on the free tier indefinitely.
Serper
$0.30 per 1,000 queries. Each new (cold) school = ~1 query for hero image. At 6K total institutions × $0.30/1K = **$1.80 one-time** to warm the entire hero image cache. After that, only newly-released institutions or cache evictions cost anything. Effectively $0/month at steady state.
logo.dev
Free tier: 1,000 logo fetches/month. Same caching as Serper. At steady state, effectively $0/month after initial warm.
WorkOS
- Free tier: 1M monthly active users.
- Currently free.
- AuthKit Pro features (SSO, SCIM, fine-grained roles) move to paid tier; not used today.
Vercel
- Free Hobby tier covers personal sites only.
- Pro at $20/month/seat is what production marketing site needs.
- Bandwidth is the cost driver. At ~10K MAU, the marketing site is probably under the Pro tier's 1TB bandwidth allowance.
EAS Build
- 30 free builds/month for Production tier ($99/month) or pay-per-build $1–4 depending on machine size.
- A typical release week: 4–8 builds (preview, staging, production iOS + Android). Easily within the Production tier.
Apple Developer Program
$99/year, fixed.
Google Play Console
$25 one-time fee, already paid.
Total infrastructure cost estimates
| MAU | Convex | OpenAI | WorkOS | Vercel | EAS | Apple/Play | Total/month |
|---|---|---|---|---|---|---|---|
| 1K | $0 (free) | ~$2.64 | $0 | $20 | $99 | ~$8 | ~$130 |
| 10K | ~$50 | ~$26 | $0 | $20 | $99 | ~$8 | ~$200 |
| 100K | ~$400 | ~$264 | ~$1K | $40 | $99 | ~$8 | ~$1,800 |
Estimates are conservative. Verify each line at the actual provider before quoting to a board.
What breaks at 10K MAU
Probably nothing. The architecture was designed for ~10K MAU as the day-one expected ceiling.
The first thing that would measurably feel slower is the discover.rails single-roundtrip query (see Status). p99 latency on that query grows with rail complexity. Splitting into nine per-rail queries is a known optimization.
What breaks at 100K MAU
Several things become worth solving:
- Convex subscriptions. Active subscription count grows with concurrent sessions. Convex handles this, but cost grows linearly. Lever: aggressive
useQueryscoping. - OpenAI per-blurb cost. Could grow to $250+/month. Lever: harder cache (per-cohort blurb instead of per-user), or move to a cheaper provider.
collegestable size. 6K institutions × 30 KB of nested data ≈ 180 MB. Well within Convex limits but worth understanding.- Cron fan-out. The weekly new-matches cron iterates active users in 500-row pages. At 100K active users, the chain depth is 200. The chain is bounded but the per-week compute scales linearly.
scorecard refreshwrite pressure. Monthly. 6K upserts in a window. Convex handles it; budget a 5-minute window per refresh.
None of these is a cliff. All are linear scaling.
Cost optimizations that have NOT been done
In priority order, where dollars (not engineering hours) become the constraint:
- Move push delivery to a transactional provider if FCM/APNS hit rate limits. Resend or AWS SNS would be the fallback.
- Cohort-level blurb caching. A "for users with profile X" cached blurb instead of one per user. Cuts OpenAI cost by ~10×. Trade-off: less personalized copy.
- Aggregate components (
aggregateWorkforce,aggregateTransferOutcome) fully replacingby_active_score. Reduces top-N read cost. - Edge caching the public marketing site. Vercel's free CDN already handles this; only pull this lever if Vercel bandwidth becomes meaningful.
Technical detail
Why OpenAI cost isn't bigger
The 30-day cache + the fact that the blurb is a ~150-output-token generation. The model is cheap; the request is short; the cache absorbs the rest.
Where Convex cost comes from
Convex bills on (a) function call count, (b) database storage, (c) bandwidth in/out of the deployment. Subscriptions don't bill separately, but every subscription update is a function call internally.
Why staging Convex would double infra cost
A second deployment doubles the storage line (same data, two copies if synced; sparse data otherwise) and doubles the cron-firing cost. Decision in Deployment was to skip staging Convex on this trade-off.