A Next.js app grows, traffic climbs, and suddenly the API bill, or the database one, is the fastest-growing line item. The usual cause of those costs isn’t traffic itself; it’s the same data being fetched far more often than it changes.

Where Next.js API costs come from
Before changing any code, it helps to know which habit is behind the bill. In my experience it is almost always one of these:
- Per-request fetches of slow-changing data. Product lists, settings and content pulled fresh on every page view.
- Fetching from the browser. Every visitor calls the API separately, so nothing is shared between users.
- Duplicate and sequential calls. Several components ask for the same thing, one after another.
- Over-fetching. Full records come back when the page shows three fields.
- Polling. A dashboard that refreshes every few seconds in every open tab, whether anyone is looking or not.
- Bots. Crawlers count as traffic too, and every uncached page they hit is a fresh set of API calls.
Cache by how often data updates
If every page load hits an external API or database for data that updates hourly, you’re paying for thousands of identical requests. Next.js gives you layers: cache fetch responses, use Incremental Static Regeneration to rebuild pages on a schedule instead of per request, and cache expensive queries. Data that changes hourly should be fetched hourly, not on every visit.
In recent versions of Next.js, fetch is not cached by default, so caching is something you opt into. The Next.js caching documentation covers the model for your version. The patterns I use most are these:
- With Cache Components enabled, put the
'use cache'directive at the top of a data function and give it a lifetime withcacheLife('hours')or a profile that matches the data. - On the previous caching model, set a revalidation time on the request, for example
fetch(url, { next: { revalidate: 3600 } }). - When data changes on a known event, such as an editor publishing a post, revalidate by tag or path at that moment instead of using a short timer.
- For database and SDK calls that do not use
fetch, wrap the query in a cached function so it follows the same rules.

Kill waterfalls and duplicate calls
Components that each fetch the same thing, or fetch sequentially when they could run in parallel, multiply requests. Fetch shared data once, higher up, and pass it down.
When two requests do not depend on each other, start them together with Promise.all instead of awaiting one before the next. When several server components need the same record in one render, wrap the loader in React’s cache so the call runs once per request.
Move work to the server, too. Fetching from the client means each user’s browser hits your API directly, and you can’t cache across users. Server Components and server-side fetching let you fetch once and reuse the result. And request only what you need: pulling full objects when you use three fields wastes bandwidth and, on metered APIs, money.
Deciding what runs where is its own skill, and server components vs client components gives a simple rule for it. If a client request fires twice in development, check Strict Mode before blaming your code; why your React app re-renders twice on every click explains why.
Polling and bots need their own fixes. For a dashboard, poll less often, pause polling when the tab is hidden using document.visibilityState, and only refetch when something has changed. For crawlers, make sure public pages are served from cache. A bot reading a hundred pages should cost you a hundred cache hits, not a hundred rounds of API calls.
Session checks are another hidden cost. If every request validates the session against an external auth service, a busy app pays for that on every page. Read the session from the cookie for quick routing decisions, and keep the full check close to the data. I cover that split in handling auth redirects in Next.js without the flicker.
How to find what is costing you
- Open your provider’s usage dashboard and sort endpoints by request count, not by cost alone.
- Log outgoing requests on the server with the route that made them, for one day.
- Compare requests to page views. If one page view triggers ten identical calls, you have found your leak.
- Check your host’s cache headers on the pages you expect to be cached, to confirm they really are.
Mistakes to avoid
- Caching user-specific data under a shared key. One customer can end up seeing another’s details.
- Setting revalidation to a second or two, which is barely different from no cache.
- Disabling caching everywhere to fix one stale page, instead of revalidating that page when its data changes.
Most cost blowups come down to one habit: treating data that rarely changes as if it changes every request. Cache by how often data actually updates and the bill usually drops sharply. If yours keeps climbing and you want a second pair of eyes, this kind of audit is part of my web application development work.




