Skip to main content
← Back to blog

How Firestore Read, Write and Delete Costs Are Calculated (and How to Keep Them Under Control)

6 min readUpdated ReadMeter Team

Firestore pricing looks straightforward: you pay per document you read, write or delete. In a real app those operations multiply quietly, through listeners, re-renders, pagination and the same query running in three components at once.

This guide covers what each operation costs, the billing rules that surprise people, and how to see the numbers while you develop instead of on next month's invoice.

The short version: Firestore prices

Firestore (Standard edition) charges per 100,000 document operations. Prices depend on where your database lives. Multi-region locations cost about twice as much as a single region:

Per 100,000Free per dayMulti-region (nam5, eur3)Single region (us-central1)
Reads50,000$0.06$0.03
Writes20,000$0.18$0.09
Deletes20,000$0.02$0.01

Default prices as of October 2026, before committed-use discounts. Other locations differ; check Google's pricing page for yours. The free quota is per project and resets every day, so it covers small apps completely and big apps barely at all.

Two things stand out:

  • A write costs three times a read. A page that writes on every load can cost more than one that reads ten documents.
  • Deletes are cheap but not free. Clearing a collection costs one delete per document, plus the reads you made to find them.

Storage and network egress are billed too, but for most apps the document operations are the bill.

How Firestore counts reads

One document returned is one read. A query that returns 50 documents costs 50 reads, whether or not you use every field and whether or not the data changed since you last fetched it.

const snapshot = await getDocs(
  query(collection(db, "orders"), where("status", "==", "open"))
);
// 120 matching orders → 120 reads

The rules that catch people out:

  • Nothing returned still costs 1 read. An empty query is billed one read, and so is getDoc on a document that doesn't exist. A loop that checks 30 days for an entry pays 30 reads even when it finds nothing.
  • limit() caps the cost. It is the simplest lever you have: limit(20) on a 2,000-document collection is 20 reads, not 2,000.
  • Counts and aggregations are cheap. count(), sum() and average() cost 1 read per batch of up to 1,000 index entries, with a minimum of 1. Use them instead of fetching documents just to count them.
  • Cache reads are free. getDocFromCache and getDocsFromCache never reach the server and are not billed.

Real-time listeners

onSnapshot is billed like a query that keeps running:

  • Attaching a listener reads every document in the result, like getDocs.
  • After that, each document that is added, changed or removed from the result costs 1 read.
  • Reconnecting after more than 30 minutes offline is billed as a brand-new query (with offline persistence on). Without persistence, every reconnect is.
onSnapshot(query(collection(db, "posts")), (snapshot) => {
  // 1,000 posts → 1,000 reads on attach
  // 200 posts edited later in the day → 200 more reads
});

The expensive mistake is a listener that is re-created on every render or navigation: each one pays the full initial result again.

How writes are counted

Every document you create or change is one write. setDoc, addDoc and updateDoc each cost one write, however many fields change. A setDoc with { merge: true } is still one write.

  • Batches don't discount anything. A writeBatch with 50 updates is 50 writes. Batching makes the writes atomic; it doesn't make them cheaper.
  • Transactions bill the reads too. Every transaction.get() is a read, and when Firestore retries a transaction because of contention, the reads run again and are billed again. The writes count once, from the attempt that commits.

Writes are where "harmless" code gets expensive, because nothing on screen shows them. A lastSeen timestamp updated on every navigation, a view counter bumped from a component that renders three times, or one updateDoc per item in a list all look fine in review and add up at 3× the read price.

How deletes are counted

Every document you delete is one delete, at the lowest of the three prices. The catches:

  • Deleting a collection is per document. There is no single "drop collection" operation in the client SDK, so you read the documents to find them and then delete each one.
  • Subcollections aren't deleted with their parent. Deleting a document leaves its subcollections in place; removing them is more deletes.

A worked example

Take a dashboard page on a multi-region database, loaded 10,000 times a day. Each load reads 40 documents and writes one lastSeen timestamp.

Per dayOver the free tierPer month (30 days)
Reads: 40 × 10,000400,000350,00010.5M → $6.30
Writes: 1 × 10,00010,0000$0

Now say the lastSeen update sits in an effect that runs five times per load:

Per dayOver the free tierPer month (30 days)
Writes: 5 × 10,00050,00030,000900K → $1.62

Neither number is dramatic on one page. Repeat that across a whole app, add a list that reads 300 documents instead of 20 and a few listeners re-created on navigation, and small per-page numbers become a bill in the hundreds.

Why costs add up faster than expected

It's rarely one bad query. It's usually a mix of:

  • The same query running several times per load: sibling components mounting the same hook, effects with unstable dependencies, fetches started from render. Firestore bills every run.
  • Listeners attached too broadly, or re-attached on every navigation.
  • Whole collections fetched where a filtered, limited query would do.
  • Pagination that overlaps, so the same documents are read on two pages.
  • Writes on every load or render that nobody meant to repeat.

All of these are invisible in development. The Firebase console shows daily totals, not the screen or function that caused them.

How ReadMeter helps

ReadMeter is a localhost widget that counts the Firestore operations behind every page while you build:

  • Reads per page load, with the calling function, the collection and how many times each query ran. A query that runs five times shows up as 5×.
  • Writes and deletes, counted apart from reads and priced at their own rates, so a page that writes on every load stands out.
  • A fix prompt for Claude Code, Cursor or any coding agent, with the numbers and the source line, so the fix starts from evidence.
  • readmeter audit, which loads every route of your app, ranks what to fix across all of them, and measures again after the fix.
  • readmeter check, which fails CI when a page goes over a read, write or delete budget.

Counts are estimates of what the client SDK does (and the Admin SDK, with one wrap), not your invoice. They use the multi-region prices above by default, and you can set your own.

Practical takeaway

Firestore pricing is simple: you pay per document read, written and deleted. What makes it expensive is how modern frontends behave: listeners, re-renders, repeated fetches and writes nobody meant to repeat.

Measure reads and writes during development, on the pages you're building, and you fix them while they're cheap to fix instead of after the bill arrives.

See your Firestore reads while you build

Get visibility into read patterns before traffic arrives. $9 once, on up to 3 of your machines.

Get ReadMeter — $9 (one-time)