---
title: "Prisma Postgres pricing: count the calls behind your app"
date: 2026-10-31
description: "Free and paid database rates, the Starter-to-Pro crossover, and a locally tested catalogue showing how thirteen SQL calls become one."
tags:
  - "free-tier"
  - "cloud-infrastructure"
  - "side-projects"
---

Prisma Postgres starts at **$0 for 200,000 operations a month and 2 GB of storage**. Paid plans start at **$10 a month**, with a base fee, included usage and charges above the allowance. The important question is how many operations your application generates: a page showing twelve bookmarks can issue thirteen SQL queries or one, while displaying exactly the same records.

The prices below were checked on **27 October 2026** and rechecked on **3 November 2026**. The catalogue example was run locally; it is not a hosted Prisma billing measurement.

## What Prisma Postgres costs

These are the database allowances and rates on the [current Prisma pricing page](https://www.prisma.io/pricing). Prices are in US dollars.

| Plan | Monthly base | Operations included | Extra operations | Storage included | Extra storage |
| --- | --- | --- | --- | --- | --- |
| Free | $0 | 200,000 | No paid overage | 2 GB | No paid overage |
| Starter | $10 | 1 million | $8 per million | 10 GB | $2/GB |
| Pro | $49 | 10 million | $2 per million | 50 GB | $1.50/GB |
| Business | $129 | 50 million | $1 per million | 100 GB | $1/GB |

Free needs no card and allows fifty databases. Its operation and storage allowances are [shared across the workspace](https://www.prisma.io/docs/full-stack-tutorial#what-the-free-plan-includes), which contains your projects and databases. Those fifty databases share one budget.

For a paid database estimate, add the base fee, operation usage above the included amount and storage above the included amount. Clamp both excess amounts to zero. Database data transfer is included; your application host can still charge for serving responses.

Paid plans [include a hard spend limit you can set in the Console](https://www.prisma.io/pricing). Set your cap in the Console and compare it with your estimated monthly bill.

If you host the app on Prisma Compute, budget its request, memory, CPU and outbound-bandwidth meters alongside the database. The [Compute allowance table](https://www.prisma.io/docs/full-stack-tutorial#prisma-compute) gives the separate hosting allowances.

## When to leave Free, and when Starter stops being cheaper

There are three useful triggers: your projected operations exceed 200,000 a month, your database needs more than 2 GB, or you need automated backups. That last one can arrive before you have much traffic. Free has none.

For an operation-heavy app using no more than 10 GB, Starter and Pro reach the same database-only cost at **5.875 million monthly operations**:

```text
Starter: $10 + (5.875 − 1) × $8 = $49
Pro:     $49, within its 10 million included operations
```

Above that point, Pro is cheaper under those assumptions. At eight million operations, Starter is $66 and Pro is $49. Additional storage or app hosting changes the comparison; the cheaper base plan is not automatically the cheaper bill. These are calculations from the published rates, not invoices.

For Free, the practical tipping point can be a chatty page rather than a large user count. Sixteen thousand list renders at thirteen SQL queries each would use **208,000 operations** under the ordinary-SQL model, before details, writes or maintenance. Reducing that route to one query changes the projection to 16,000. The example below shows where those twelve extra queries came from.

There is also a qualification in Prisma's own material: its [FAQ describes Free as intended for evaluation rather than production](https://www.prisma.io/docs/postgres/faq#is-there-a-sample-workload-to-estimate-my-expected-charges). Staying inside a numeric allowance does not establish that the service is suitable for data you cannot afford to lose.

## An operation is not always a SQL statement

[Prisma's operation definition](https://www.prisma.io/docs/postgres/faq#how-does-pricing-differ-between-using-prisma-orm-and-direct-tcp-connections) distinguishes a Prisma Client query from a SQL query issued through another tool. A single Prisma Client call can produce several SQL statements. Counting those statements as separate billed Client operations would overestimate that route.

The [original operations-billing explanation](https://www.prisma.io/blog/operations-based-billing) illustrates this with a nested write: one Client call, several underlying statements. Conversely, one HTTP request can make many separate database calls. Name the layer you are counting before multiplying it by traffic.

The following example counts explicit SQL calls from a small JavaScript catalogue. It uses no ORM, so it does not establish Prisma Client billing behaviour. Applying its counts to hosted pricing is a documented ordinary-SQL estimate that still needs checking against the service's usage reporting.

## Twelve bookmarks, thirteen queries

The fixture contains twelve bookmarks in three categories. A straightforward implementation reads the bookmarks, then looks up the category for each card:

```js
const cards = [];
for (const b of await query('SELECT * FROM bookmark ORDER BY id LIMIT 12')) {
  const [c] = await query('SELECT name FROM category WHERE id=$1', [b.category_id]);
  cards.push({ id: b.id, title: b.title, url: b.url, category: c.name });
}
```

That is one list query and twelve category queries, including repeated lookups of the same categories. Joining the category table returns the same card fields in the same order:

```sql
SELECT b.id, b.title, b.url, c.name AS category
FROM bookmark b
JOIN category c ON c.id = b.category_id
WHERE ($1::integer IS NULL OR b.category_id = $1)
ORDER BY b.id
LIMIT 12;
```

Here `$1` is either a category ID or `null` for all categories. Bind it as a parameter. The stable ID order matters because the comparison should preserve the page's contents, not merely return twelve rows.

The local harness wrapped `db.query()`, recording the route name, SQL, parameters and returned rows. Schema creation, seeding and test-control queries were outside the route counts.

| Catalogue action | Initial calls | Revised calls | Check |
| --- | --- | --- | --- |
| List twelve bookmarks with category names | 13 | 1 | Identical ordered card objects |
| Open bookmark details | 1 | 1 | Expected bookmark returned |
| Save an edited title | 1 | 1 | Updated title returned |
| Import ten bookmarks | 10 | 1 | Identical stored rows |
| Retry that import | — | 1 | Still ten imported IDs |
| Filter to a category with no bookmarks | — | 1 | Empty result |

These were actual local calls using **Node 26.2.0 and PGlite 0.5.12**, which reported PostgreSQL 18.6 compiled to WebAssembly. [PGlite runs Postgres locally](https://pglite.dev/docs/) without a server account. This tested the SQL and application logic. It did not test Prisma's network connection, pooler or billing system; the provider's current backup documentation refers to hosted PostgreSQL 17.

To explore the same query shape locally, install the pinned runtime in an empty directory:

```sh
npm install --save-exact @electric-sql/pglite@0.5.12
```

Save this as `catalogue.mjs`, then run `node catalogue.mjs`:

```js
import { PGlite } from '@electric-sql/pglite';
const db = new PGlite();
await db.exec(`
  CREATE TABLE category (id integer PRIMARY KEY, name text NOT NULL);
  CREATE TABLE bookmark (
    id integer PRIMARY KEY, title text NOT NULL, url text NOT NULL,
    category_id integer NOT NULL REFERENCES category(id)
  );
  INSERT INTO category VALUES (1,'Code'), (2,'Making'), (3,'Reading');
  INSERT INTO bookmark
    SELECT n, 'Bookmark ' || n, 'https://example.com/' || n,
      1 + ((n-1) % 3) FROM generate_series(1,12) AS n;
`);
console.log((await db.query(`
  SELECT b.id, b.title, b.url, c.name AS category
  FROM bookmark b JOIN category c ON c.id = b.category_id
  WHERE ($1::integer IS NULL OR b.category_id = $1)
  ORDER BY b.id LIMIT 12
`, [null])).rows);
await db.close();
```

This is an in-memory fixture, discarded when the process closes. The data is synthetic. It is a way to inspect a query, not a persistence or production-hosting recipe.

## Batch the import without weakening the checks

For the ten-row import, replacing a loop of individual inserts with one parameterised statement reduced the recorded calls from ten to one:

```sql
INSERT INTO bookmark (id, title, url, category_id)
SELECT id, title, url, category_id
FROM jsonb_to_recordset($1::jsonb)
  AS x(id integer, title text, url text, category_id integer)
ON CONFLICT (id) DO UPDATE SET
  title = EXCLUDED.title,
  url = EXCLUDED.url,
  category_id = EXCLUDED.category_id
RETURNING id;
```

Pass a JSON array of bookmark objects as the single bound parameter. The primary key makes a retry update the existing IDs. It does not make the retry free: the harness recorded another call. Deduplicate IDs within each incoming batch and bound its size before using this shape in an application; the tested fixture had ten distinct IDs.

The checks compared all imported fields against the loop's result. They also retried the batch, supplied an empty filter, and saved an SQL-shaped title as literal data. A batch containing one valid row and one invalid category reference failed with foreign-key error `23503`; neither row was inserted. An unwrapped loop can leave earlier rows committed when a later one fails, so batching changes failure behaviour as well as call count.

## Keep a budget for the calls outside the page

Suppose a month contains 10,000 list renders, 10,000 detail views, 2,000 saves and 100 ten-row imports. Using the observed calls, the initial design projects to **143,000 SQL operations**; the joined-and-batched design projects to **22,100**. Those traffic counts are assumptions, not observed users.

That leaves 57,000 or 177,900 operations respectively below Free's allowance. Neither remainder is a promise of capacity. Other databases in the workspace, background jobs, admin tools, retries and database health probes also need room. Even a [`SELECT 1` probe counts](https://www.prisma.io/docs/postgres/faq#does-a-select-1-query-count-as-a-billable-operation).

Check actual usage in Prisma Console before treating the projection as a budget.

## Connection limits and recovery can decide the plan first

Use the [pooled connection for application traffic and the direct connection for migrations and exports](https://www.prisma.io/docs/postgres/database/connecting-to-your-database). Keep credentials on the server: every generated credential has full access to its database, rather than a selectable read-only or table-specific role.

Free lists [ten pooled and ten direct concurrent connections](https://www.prisma.io/docs/postgres/database/connection-pooling). The [Free-plan tutorial](https://www.prisma.io/docs/full-stack-tutorial#what-the-free-plan-includes) confirms that five of those ten direct connections are reserved for platform operations, leaving five for your workload. Reuse clients and limit concurrency across application instances. The monthly operation allowance does not remove this simultaneous-connection limit.

When Free's query allowance runs out, [reads and writes stop](https://www.prisma.io/docs/postgres/faq#what-happens-if-i-exceed-the-included-queries-on-the-free-tier). Upgrade or wait for the next calendar month. A workspace can activate one 24-hour grace period to regain access, including for an export; it is available once, not once each month. Blocked queries do not mean the data has been deleted.

Arrange recovery before that happens. Free has no automated backups. Paid plans retain daily snapshots for seven days on Starter and Pro, or thirty on Business; the [backup documentation](https://www.prisma.io/docs/postgres/database/backups) says snapshots are created on days with activity. They do not provide current point-in-time recovery. For your own export-and-restore process, use a direct connection and PostgreSQL 17 `pg_dump`/`pg_restore` tools, as the backup guide recommends, then test the restored data. No hosted dump or restore was tested for this example.

For a broader choice of services, the [maintained free-compute guide](/free-compute/) covers other database and hosting options. For this plan decision, start with one representative app route, count the calls at the client layer you actually use, and price the whole workspace. Then decide whether the remaining operation budget and recovery arrangements are enough for the data you intend to keep.
