Skip to main content

Ask an agent which ad produced their last closing and you get a shrug. The CRM says "Web Lead." Ads Manager says 41 leads at $22. Nobody connects those two facts, so budget moves on feel — and the campaign that produced two listings gets cut for an ugly cost per lead.

Why is real estate harder to track than almost any other lead gen?

Because the money event lands 3 to 18 months after the click, and by then every tool in the chain has lost the trail. Three things break in that gap:

  • Cookies expire. Safari's ITP caps cookies written by JavaScript at seven days. Seven-day memory, nine-month sales cycle.
  • Platform windows close. Meta's attribution settings max out at 7-day click. Google Ads stretches further — up to 90 days for click-through on most conversion actions — but that still doesn't reach a spring-to-fall closing.
  • Everything claims credit. The portal, the lead ad, the branded search, the referral partner — all "right," and you still don't know where the next dollar goes.

And deal values are enormous relative to lead costs, so cost per lead is a terrible steering metric. Chasing a $12 CPL is how you buy five hundred renters.

Should you track portal leads or your own site leads?

Measure what you control, which means your own site. Portal marketplaces hand you a name and a phone number with no click ID, no keyword, no campaign — the attribution happened inside their platform and they aren't sharing it. Handle them the boring way: one fixed CRM source per portal, the invoice as the cost, and a quarterly cost-per-closing you calculate by hand. A purchasing decision, not an optimization loop.

How do you track IDX leads when the property search is a third-party iframe?

If your IDX search loads in an iframe from the vendor's domain, you get nothing — no page views, no form submits, no click ID carried across, no shared cookie. The visitor is on your site visually and on someone else's site technically. It's the most common tracking hole on a real estate website, and nobody notices because the leads still arrive by email.

Your options, best to worst:

  1. Same-domain JavaScript widget. Some search platforms render into your page instead of an iframe. Ask whether they push events to the dataLayer.
  2. A subdomain on your own root domain. search.yourdomain.com beats yourbrand.vendor.com: cookies scoped to .yourdomain.com work on both.
  3. The vendor's webhook. Every serious IDX platform can POST a new lead to a URL. Point it at your own server-side container and that's your event source — no browser required. (postMessage out of the iframe works too, if the vendor cooperates.)

Events worth instrumenting either way:

EventWhere it usually breaks
home_valuation_submitSeller CMA tool is a hosted third-party page
property_inquiryInside the IDX iframe
saved_search_createdRegistration wall on vendor domain
showing_requestCalendar embed — another iframe
call_startNo dynamic number insertion
lead_ad_submitMeta form never touches your site
A timeline from ad click to hidden form field to CRM record, a stage-change webhook at appointment set, and an offline import crediting the closing to the same gclid.
Cookies expire in seven days and closings land months later, so the identity has to live on the lead record, not in the browser.
  1. Ad click, day 0 — gclid stored in a first-party cookie
  2. Hidden form field — Click ID written onto the lead record
  3. Appointment, week 2 — CRM webhook fires the stage change
  4. Closing, month 9 — Offline import keyed to the same gclid
  5. Google Ads learns — The campaign is credited with revenue

How do you get the ad click ID into the CRM?

Capture it on the landing page and carry it into the lead record at first contact. That step makes everything downstream possible.

  1. Grab the identifiers on arrival: gclid, gbraid, wbraid, fbclid, msclkid, plus utm_*.
  2. Store them in a first-party cookie so they survive a visitor bouncing around 30 listing pages before filling anything out.
  3. Write them into hidden fields on every form: valuation tool, contact, showing request, IDX registration.
  4. Map those hidden fields to CRM custom fields. The agent/team CRM category — Follow Up Boss, kvCORE, Sierra Interactive and their peers: lead ingestion, stages, outbound webhooks. That stage-change webhook is the whole ballgame.
  5. Capture email and phone for hashed matching. Click IDs get lost; a hashed email is your second lifeline, and it's what Google's Enhanced Conversions for Leads and Meta's Conversions API match on.
// Persist ad click IDs on landing
var p = new URLSearchParams(location.search), s = {};
['gclid','gbraid','wbraid','fbclid','msclkid','utm_source','utm_campaign']
  .forEach(function (k) { if (p.get(k)) s[k] = p.get(k); });
if (Object.keys(s).length) document.cookie = 'tc_attr=' +
  encodeURIComponent(JSON.stringify(s)) +
  ';path=/;max-age=7776000;domain=.yourdomain.com;SameSite=Lax';

Calls need their own handling: dynamic number insertion, or at minimum one tracked number per channel. Meta Lead Ads never touch your site, so there's no click ID — what you get is a lead_id. Store it and use it as the identity when you report stages back.

Which stages should you push back as conversions, and at what value?

Push back the whole ladder, but optimize on the earliest stage that reliably predicts a closing — usually appointment set. It lands inside the platforms' learning windows, and it's harder to fake than a raw form fill.

StageTimingRole
Inquiry / form fillDay 0Volume signal. Low value.
Appointment setDay 0–14Primary optimization target.
Showing or listing consultWeek 1–8Lead quality by campaign.
Listing agreement / offer acceptedMonth 1–9Reporting, audience building.
ClosedMonth 3–18Your actual P&L.

Values come from expected-value math: expected gross commission × the historical rate from that stage to closed. Illustration only — if a buyer-side closing brings $9,000 GCI and 6% of inquiries close, an inquiry is worth about $540; an appointment set closing at 20% is worth $1,800. Pull both ratios from your own CRM, not a blog.

Two columns contrasting a single Lead conversion action, which lets Smart Bidding chase cheap buyer leads, against separate campaigns, conversion actions and values per intent.
Put both into one conversion action and Smart Bidding chases whichever is cheaper. That's always buyers.
One 'Lead' actionSeparate by intent
Buyer and seller leads counted alikeSeparate campaigns and conversions
Smart Bidding chases the cheaper oneSeller value reflects listing inventory
Seller campaign quietly starvesBuyer volume judged on its own
Browsers and renters fill the pipelineValues from your CRM's close rates
The CPL chart looks great

Do buyer leads and seller leads need to be optimized separately?

Yes, and mixing them is the most expensive mistake in real estate accounts. A listing is inventory — shorter cycle, and it generates sign calls and buyer leads for free. Seller keywords cost multiples of buyer keywords for that reason. Buyer leads are cheap and plentiful, and a large share are browsing or renting. Put both into one conversion action called "Lead" and Smart Bidding chases whichever is cheaper. That's always buyers, and your seller campaign starves while the CPL chart looks great. Separate campaigns, separate conversion actions, separate values.

Should tracking live at the agent level or the brokerage level?

One dataset at the brokerage, with agent as a parameter — not thirty pixels fighting over one website. Single GA4 property, single Meta dataset, single server container, and pass agent_id as an event parameter and a CRM field. Separate ad accounts only where budgets genuinely are separate. I've opened sites carrying eight Meta pixels because eight agents each pasted their own. Nobody's numbers were usable.

IDX webhooks, site forms, Meta lead ads, call callbacks and CRM stage changes converging on one first-party server container that forwards to Google Ads and Meta.
One dataset at the brokerage with agent_id as a parameter, not thirty pixels fighting over one website.
LayerWhat sits there
InputsIDX vendor lead webhook, Site forms, hidden fields, Meta lead ads lead_id, Call tracking callbacks, CRM stage-change webhook
data.yourdomain.comResolves every source to one person, carries agent_id, and outlives the 7-day cap
DestinationsGoogle Ads offline import, Enhanced Conversions for Leads, Meta Conversions API, GA4

Why does first-party server-side tracking fix this?

Plain-language version, because most agents have never heard the term: normally tracking code runs in the visitor's browser and reports straight to Google and Meta. Ad blockers block it, Safari and iOS restrict it, and the browser forgets within a week or two. Server-side tracking moves that job to a server on your own subdomain — something like data.yourdomain.com. The browser talks to you; your server talks to the ad platforms. For a nine-month sales cycle, that changes three things:

  • The identity lasts. A cookie set by your server over HTTP from your own subdomain isn't held to the seven-day cap Safari applies to JavaScript-written cookies. Not permanent, but the gap between a week and months is the gap between knowing and guessing.
  • Your CRM has somewhere to send stage changes. "Appointment set" or "closed" hits your server, which forwards it to Google Ads (offline conversion import or Enhanced Conversions for Leads) and Meta's Conversions API with hashed email and phone. Better identifiers mean higher match rates, so platforms optimize on real buyers and sellers instead of noise — that's what pulls cost per appointment down.
  • The blind spots get stitched together. IDX webhooks, Meta lead_id, calls and site forms land on one server and resolve to one person.

Without it you're steering a six-figure-deal business on a signal that expires in seven days.

Frequently asked questions

How do I know which Google Ads campaign produced a closing months later?

Store the gclid on the CRM record at first contact, then upload the closing back to Google Ads as an offline conversion keyed to that same gclid. If the click ID is gone, Enhanced Conversions for Leads matches on hashed email or phone instead.

Can I track leads that come from Zillow or other portals?

Not in a way that helps you optimize — the portal owns the click and doesn't pass a click ID. Tag them with a fixed CRM source and judge the channel on cost per closing each quarter.

Why don't my IDX property inquiries show up in Google Analytics?

Almost always because the search runs in an iframe on the vendor's domain, so nothing inside it fires on your site. Move search to a subdomain of your own domain, use a same-domain widget, or route the vendor's lead webhook into your server-side setup.

What conversion should I optimize Meta lead ads toward?

Appointment set, not form submit. Meta's windows are short, so you need a quality signal within days — sending "appointment set" through the Conversions API on the lead's lead_id teaches the algorithm which forms produce real conversations.

Do small teams need server-side tracking, or is it only for brokerages?

Small teams often need it more — one lost closing is a much bigger share of their year. The setup is the same size either way: one server container, one CRM webhook, one set of conversion actions.

  • real estate
  • lead gen
  • offline conversions
  • crm
SETUP GUIDES

Guides for this topic

How accurate is your conversion tracking?

PLATFORM-REPORTEDconversionsdivided byBACKENDreal orders or leadsequalsaccuracy
TRY IT · YOUR NUMBERS
68%Broken. Something is missing entirely. Worth a free audit.
Reading guides because something is broken?Book 30 minutes with Ariful. We'll find it on the call and send a written fix list within 24 hours.
Get my free audit →
Ariful Islam
WRITTEN BYAriful IslamFounder of Tracking Consulting and a computer engineer (Diploma, BSc, MSc in CSE). 300+ clients and 1,000+ tracking projects since 2019.
About Ariful →
FREE · 30-MIN CALL · WRITTEN AUDIT IN 24H

Want us to check your accuracy for you?

We look at your setup together on the call. Within 24 hours you get a written audit and a prioritized fix list you can hand to any developer. Not a fit? We'll say so.