Guide
Firestore reads too high? How to find what's reading
November 3, 2026 · 6 min read
Your Firestore read count jumped and you don't know why. Where to see it, how to tell which kind of read it is, and the code patterns that multiply reads without anyone noticing.
1. See when it started
Open your database's Usage page, in the Firebase console (Firestore > Usage) or in the Google Cloud console. It shows reads, writes and deletes over time. Find the day, and if you can the hour, when the curve changed, and compare it with your deploys: most read spikes start with a release.
Two things to know about this dashboard. It's an estimate: Google says "the dashboard is not an exact view of billed operations. Billed usage is likely higher." (Monitor Firestore usage). And it trails a little: "updates may take up to 4 minutes to show up in your dashboards."
2. Split lookups from queries
In Cloud Monitoring, open Metrics Explorer and chart the Firestore metric Document Reads. You can break it down by type: LOOKUP for single documents read by path, QUERY for documents returned by queries and listeners. Two more metrics help: Snapshot Listeners and Active Connections.
- Mostly QUERY: a query reads too many documents, or a listener re-reads its results over and over.
- Mostly LOOKUP: something fetches documents one by one, often in a loop or inside your Security Rules.
- Listeners growing faster than users: your app opens listeners and never closes them, or opens a new one on every render.
3. The usual causes
- Queries without a limit. A page that loads a whole collection to show the first 20 reads every document in it, on every visit. Add
limit()and paginate with cursors. This is how $30,356 in 72 hours happened. - Listeners that subscribe again. In React, an
onSnapshotinside auseEffectwhose dependencies change on every render opens a new listener each time, and each new listener reads its whole result set again. On the web, offline persistence is off by default, and Firestore's pricing page is clear about what that costs: "If offline persistence is disabled, you will be charged for documents and index entries read as if you had issued a brand-new query whenever the listener disconnects and reconnects." - Reads inside Security Rules. Every
get()orexists()in your rules reads a document. "You are charged for reads that are necessary to evaluate your Cloud Firestore Security Rules" (Firestore pricing). A rule that looks up the user's profile on every request doubles the reads of a busy screen. - Counting by reading. Loading a collection to show how many items it has reads every document. A
count()aggregation is billed by index entries instead: "count() operations that read between 0 and 1000 index entries are billed for one document read." - Polling. A timer that runs a query every few seconds pays for it every time, results or not, since Firestore bills "a minimum charge of one document read for each query that you perform, even if the query returns no results". Use a listener, or poll much less often.
- Functions that read on every write. A Cloud Function triggered by each write that then reads a collection multiplies your traffic. If it also writes to the documents that trigger it, it can loop.
- Someone else. If your rules let anyone, or any signed-in user, read a collection, a script with your public config can read it all day. The Firebase console shows how many requests your rules evaluate, Cloud Monitoring can alert you when denied requests spike, and App Check stops requests that don't come from your app.
The React case, in code
The first effect below subscribes again whenever filters is a new object, which in many components is every render. The second depends on a plain value and caps the result:
// Subscribes again every time `filters` is a new object
useEffect(() => {
const q = query(collection(db, "orders"), where("status", "==", filters.status));
return onSnapshot(q, (snap) => setOrders(snap.docs));
}, [filters]);
// Subscribes once per status, and reads at most 50 documents
const status = filters.status;
useEffect(() => {
const q = query(collection(db, "orders"), where("status", "==", status), limit(50));
return onSnapshot(q, (snap) => setOrders(snap.docs));
}, [status]);4. Check one query with Query Explain
Once you suspect a query, run it with Query Explain. It reports the read operations the query was billed for and the index it used, so you can check a fix before you deploy it (Query Explain).
5. Get told next time
A budget alert reaches you hours after the reads, so watch the reads themselves. In Cloud Monitoring, create an alerting policy on Document Reads with a threshold a little above your normal peak.
Or let FireShield do it. On Pro it reads each service's usage every few minutes, compares the last hour with that service's normal hour over the past week, and alerts you, or switches the expensive part off, when Firestore reads jump.
