Skip to content

Pricing & billing

PerSQL billing is a prepaid balance. You top the balance up; metered usage drains it. Pricing is usage-only — no subscriptions, plan tiers, or seat fees. Five meters:

MeterRate
Per 1M requests$0.50
Per 1M rows read$0.005
Per 1M rows written$2.00
Per GB-month storage$0.40
Per 1M AI tokens$1.00

Reads and writes are metered separately because they have very different underlying costs — a lumped “rows” rate would punish read-heavy apps and underprice write-heavy ones.

AI tokens cover any AI call PerSQL makes on your behalf: the ask NL→SQL tool, auto-docs, and the index advisor. One uniform rate per token (input + output) so you don’t need to pick a model.

New namespaces start with a $5 welcome credit so you can try the platform without entering a card first.

Any call that reaches your database — measured uniformly so there’s no entry-point arbitrage:

  • Every /v1/* call (query, batch, tables, propose, apply, claim_branch, redeem_approval, …)
  • Every /p/:ns/:db/* public endpoint call
  • MCP tool calls
  • Console-driven ad-hoc queries

A 50-statement /v1/batch is one request. Statement count is still recorded for analytics; it just doesn’t multiply your bill.

Storage is sampled once per ~24h per database, excluding PerSQL’s own system tables — you’re billed for your data, not our overhead. An empty branch contributes nothing; deleting a database stops its samples immediately. Samples are summed over the window and divided out to GB-months.

The prepaid balance is the only spend control. Top it up to what you’re willing to lose; the gate returns 402 when it runs out. There is no per-token request count cap and no per-namespace monthly cap. Each database is fully isolated, so tenants cannot crowd each other — there is nothing to guard at the throughput layer.

If you want a tighter blast-radius for a single key (CI, end-user sessions, a sub-agent), mint a separate namespace with its own balance and use a token scoped to it. The balance on that namespace caps what the key can ever spend.

  1. Usage drains your balance in near real-time. Per-request cost is checked before the request runs; per-row cost is added once the row counts are known.
  2. Top-ups land in the ledger synchronously via the Stripe webhook, idempotent on the PaymentIntent id so a duplicate delivery is a no-op.
  3. A daily reconcile sanity-checks the running balance against the ledger and flags any drift to our internal status feed.

Settings → Billing → Add funds opens a Stripe Checkout page for a one-time payment ($5 minimum). When the payment completes, the balance is credited within a moment and the billing page’s ledger shows the top-up.

The billing page also shows the current balance, recent usage, and the full ledger (top-ups + daily usage debits).

Agents holding an admin-role token can fund their namespace without a human: POST /v1/billing/topup with { "amountUsd": 25 } returns a Stripe Checkout URL plus session id. MCP runtimes call the topup_balance tool, which wraps the same endpoint.

The 402 response when the balance runs out carries an x402-compatible body (x402Version, accepts[]) that advertises the top-up endpoint, the suggested amount, and the request shape. An MPP-capable runtime (Stripe Machine Payments Protocol, Coinbase x402) can drive Checkout end-to-end; other runtimes can hand the URL to a human.

When the balance gets low — but before it’s gone — the namespace owner gets a one-time warning email with a link to top up. When the balance does hit zero, the gate returns 402 on /v1, public endpoints, and MCP — the namespace is paused until you add funds. Your data is untouched; only new requests are blocked. The console billing page stays reachable so you can top up.

Billing needs two secrets — no meters, no Prices, no products:

  • STRIPE_SECRET_KEY — Checkout sessions.
  • STRIPE_WEBHOOK_SECRET — verifies the inbound webhook.

In the Stripe dashboard, add a webhook endpoint pointing at POST /api/stripe/webhook and subscribe it to checkout.session.completed. Copy its signing secret into STRIPE_WEBHOOK_SECRET. Do this once per stack (test mode for staging, live mode for production).