Home/Blog/Dynamic Number Insertion Explained: How DNI, Number Pools, and Session Tracking Actually Work

Dynamic Number Insertion Explained: How DNI, Number Pools, and Session Tracking Actually Work

CallFlux Team July 25, 2026 11 min read
A web designer's desk with a smartphone, laptop and wireframe sketches, representing dynamic number insertion on a website

Every inbound phone call carries the same information by default: a number, a time, and a duration. Nothing about which ad, keyword, or landing page produced it. Dynamic number insertion is the mechanism that fixes that — a few kilobytes of JavaScript that turns an anonymous ring into an attributed conversion.

It is also the piece most often deployed badly. A pool sized too small silently merges two visitors into one attribution record. A tracking number hardcoded into a footer template leaks into directory citations. A script placed after a lazy-loaded header swaps nothing at all. This guide covers what the technology actually does, how the two flavors differ, how pools are sized, and the deployment rules that keep your attribution clean and your local SEO intact.

What DNI does, step by step

The short version: DNI decides which phone number a given visitor should see, shows it to them, and remembers why.

Here is the full sequence for a single visitor:

  1. The visitor arrives. They click a Google Ads listing, a Facebook ad, or an organic result and land on your page.
  2. The script reads the context. On page load, the DNI snippet inspects the document referrer, the URL query string (gclid, msclkid, fbclid, and any utm_source / utm_medium / utm_campaign / utm_term parameters), the landing-page path, and any first-party cookie from a previous visit.
  3. A number is assigned. The platform matches that context against your tracking rules and either returns a fixed number for the source or checks out a unique number from a pool for this session.
  4. The number is swapped in the DOM. Every visible instance of your business number — header, hero, footer, sticky mobile call bar, tel: links — is replaced with the assigned number.
  5. The session is held. The number stays assigned to that visitor for the duration of the session and a configurable window after it, so a prospect who reads, leaves, and calls twenty minutes later still resolves correctly.
  6. The call arrives and is matched. When the tracking number rings, the platform looks up which session held it, attaches the full source context to the call record, and forwards the call transparently to your real business line. The caller hears nothing unusual.
  7. The number is released. After the session and its cooldown expire, the number returns to the pool for the next visitor.

Everything valuable about call tracking flows from step 6. Once a call record carries gclid, keyword, campaign, and landing page, you can push that call — and eventually its revenue — back into Google Ads as a conversion. That is the loop described in our Google Ads call tracking guide, and DNI is the part that makes it possible.

Source-level vs session-level: the decision that sets your ceiling

There are two implementations, and choosing between them fixes how granular your data can ever be.

Source-level DNI assigns one number per traffic source. Google Ads visitors all see (817) 555-0110. Facebook visitors all see (817) 555-0111. Organic sees a third. When a call comes in, you know the channel and nothing finer.

Session-level DNI assigns a distinct number to each concurrent visitor from a shared pool. Because only one visitor holds a given number at a given moment, the platform can resolve a call back to that visitor's exact keyword, ad, and landing page.

Source-level DNISession-level DNI
Numbers requiredOne per channel (3–8 typical)Pool sized to peak concurrent visitors
Attribution granularityChannel onlyCampaign, ad, keyword, landing page
Monthly number costVery lowModerate — scales with traffic
Google Ads conversion importChannel-level onlyKeyword-level via gclid
Best fitLow-traffic sites, offline media, brand-awareness trackingPaid search, any account optimizing at keyword level
Failure modeNone — but the data ceiling is lowPool exhaustion causes number sharing

Most businesses end up running both. Source-level numbers are ideal for offline and static placements — a vehicle wrap, a direct-mail piece, a print ad, a Yelp listing — where there is no web session to key off. Session-level DNI covers the website. On CallFlux, both live in the same phone-number pool and report into the same dashboard, so you are not stitching two systems together.

Sizing a number pool without guessing

The most common sizing error is estimating from monthly traffic. Monthly volume is irrelevant. What matters is peak concurrency — the maximum number of tracked visitors on your site at the same moment, plus everyone still inside their session-hold window.

A workable method:

  1. Find your peak hour. In your analytics platform, look at sessions per hour for the channels you intend to track with session-level DNI, and take the busiest hour of a busy week — not the average.
  2. Convert to concurrency. Divide peak hourly sessions by the number of session windows that fit in an hour. If your session-hold is 30 minutes, there are two windows per hour, so 60 peak sessions per hour implies roughly 30 concurrent holds.
  3. Add headroom. Traffic is bursty; a single ad approval or a local news mention can double it for an afternoon. A 30–50% buffer over calculated concurrency is standard practice.
  4. Re-check quarterly. As spend grows, so does concurrency. Pool sizing is not a one-time setup task.

The economics favor generosity. At $1.15 per local tracking number per month, doubling a 20-number pool costs about $23 a month. A single misattributed job worth several hundred dollars wipes out a year of that difference. Under-provisioning to save number fees is one of the few places in marketing operations where the cheap choice is reliably the expensive one.

There is one honest caveat: pool sizing only matters for the traffic you route through session-level tracking. If 90% of your visitors are organic and you only need keyword-level data on paid search, size the pool for paid search concurrency and put organic on a source-level number. Nobody needs a hundred-number pool to track a brand-name query.

Deploying DNI without breaking local SEO

This is the objection that stops most local businesses from ever turning DNI on, and it is based on a real risk that is easy to eliminate.

The risk: your NAP — Name, Address, Phone — needs to be identical across your website, your Google Business Profile, and the directory citations that reference you. If a tracking number becomes the number search engines and scrapers associate with your business, you get inconsistency across dozens of listings, which is genuinely bad for local ranking signals.

The fix is a set of rules, not a compromise:

  • Your real number lives in the server-rendered HTML. Whatever a crawler receives before JavaScript executes must be your canonical business line. DNI is a client-side swap layered on top.
  • Your structured data never changes. The telephone property in your LocalBusiness schema stays hardcoded to the real number. Do not let the DNI script touch JSON-LD.
  • Your Google Business Profile keeps the real number as primary. If you want to measure GBP calls, use a dedicated tracking number in the secondary-number field — Google's own guidance supports a tracking number as an additional number while the primary stays canonical.
  • Directory citations reference the real number, always. No tracking numbers in Yelp, Angi, BBB, or chamber listings.
  • Swap by selector, not by regex over the whole page. Tag your number instances with a class or data attribute so the script knows precisely what to replace. Blind text-node scanning is slower and occasionally rewrites something it should not — an invoice number, an address suite, a year in a testimonial.

Follow those five and DNI is invisible to search engines and precise for humans. The tracking number only ever appears in a rendered browser session, which is exactly the audience it is meant to measure.

The failure modes worth knowing about

Flicker. The real number renders, then swaps a beat later, and the visitor sees it change. Fix it by loading the script early rather than deferring it to the bottom of the body, and by keeping the swap target a small set of selectors so the replacement completes in the same paint.

Single-page-app re-renders. On React, Vue, or similar frameworks, client-side navigation replaces the DOM without a full page load, and a naive DNI script never re-runs. The number reverts to the hardcoded default on every route change. The fix is calling the swap on route change, or rendering the number from a state value the script sets once.

Cached HTML serving a stale number. If a CDN caches a page after the swap fired for one visitor, every subsequent visitor sees that visitor's number. This is why the swap must happen client-side per session and never be baked into a cacheable server response.

Sticky mobile call bars missed by the swap. These are frequently rendered by a separate component or plugin and use a raw tel: href that the selector list forgot. On a mobile-heavy service business, that single missed element can leak the majority of your calls out of attribution. Audit on a real phone, not a desktop emulator.

Pool exhaustion during a spike. When every number is checked out, the platform must fall back to the default number, and those calls arrive unattributed. Well-built platforms alert on this. If your reports show a sudden cluster of unattributed calls at your busiest hour, exhaustion is the first thing to check.

Where DNI ends and analysis begins

DNI is plumbing. It gets accurate source data attached to a call record, which is a prerequisite for everything downstream and valuable for nothing on its own. The payoff arrives when that attributed call is also transcribed, summarized, and scored — when you can see not only that "brake key replacement near me" produced eleven calls last week, but that seven of them were qualified, four booked, and one was a robocall you should exclude from your cost-per-lead math.

That is the layer CallFlux adds on top of the tracking mechanics: every call captured through the DNI and call-tracking stack is automatically transcribed and run through AI call insights for summary, intent, and lead score. Flat-rate pricing means the number of calls that flow through it never changes your bill — only the size of your number pool does, at $1.15 a month per local line.

If you are still reading a single business number on every page of your site, every marketing dollar you spend is producing calls you cannot tell apart. See how CallFlux deploys DNI and start separating them.

Frequently Asked Questions

What is dynamic number insertion (DNI)?

Dynamic number insertion is a small JavaScript snippet that replaces the phone number displayed on your website with a trackable number chosen based on how each visitor arrived. When that visitor calls, the platform knows which channel, campaign, keyword, and landing page produced the call, then routes the call transparently to your real business line. Without DNI, every inbound call looks identical no matter what marketing paid for it.

What is the difference between source-level and session-level DNI?

Source-level DNI assigns one fixed number per traffic source — one for Google Ads, one for Facebook, one for organic search. It is cheap and tells you the channel but not the keyword. Session-level DNI draws a unique number from a pool for each individual visitor session, so a call can be traced to the exact keyword, ad, and landing page. Session-level costs more because it needs more numbers, but it is the only way to get keyword-level call attribution.

How many tracking numbers do I need in my pool?

Pool size is driven by concurrent visitors, not total monthly traffic. Estimate your peak concurrent sessions from the channels you are tracking, then provision enough numbers that each active visitor can hold a distinct number for the length of their session plus a cooldown window. Too few numbers and two visitors share one, which collapses attribution; a modest surplus is far cheaper than corrupted data. On CallFlux, local tracking numbers are $1.15 per month, so over-provisioning is inexpensive insurance.

Does dynamic number insertion hurt local SEO or NAP consistency?

Not when it is deployed correctly. The rule is that your real business number stays in the server-rendered HTML, your structured data, your Google Business Profile, and your directory citations. DNI swaps the visible number client-side, per session, after the page loads. Search crawlers and citation scrapers see your canonical number; human visitors from tracked campaigns see a tracking number. Problems only arise when a tracking number is hardcoded as the primary NAP.

Will DNI slow down my website?

A well-built DNI script is a few kilobytes and loads asynchronously, so it does not block rendering. The main perceived risk is a visible flicker where the real number appears briefly before the swap. That is avoided by matching the number consistently across the page, keeping the script early in the document, and letting the swap target a CSS class or data attribute rather than scanning the entire DOM repeatedly.

Can DNI track calls from organic search and Google Business Profile?

DNI tracks any visitor who lands on your website, including organic search, email, and referral traffic — the script reads the referrer and URL parameters regardless of channel. It cannot track a call placed directly from your Google Business Profile listing, because that call never touches your site. For those, you use a separate dedicated tracking number configured as the profile's primary number, which is a different technique with its own NAP considerations.

Sources

Ready to track every call?

Start your free trial and see exactly which marketing channels drive phone calls.

Get Started Free