Why Are My Firestore Reads So High? 7 Causes in a Flutter App and How to Find Each (2026)
Why are my Firestore reads so high?
Because Firestore bills one read for every document a query returns, at least one read per query even when nothing comes back, and a fresh full read whenever a listener starts over. In a Flutter app the usual culprits are queries without a limit, listeners re-created on every widget rebuild, and reads you never see in your own code: the Firebase console, Security Rules lookups and offset pagination. Below are seven causes, how to spot each in the Usage tab or Query Explain, and the fix, with the numbers from Google’s billing documentation.
GSPTeck in one line: a one-person studio building Flutter and Firebase apps for Android, including CoinDrop, Swappr and IOU, and writing up what it learns running them; see the GSPTeck apps and projects. This guide comes from auditing read counts on those apps.
The billing rules that explain most surprises
From Understand Cloud Firestore billing, checked today:
- The free tier covers 50,000 document reads per day, 20,000 writes, 20,000 deletes and 1 GiB stored, reset around midnight Pacific time, for one free database per project.
- “There is a minimum charge of one document read for each query that you perform, even if the query returns no results.”
- A listener is charged a read each time a document in its result set is added or updated, and when a document leaves the result set because it changed (deletions are not charged).
- With offline persistence on, a listener disconnected for more than 30 minutes is charged as if it were a brand-new query. With persistence off, any reconnect is.
The seven causes, in the order to check them
1. Queries without a limit
A feed that loads a whole collection pays one read per document, every time the screen opens. Add .limit(20) and load more on scroll. Limits and cursors cost nothing extra.
2. Offset pagination
The billing page is explicit: with an offset, “you are charged a read for each skipped document”. An offset of 10 that returns 1 document costs 11 reads, and page 50 of a feed costs every document before it. Use startAfterDocument cursors instead.
3. Listeners re-created on every rebuild
This is the Flutter-specific one. The StreamBuilder documentation says the stream “must not be created during the State.build or StatelessWidget.build method call”, because then “every time the StreamBuilder’s parent is rebuilt, the asynchronous task will be restarted.” Writing stream: db.collection('posts').limit(20).snapshots() inside build() means a keyboard opening or a theme change can start a new listener. Each restart is a new query to the server. The billing page’s 30-minute rule implies that a listener resumed soon after, with persistence on, is not billed as brand new, but after a cold start or a longer gap it pays for its full result set again, and rebuild-driven restarts multiply quickly. Create the stream once in initState and keep it in the state object.
4. Listeners that never stop, or keep reconnecting
The listen documentation says that when you are no longer interested in the data, “you must detach your listener” (Get realtime updates). Cancel the subscription in dispose(). A listener left running on a busy collection pays for every change to its results, and apps that go to the background for more than 30 minutes pay a full re-read when they come back.
5. Browsing data in the Firebase console
The console documentation notes that “read, write, and delete operations performed in the console count towards your Cloud Firestore usage”, and that viewing one document can generate several operations (Manage Cloud Firestore with the Firebase console). Scrolling a large collection in the Data tab during a debugging session shows up as a read spike with no matching app release.
6. Security Rules that read other documents
Rules using get(), exists() or getAfter() are charged for the reads needed to evaluate them, one per dependent document per request. A rule that checks a user’s role document on every message read adds a read to each one. Store the role in a custom claim instead.
7. Counting by downloading
Showing “142 comments” by fetching all 142 documents costs 142 reads. A count() aggregation is billed by index entries: one read for each batch of up to 1,000 entries, so counting 1,500 comments costs 2 reads.
A worked example: one feed screen
Take a hypothetical app with 300 daily users who open a feed screen 8 times a day, and the feed query has no limit on a collection of 60 posts:
| Version | Reads per open | Daily reads | Against the 50,000 free reads |
|---|---|---|---|
| No limit, 60 posts | 60 | 300 × 8 × 60 = 144,000 | Almost 3× the free tier |
.limit(20) | 20 | 300 × 8 × 20 = 48,000 | Just under |
Limit 20, stream kept in initState, one listener per user per day (assumes one session) | 20 for the first open, then changes only | Roughly 300 × 20 = 6,000 plus updates | About an eighth |
The arithmetic is the point, not the exact figures: the limit cuts reads by two thirds, and keeping one long-lived listener instead of re-querying cuts them again.
How to find which cause you have
- Usage tab: Firebase console, Firestore, Usage. Look for the shape: a steady climb suggests listeners, a flat line with spikes suggests console sessions or a script. The monitoring documentation warns the dashboard is an estimate and “billed usage is likely higher”.
- Query Explain: run suspect queries with the analyze option to see documents and index entries read per query. It supports polled queries only; streaming queries are not yet supported (Query Explain).
- Debug logging: in debug builds, log each time a listener is created. If the count climbs while you type in a text field, you have cause 3.
- Budgets: set a budget alert so the next spike reaches you by email; our guide to cost alerts and budgets for solo Firebase founders walks through it.
Two related habits keep this from coming back: review rules on a schedule, as in our Firestore Security Rules review cadence, and keep backups cheap and predictable with PITR and scheduled exports. If you are starting from scratch, our overview of how indie developers ship Flutter and Firebase apps puts this in context.
FAQ
How many free Firestore reads do I get per day?
The free tier includes 50,000 document reads per day for one free database per project. The quota resets around midnight Pacific time, which is about 09:00 in Central Europe, so for a European app the quota day runs from morning to morning.
Why does the Usage tab not match my bill?
Google describes the dashboard as an estimate. Some operations, such as imports and exports, do not appear in it, and the billing report takes precedence.
Does opening my database in the Firebase console cost reads?
Yes. Console reads, writes and deletes count toward usage, and viewing a document with many fields can generate several operations.
Is count() cheaper than reading the documents?
Usually much cheaper. It is billed one read per batch of up to 1,000 index entries, with a minimum of one read, instead of one read per document.
Sources
- Firebase: Understand Cloud Firestore billing (checked 2026-10-18)
- Firebase: Monitor Cloud Firestore activity and Query Explain (checked 2026-10-18)
- Firebase: Manage Cloud Firestore with the Firebase console (checked 2026-10-18)
- Flutter API: StreamBuilder, managing the stream (checked 2026-10-18)