Why Are My Firestore Reads So High? 7 Causes in a Flutter App and How to Find Each (2026)

By giosaia (GSPTeck) · Published 2026-10-18 · Last verified 2026-10-18

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

Grid of six Firestore billing rules: one read per document returned, a minimum of one read per query, 50,000 free reads a day, a full re-read after 30 minutes offline, count() at one read per 1,000 index entries, and rules get() reads
Firestore billing rules.

From Understand Cloud Firestore billing, checked today:

The seven causes, in the order to check them

Two columns of seven causes of high Firestore reads: in your code, missing limits, offset pagination, streams created in build() and undetached listeners; outside it, console browsing, rules lookups, counting by download and reconnects
Seven causes.

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

Grid of a worked feed example for 300 users opening a screen 8 times a day: 144,000 reads with no limit, 48,000 with limit 20, about 6,000 plus updates with one listener, against 50,000 free reads
One feed screen, three versions.

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:

VersionReads per openDaily readsAgainst the 50,000 free reads
No limit, 60 posts60300 × 8 × 60 = 144,000Almost 3× the free tier
.limit(20)20300 × 8 × 20 = 48,000Just under
Limit 20, stream kept in initState, one listener per user per day (assumes one session)20 for the first open, then changes onlyRoughly 300 × 20 = 6,000 plus updatesAbout 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

Four-step flow to find the cause: read the shape of the Usage curve, run Query Explain with analyze, log listener creations in debug builds, set a budget alert
Find the cause.

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.

Vertical Pinterest graphic listing why Firestore reads climb in a Flutter app
Pinterest pin: why Firestore reads climb.

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