Stop hardcoding your pricing page - render it from your metering layer
Every B2B SaaS I have shipped repeats the same mistake: plans live in two places - the dashboard that enforces them, and a const PLANS = [...] on the marketing site. They drift within a quarter.
Last updated: 2026-05-11
Why hardcoded pricing pages drift
You ship v1 with a clean const PLANS = [{ name: "Pro", images: 500 }]. Two weeks later you bump Pro to 1000 images in the dashboard because a deal needs it, and you ship. The marketing page still says 500. A prospect signs up, sees 1000 in the app, and either feels cheated (the page lied) or never knows about the extra (you under-sold). Multiply by every plan, every limit, every metadata-gated variant. The drift is invisible until a customer asks.
The single-source-of-truth fix
If your metering layer already owns the canonical plan structure - names, limits, units, period rollover, even which models count toward which bucket - your pricing page should read from it. Not duplicate it. The API call is dirt cheap (one row read per plan, cacheable for minutes), and the response shape maps cleanly onto a pricing card.
What "rendering from metering" actually looks like
In Vevee the endpoint is GET /api/v1/plans (or vevee.availablePlans() from the SDK). It accepts a public key - same key class you would expose to a browser - and returns every plan for the calling app with id, display name, period type ("monthly"), period anchor ("subscription_start" vs "calendar"), and the full limit-group list. Each limit group includes its label, unit (count / tokens / seconds / cents), quota, the event pattern it meters against (e.g. "image.*"), and the full match rules - so you can render "500 Flux Pro renders (4k only)" without re-encoding the filter logic in your frontend.
What you keep doing yourself
Two things stay on your side. One: price tags. The metering layer is not the right home for "$20/mo" - Stripe is. Keep a small map of planId → display copy, or join Stripe products via metadata. Two: display order. APIs return plans in creation order; pricing pages need a deliberate cheap → expensive sequence. Hardcode an array of plan ids if you reshuffle plans often.
Variant-gated limits are the real win
The case for fetching plans is strongest when plans differ on metadata, not just total quotas. "Pro gets 4k images, Free is capped at 1k" is one match rule per group - and the moment you express it in code on the frontend, you have two copies of the same truth and a bug factory. availablePlans() ships the full matches array, so the pricing page describes the gating exactly as the meter enforces it. Same line of truth on both sides.
It is the same instinct as feature flags
Hardcoding plan structure on a marketing site is the same anti-pattern as hardcoding feature-flag values in client code. You centralise flags because they change faster than deploys. Plans change faster than deploys too - every promo, every enterprise carve-out, every Q4 price test. Putting them behind one API and reading from it consistently is just good hygiene. We added availablePlans() to Vevee specifically to close this gap.
More from the blog
How to manage subscription renewals: aligning Vevee with Stripe
A user signs up on Jan 15. Stripe charges them on the 15th of every month. Your metering layer resets on the 1st. Two clocks. One angry support ticket per cycle.
thinking · 4 minMeter AI by user, not by account - your margin depends on it
A few users will cost you 100x what your median user costs. If you only meter at the account level, you will not see them coming until your gross margin is gone.
engineering · 5 minreserve / commit / release: the only correct way to enforce AI quotas
Every team I have seen build per-user AI metering has shipped a version of canUse → call OpenAI → track. It looks correct in single-threaded tests. It is broken in production.
thinking · 4 minWhy Stripe Billing is not enough for AI products
Stripe is excellent at one thing: turning usage into invoices. AI products need three other things, and Stripe does not do any of them.