Attribution · Home Service Businesses · 8 US metros

Lead attribution for home services:
from search click to paid invoice.
Rankings are not proof — an invoice is.

Ihor Odariuk builds closed-loop lead attribution for home service businesses — HVAC, plumbing, appliance repair, cleaning — connecting the organic search visit, the landing page, the lead, the completed job and the invoice amount, so an owner can see which page produced which order instead of arguing about which channel deserves credit.

Closed-loop lead attribution means every lead is traced from its first source through to revenue in the CRM. Most service businesses can see website traffic and can see revenue, but nothing joins the two: analytics counts anonymous sessions, the CRM counts named customers, and no shared field connects them. The fix is four links — first-touch source tracking on the visit, lead capture that carries that source, an offline conversion join to the CRM by phone number, and an audit of paid lead spend once the denominator finally exists.

Search impressions 6,119 → 260,233 / moSearch visits 388 → 1,465 / mo11,724 jobs re-attributed to their real source97,800 call records joined to CRM jobsTwo channels found to be mislabelled
The Problem

How do you prove SEO ROI to an owner?

Not with a monthly SEO report. That report shows impressions, positions and clicks; the owner looks at the bank account. Nothing in the report explains the bank account, so within a few months it becomes decoration — and marketing is the first budget cut when the year gets tight. Proving return on investment means showing jobs and invoice amounts per channel, in the CRM the owner already trusts.

This is not a reporting-style problem. It is a data problem: the analytics platform stores anonymous sessions, the CRM stores named customers, and there is no shared key between them. The business genuinely cannot answer "which page produced this order" because nothing in the stack ever recorded it.

What the starting position looked like

The client in this case study is a residential appliance repair company operating across eight US metros, running roughly 1,600 jobs a month, buying leads through Google Local Services. On arrival:

  1. Analytics had recorded zero conversions in its entire history. The key events were setup-wizard templates that matched nothing on the site. Calls and form submissions had never been counted once.
  2. The ad account was optimising toward a page view. A booking conversion fired on every page load in one metro account, producing 596 phantom conversions. The algorithm was being trained to buy browsers.
  3. 45% of jobs had no source at all. Dispatchers create jobs by hand and rarely fill the lead-source field, so nearly half the work in the CRM was unattributable to anything.
  4. The website was credited with 1.4% of monthly jobs — a number nobody trusted, and which nothing in the stack could confirm or refute.
The Method

The four links, in the order they have to be built

Each link is useless without the one before it. This is why starting with content or ads, which is what most engagements do, produces work nobody can defend later.

01
Mark the visit
First-touch source, landing page and timestamp are written to a 90-day cookie on the first visit only. Internal navigation does not overwrite it, so the credit stays with whatever actually introduced the customer to the business — including AI assistants, which arrive as a referrer and would otherwise be filed as direct traffic.
02
Capture the lead with its origin attached
Every form submission is written server-side with the visitor's first-touch data and a normalized phone key. Phone clicks are logged the same way. This file is the only place in the stack where an anonymous visit and a real contact detail exist in the same row.
03
Join it to the money
The phone key matches the customer record in the CRM, which carries the job, its status and its invoice amount. Because the join runs on the phone number, it works even when a dispatcher created the job by hand and skipped every tracking field — which is the normal case, not the exception.
04
Audit the paid channel against the same data
With the denominator finally in place, paid leads can be measured rather than assumed. Call recordings are transcribed and classified, then joined to the CRM to separate two very different losses: leads that were never real, and real customers lost during the conversation. Only the second kind is recoverable, and it is the larger of the two.

SEO, GEO and AEO: which one actually produced the job?

Search engine optimization (SEO) earns the ranking. Generative engine optimization (GEO) and answer engine optimization (AEO) earn the mention inside an AI answer — ChatGPT, Google AI Overviews and AI Mode, Perplexity, Gemini, Copilot. Most businesses now buy all three and can measure none of them separately, because AI assistants send a visitor with no search query and, in analytics, land in the same bucket as someone who typed the domain from memory.

The first-touch cookie separates them. When a visit arrives with chatgpt.com or perplexity.ai as the referrer, that value is stored and travels with the lead into the CRM, so an AI-assistant referral that becomes a $400 job is visible as exactly that. Alongside it, a server-side log records every AI crawler hit by URL, which distinguishes the two signals people constantly conflate: a bot fetching a page live to compose an answer is not the same as the page being cited, and it is not the same as a human arriving from that answer.

On this client the AI-assistant channel is small but real and growing — and it is now the only channel in the business measured the same way as the rest: source, landing page, job, invoice amount. Businesses investing in AI visibility without that plumbing are buying a channel whose only evidence is a screenshot of a chatbot answering nicely.

What the chain cannot do, said out loud

It cannot tell you which keyword an organic visitor typed. Google removed that in 2011 and no tool has it. What the chain gives per lead is the source and the landing page; which queries feed that page comes from Search Console in aggregate. Paid clicks are the exception — there the click id resolves to the real keyword through the ad platform API.

It also does not tie one specific phone call to one specific visitor unless you pay for dynamic number insertion. A number per channel and logged tel: clicks get you most of the way there for nothing, which is usually the right trade.

Results

Search visibility, month by month

MonthSearch visitsImpressionsCTR
Sept 2025 — baseline3886,1196.34%
Dec 202538115,6012.44%
Mar 202643131,7471.36%
May 202656970,2240.81%
Jun 202666492,8110.72%
Jul 20261,098140,6410.78%
Aug 20261,465260,2330.56%

Source: Google Search Console API. CTR falls as the site starts appearing for thousands of new queries where it ranks below the top positions — the number that matters, actual visits, tripled. This is the one metric no CRM setting, booking widget or ad budget can explain away, which is why it is the one shown here. Client anonymized.

What the measurement disproved

The first thing attribution did was kill my own numbers

Before the chain existed, this engagement looked far more impressive. Jobs credited to the website had gone from 23 to 127 a month. Then the attribution was built, and it took that number apart: the growth was two booking channels switching on, not the website earning work. One was paid — bookings billed through Local Services Ads at full lead price, indistinguishable from organic in the CRM. The other came from a booking form embedded in the company's Google Business Profiles, not from the site at all. Calls to the site's own tracked number had not grown in twelve months.

The same happened to the lead-dispute automation. By the ad platform's lead status it looked like a success. Cross-checking the tool's own list of filed disputes against the API told a different story: 3,614 disputes filed, 14 credited — 0.39%, while leads the tool never touched were credited 8.46% of the time. The platform issues credits automatically by its own models; disputing does not move them. The invoices confirmed it: in the month with 1,284 filed disputes, total credit memos came to $77.89.

Both findings cost me results I would rather have kept. They are on this page because that is the entire point of the work: a measurement system that can only confirm you is not a measurement system. What survived — the search data, the lead-to-invoice chain, the recovered job sources — survived a deliberate attempt to break it.

Every figure here was pulled programmatically from the Search Console API, the CRM job and call-log APIs, and the ad platform's reporting and billing APIs. All of it can be reproduced on request. None of it comes from a dashboard screenshot.

FAQ

Common questions about lead attribution.

No, and neither can anyone else. Google stopped passing the search term to websites in 2011. What is knowable per lead is the source (organic, paid, AI assistant, direct, referral) and the exact landing page. Which queries bring people to that page comes from Search Console in aggregate. For paid clicks it is different — the gclid resolves to the actual keyword through the Ads API. Anyone promising per-lead organic keywords is selling something that does not exist.

Three ways, cheapest first. One number per channel — a site line, profile lines, paid lines — proves the channel without any software. A click on a tel: link is logged with its source and page, which proves intent from a specific page. Dynamic number insertion is the only method that ties one specific call to one specific visitor, and it costs money per number. Most businesses do not need the third one to make better decisions.

That is the normal case, not the exception — in this project 45% of jobs had an empty source field. The join runs on the phone number instead: the lead log stores a normalized 10-digit key at the moment of submission, and the CRM is matched on it afterwards. The dispatcher's workflow does not change at all, and the attribution still holds retroactively.

No. The work sits on top of what already exists — WordPress and Housecall Pro in this case. The pieces are a first-touch cookie, a hidden field in existing forms, a server-side log file, and read-only API access to the CRM and ad accounts. Nothing about how the team books jobs changes.

The tracking itself is live within a week and starts collecting immediately, though it is not retroactive — it measures from the day it goes in. Historical attribution can often be rebuilt separately from call logs, which is how the 2026 job history in this project was recovered. Content and ranking effects run on their own timeline, typically one to three months before revenue moves.

Both, and that is the point. SEO without attribution produces rankings nobody can connect to revenue, which is why marketing budgets get cut first in a downturn. Attribution without SEO measures a channel that is not growing. The sequence that works is: make the channel measurable, then grow it, then prove what the growth was worth in the CRM.

By referrer, captured on the first visit and carried into the lead record. A visitor arriving from chatgpt.com or perplexity.ai has no search query attached, so default analytics files them as direct traffic and the channel looks like it does not exist. Storing the referrer in a first-touch cookie makes AI-assistant visits countable, and joining that cookie to the CRM makes them countable in dollars. Separately, a server-side log records which URLs AI crawlers fetch, which is a useful early signal but is not the same thing as being cited or as a human actually arriving.

SEO earns a ranking in classic search results. GEO, generative engine optimization, earns a mention inside AI-generated answers such as ChatGPT, Perplexity, Gemini and Google AI Mode. AEO, answer engine optimization, is the overlapping practice of structuring content so a machine can lift a direct answer from it — self-contained paragraphs, real tables, genuine FAQs, clean schema. For a local service business the same underlying assets serve all three, so the practical question is not which to buy, but whether you can tell them apart afterwards. Without attribution you cannot, and every claim about AI visibility stays a screenshot rather than a number.

Work With Me

Find out what your website is actually worth.

If your CRM cannot tell you which orders came from search, that is the first thing to fix — before any content, before any ad budget. I build the chain, then grow the channel it measures.