Prospecting builds an outbound sales list from Google's business listings: search for a kind of business in a place, narrow the results down with automatic checks, then import the ones worth talking to straight into the CRM as companies and contacts.
It answers the question a keyword search can't: what have I actually covered? A single Google search returns at most 60 businesses and never tells you which ones it left out, so a "coverage sweep" divides an area into sections, searches each one, and divides again wherever a section comes back full.
The problem
Building a list of local businesses by hand is the slowest part of outbound sales, and searching by keyword makes it impossible to know when you are finished. "Electrician Houston" returns 60 results out of thousands, silently. Search "electrician Katy" next and you get an overlapping set with no way to tell the new names from the ones you have already called — and any status, note or email address you recorded gets lost the moment you re-run the search.
The fix
A feature module under app/Features/Prospecting/ with three parts:
- Find — whole-area coverage sweeps against the Google Places API, plus a by-name lookup for adding one business you already know of.
- Narrow it down — free filters over what Google already returned, plus automatic audits of each business's own website.
- Import — the keepers become CRM companies and contacts, tagged so an automation can pick them up.
Every business is stored once, keyed on its Google place ID. Finding the same business again refreshes what Google says about it and touches nothing you have done to it — the status, the email you tracked down, the notes, the CRM links all survive. That is what makes overlapping searches safe, and overlapping searches are unavoidable.
Setup
Prospecting needs a Google Places API key on a Google Cloud project with billing enabled and the Places API (New) switched on. Paste it into the prompt at the top of the Prospecting page, or under SEO Scanner → Settings — it is the same key (seo_scanner.places_api_key), one Google project and one bill. Everything except searching works without a key: you can still filter, edit, keep and reject whatever is already on the list.
Searching costs money per request. See What it costs.
How this page works, above the list, opens the working order in one window: find businesses → size them up (audits, playbook, owner) → keep or reject → build the pitch page → email them → follow up, each step shown with the row buttons it uses. It exists because the row's buttons change with a business's state — emailing and the pitch page appear only once a business is imported — so the order is not something the list can show by itself. A business that is not imported yet (and not rejected) also says so under its buttons: Import it to email them or send a pitch page.
Finding businesses
Adding one business
Add a business is for a business you already know of — a referral, a name on a van, a firm somebody mentioned. Type its name, optionally the town, and the trade it belongs to. Google's five closest matches come back with their address, category and review count; press Add on the right one and only that business goes on the list. A match already on the list says so instead, and adding it again changes nothing you have done to it. Each lookup is one billed search.
It replaced a keyword search ("electrician" + "Dallas TX", up to 60 results), because that could only ever return Google's top matches for an area — the businesses a sweep of the same area finds first. In an area already swept it added nothing new, and away from one it added up to sixty businesses to get any one of them. Bringing in an area is a sweep's job.
Coverage sweeps
A sweep searches a whole area to completion. Give it a category, a place name ("Texas", "Tarrant County", "Dallas"), and a search budget, and it works through the area in the background — you can leave the page.
How it works, and why it is built this way:
- The area is turned into a rectangle from Google's own bounding box for that place, and searched as one section.
- A section that comes back under the limit is finished. Everything Google lists in that box is now on your list.
- A section that comes back full has truncated, and there is no way to know what it dropped — so it is quartered and each quarter is searched in turn. "Full" means sixty results, Google still offering another page, or the search reaching Google's third and last page at all: inside a section Google's pages come back short, so a section can use every page, hold 38, and still have more in it.
- That repeats until every section comes back under the limit.
The effect is that the sweep tunes itself to where the businesses are. Empty countryside resolves in a single search; a dense metro keeps subdividing until it stops overflowing. Money lands where the businesses are rather than being spread evenly over a map that is mostly farmland.
Rectangles rather than towns or ZIP codes is a deliberate choice. Town names overlap ambiguously — a business in Irving may or may not surface under a Dallas search, so coverage can never be proven. ZIP codes are mostly empty countryside: Texas has around 1,900 of them and you would pay for all 1,900 to find that most hold one electrician. Rectangles tile an area exactly, so every business is in precisely one section and "have we looked here" has an answer.
You never have to think in rectangles, though. The panel reports percent swept, businesses found, sections still to search, and searches spent — and names the places involved using the towns Google already returned with the results.
When a sweep stops
- Finished — every section came back under the limit. The list is complete for that area, to the detail limit below.
- Budget spent — the sweep reached its search ceiling and paused. Nothing is lost and nothing is re-searched: type a higher budget, press Carry on, and it continues from exactly where it stopped.
- Too many businesses to list them all in: Houston, Dallas — a warning naming sections that were still full at the finest detail the sweep will go to. A city block can genuinely hold sixty businesses, so subdivision has a floor. Those areas are reported by name rather than quietly dropped, so you know where the list under-reports.
- Monthly cap reached — the whole install has spent its monthly search allowance (10,000 by default, across every sweep). Raising this sweep's own budget would not help, so the card says so instead of offering the box; a Super can raise the cap beside the API keys, or it clears itself when the month rolls over.
- Areas that could not be searched — a section whose search failed (an expired key, a network blip) is recorded with Google's own error and a Try again button. Its ground is not counted as swept: an area we could not search is not an area we searched and found nothing in. A section Google refuses on quota is not one of these: it goes back in the queue and is retried a minute later, because quota is a refusal to answer now rather than a finding about that ground.
Large areas
Sweeping a whole country is the same feature with two more zeros on it, and two things guard against getting there by accident:
- A pre-flight confirm. When the area Google resolves is bigger than a large state's bounding box (over 1,000,000 km²) or Google classifies it as a country, the sweep asks first — naming the worst-case number of searches, roughly what it would cost beyond the free allowance, and how long it would take on this server. Nothing has started at that point, and lowering the search budget first is the usual answer. Note that the bounding box of a large state exceeds that threshold (Texas's is about 1.78 million km²), so a state-wide sweep will ask too.
- A monthly search cap,
prospecting.monthly_search_cap, default 10,000 — roughly $350 at list price, across every sweep on the install. A per-sweep budget is authorised by whoever typed it; the cap is what stops ten of them, or a sweep resumed nightly, quietly adding up to a bill nobody approved. Reaching it pauses sweeps rather than failing them, so nothing is lost and no section is searched twice.
A large sweep also leaves a long queue of businesses to check. The automatic audits work through all of it, just slowly — see Automatic audits.
Sweeps run on the site's background task runner, so a sweep appears on Tools → Background Tasks with its own progress bar, and can be cancelled or retried from there. On a server that can start a background process the whole sweep runs off-request at four searches a second; on a host that cannot, it falls back to a few sections at a time on ordinary traffic, as before. A running sweep shows when it last searched something, so a working sweep and a stuck one never look the same; if nothing has run for ten minutes it says so and offers Search now, which restarts the background run — that button only appears when it is the remedy. From the terminal, php artisan prospecting:sweep keeps whatever is running moving, and php artisan tasks:run --id=N drives one sweep to completion.
Where sweeps appear. Sweeps still working — running, or paused on budget — sit under the search box as one compact row each, from either tab, and are never hidden however many newer sweeps exist. Finished sweeps fold into a single line (4 finished sweeps · 401 businesses found) with Show history opening the table behind it; a finished sweep whose sections failed is called out on that line so it cannot fold away unnoticed. The Found by filter is the way to reach what any past sweep brought in.
Removing a sweep removes its record of where it looked. The businesses it found stay on the list — they belong to the list, not to the sweep.
Narrowing the list
Search and filters
Above the list is a search box — free text over name and address — and a Filters button. The filters fold away behind it, because finding one business by name is the commonest thing anyone does here and it should not be the tenth control on screen. The button says how many filters are narrowing the list (Filters · 2), so a list cut down by a saved view never looks short for no reason, and Show all beside it widens it back out in one click — dropping the view as well as the filters.
The filters themselves read fields Google already returned, so filtering costs nothing extra:
- Status — New / Rejected / Imported, with counts. There is no separate "qualified" step: keeping a business is importing it.
- State and City — from the business's address, with counts. Picking a state narrows the city list to that state's cities, and drops a picked city the new state doesn't have, so the two can never combine into an empty list. Each shows only once the list spans more than one.
- Website — has one, or none listed. A business with no website at all is a different pitch from one with a bad website.
- Owner — Name on file or No name yet: whether there is somebody to ask for. A name their site or the web search turned up that nobody has chosen does not count, so No name yet is exactly the list still to find an owner for.
- Minimum and maximum reviews — optional tools, not a campaign filter. Whether a business works its reviews is independent of whether it needs a website, and Google counts reviews per listing, so a six-branch firm shows six modest numbers rather than one large one. Size is the Locations count below; the pitch is the audits. The list sorts by review count regardless, so the biggest single-yard operators surface at the top of whatever view you are in.
- Audit, with a verdict and a range — see below. The range is what turns a check into a campaign: service pages from 1 up to 5 is the list of businesses with a services page and a few children, which is a different conversation from the one-pagers "failed" would lump in with them. Zero is a real bound — up to 0 is exactly the one-pagers — and a cleared box is no bound at all.
- Locations from / up to — the branch count, in boxes of its own so it stacks with the audit above. This is the size filter: up to 1 is the ordinary small business, whatever its service count — a one-person operation can offer twenty services — and from 2 is a multi-location firm, independent unless the Franchise flag says otherwise. Service pages say what the site needs, never how big the business is; the two stack.
- Franchises & lead-gen sites — Shown, Hidden, Only flagged, or only one kind of flag. Its own control rather than a choice in the Audit select, because it stacks: the list you actually work is "failing the service-pages check and not a franchise and not a doorway site", and one select cannot say three things. A multi-location independent is never a flag — it is a Locations count, filtered by range like any other audit.
Saved views — one click per campaign. There is more than one list worth working, and they differ by eight controls: the single-location site at $200 a month (Locations up to 1, franchises and lead-gen sites hidden, nobody contacted yet, plus a filter saying the site needs work); the multi-location package at $1,500–4,000 (Locations from 2, the same flags hidden); the businesses with no website at all (Website: None listed). Re-picking eight controls to change campaign was the whole friction, so whatever filters are set, Save as new view… at the foot of the filter panel stores them under a name, and a switcher beside the Filters button puts any of them on screen in one choice. All businesses is always the first entry.
Size is read off Locations, never the service count — a one-person operation can offer twenty services — and review count is in none of the recipes: whether a business works its reviews is independent of whether it needs a website, and a per-listing count dilutes across branches anyway. The review boxes stay as optional tools, and the list's own order (most reviewed first) is what floats the biggest single-yard operators to the top of whichever view you are in.
An active view whose filters you then change says Edited — not saved rather than showing its name over something else, and offers Back to "name" and Update "name". Rename and Delete act on the active view; deleting one leaves the filters on screen exactly where they were. Open on this view makes it what the page opens on — one view, or none, which is Stop opening on a view. Each control appears only when it can do something.
Starting from the standard campaigns. An install with no views yet is offered the three at the foot of the filter panel — Single-location sites, Multi-location package, No website at all — in one click. They are ordinary views once taken: rename them, point them somewhere else, delete the ones you do not want, and the offer does not come back to undo that. What they cannot carry is the trade or the Found by sweep a real campaign is also narrowed to; those name rows on one install's own list, so you add them yourself and press Update.
Views are one set for the install, not per user, and each stores only the filters that differ from the plain view. A saved trade, Google category, state, city or Found by search that no longer matches anything on the list is dropped on the way in, so a sweep you later forgot can't leave the page opening empty with no visible reason. An install that had the single pre-views saved default keeps it: it becomes a view called Default, and goes on opening the page.
Automatic audits
Audits check each business's own website, and run a few at a time in the background. The Audit filter narrows the list to those that failed a given check — a failure is the pitch, so it is the one that stands out in the list. Three other results are deliberately not verdicts and get controls of their own: the Locations count, the flags that mean "skip this one", and the Field software tag that decides what to pitch.
- PageSpeed — Google's Lighthouse performance score out of 100 on mobile, plus the largest-contentful-paint figure. A slow site is the easiest problem to demonstrate on a call. The badge shows it as a letter grade —
C · 72 / 100— and passes at 80 or better, a B. Google's own bands call 90+ good and 50-89 "needs improvement", so anything under 80 is a C or worse and worth raising on the call. The score is carried onto the CRM company record at import, where the conversation actually happens. - Service pages — counts pages dedicated to one service each in the business's sitemap. A business with one "Services" page and nothing under it is a business we can visibly help; one with nine service pages already does what we would sell them. A page counts whether it is filed under
/services/, named for what it does (/ceiling-fan-installation), or sits among several siblings under a hub the site invented (/plumbing/faucets,/electrician/smart-home,/weatherford/ac-repair) — the shape most contractor sites actually have. Blog posts, coupons, testimonials, city pages and the like never count, however many there are, and the same service under several cities counts once. A site Google lists ashttp://orwww.that redirects to its real address is read at that address; one listed with a tracking query or as a city landing page has its sitemap read at the site root; one whose domain now redirects to a different site is reported as moved, with the new host, rather than audited there. A one-page site with no internal links counts as zero — that is the finding, not a failure to read it. - Social proof — whether the business's own site shows anybody vouching for it: a review widget, quotes from customers, star ratings or testimonial wording on the home, contact or about page (pages the pass has already read, so it costs nothing extra), or a reviews or testimonials page in its list of pages. Any of those is a pass — Shows reviews. A site that shows none fails — No reviews or testimonials — and a failure is the pitch, so it is in the Audit select like the others. A home page built by script in the browser carries almost no words in the HTML a server sends, and its reviews may be among the missing, so that is no verdict, never a failure. Businesses checked before this audit existed are caught up by the background backfill, which runs only the audits a business is missing — never a PageSpeed re-run. A block the theme marks as testimonials or reviews in its markup counts only when it holds a quote's worth of words — a menu item, a link, a "leave a review" form or an empty tab a script fills later does not. Because it reads only three pages, a failure reaches a pitch page only once the site audit agrees (see Pitch pages).
- Schema type — whether the site's hidden schema.org markup says what kind of business this is, not just whether there is any. Yoast and most themes publish a bare
Organizationout of the box, so a dental clinic and a restaurant can declare exactly the same thing and Google learns nothing from it. The declared type is judged against Google's own category for the business and the trade it was found under — either one is enough for a pass, so a firm found hunting electricians that Google files as a plumber passes withElectricianorPlumber. A failure, though, is said in Google's terms whenever Google has a category for the business: the trade is only the search that turned it up, so it names the right type only for a business Google gave no category, and "a different line of business" is never claimed against the search term alone. Three things fail, and a failure is the pitch, so it is in the Audit select: no business markup (a bareService— a thing offered — doesn't count as one); only a generic type —Organization,LocalBusiness,Corporation,ProfessionalService— when schema.org has a type for what they are (Organization only — should be Dentist); and a different line of business (Restaurant — Google lists them as dentist). The family is close enough to pass (HomeAndConstructionBusiness — Electrician fits better), and so is a sibling inside its main family (Plumberfor an electrician,MedicalClinicfor a dentist), because dual-trade firms do that honestly — but only the main family: a vet is not a dentist because both are medical organisations. A few types a business honestly declares from another branch of schema.org also pass as close: a vet marked up as anEmergencyService(a 24-hour animal hospital), anAnimalShelteror aPetStore, and a pet store asVeterinaryCare. A vet's own markup is expected asVeterinaryCare— co-typed withLocalBusinessis a fit, and it is exactly what a WebProCMS vet site publishes — and the ways vets are searched for (animal clinic, pet hospital, emergency vet) read as a vet before they read as a clinic or a hospital. A store category schema.org has no type for is never told to be a bareStore. A business whose category schema.org has no type for (a landscaper) is never told to be more specific thanLocalBusiness— only that a bareOrganizationsays nothing about a local business — and one Google files as an organization rather than a local business (an association or union local, a nonprofit, a government office, a school, a manufacturer or supplier, a corporate office, a consultant, a telecom provider) passes with any business type, a bareOrganizationincluded. Google's catch-all categories (service,point_of_interest,establishment) say nothing about the business, so they count as no category at all and the trade names the right type — on one install 1,400 of the electricians on the list were filed asservice. A JSON-LD block that didn't parse, or a type the check can't place (Chiropractor, which schema.org doesn't define), might hold the right answer, so a reading that would fail is no verdict; so is a home page built by script in the browser. A page type nobody listed (MusicEventon a blog post) is not an attempt at a business type and never silences a finding. Read from the home, contact and about pages the pass has already fetched, so it costs nothing extra, and businesses checked before it existed are caught up by the backfill. The site audit judges the same way but over every page it reads, so on a site whose only specific type sits on a deep service page the two can differ; only the site audit's finding reaches a pitch.
How many get checked automatically. All of them. The background checks work through the never-checked backlog oldest-first, however long it is. Each pass stops on a clock (two minutes on a cron, thirty seconds inside a visitor's request), so a big backlog never holds anything up, it just takes longer. Each business is a PageSpeed call plus several page fetches, so a state-wide sweep that brings in several hundred businesses with websites can take several hours to clear. There is deliberately no size limit. A limit doesn't trim the overflow, it switches the checks off for every business, and then every audit filter on the list matches nothing. To get one list checked sooner, Check this view now under the list checks the businesses in the filters you have open (up to 500 at a time, oldest first) as a background task you can watch on Tools → Background Tasks. It checks only businesses already in the view, so clear any audit filter (Audit result, Locations, Franchises & lead-gen sites) first, because a business that has never been checked can't match one.
Locations: a count, not a verdict
Locations is a measurement rather than a check: how many places the business operates from, counted from listings on this list that share its website or its name (once the branch tag is dropped — Milestone Electric - Frisco and Milestone Electric Plano are one name), from branch pages under a locations hub in its sitemap, and from a location finder on its home page, which proves "more than one" even when nothing else can be counted. It shows as Locations: 4 and never turns red or amber, because whether a second branch is good news depends entirely on the campaign: for a $200-a-month site it is the wrong customer, for a multi-location package it is the whole list. So it gets from / up to boxes of its own in the filter panel, beside the review boxes, rather than a place in the Audit select — which means it stacks: locations up to 1 is the single-location campaign, locations from 2 is the multi-location one, and either adds a service-pages or PageSpeed filter on top to say what the site needs, without giving up the other. Size is never read off the service count — a one-person operation can offer twenty services. A count that cannot be read is a no-verdict, retried weekly, never "one".
Field software: a tag that decides the pitch
A third kind of result, alongside verdicts and the Locations count: Field software reads which job-management platform the business already runs — ServiceTitan, Housecall Pro or Jobber — off their own site.
It is neither the pitch nor a reason to skip, so it is never red or amber and never appears in the Audit select. It changes what you sell:
| What it finds | What that means on the call |
|---|---|
| ServiceTitan | A bigger shop with real software and, usually, an agency-built site. They are not replacing their dispatch system, and offering to replace their booking marks you as someone who did not look. Sell the website and the connector. |
| Housecall Pro / Jobber | A smaller operation. Their booking is a bolt-on, and ours is a genuine offer. |
| Runs nothing | The ordinary prospect, and the one our own booking was built for. |
"Runs nothing" is a real answer, not a blank — it is filterable, because "failing service pages AND running nothing" is a booking campaign while "failing service pages AND on ServiceTitan" is a connector campaign, and the two get different emails. A site that could not be read is different again: it has no answer either way and appears in neither list, retried weekly like any other no-verdict.
Detection leads on each vendor's own domain, which survives them moving the widget to a new subdomain, plus specific widget markers where they are documented — ServiceTitan's Scheduling Pro embeds embed.scheduleengine.net/schedule-engine-v3.js and renders an se-widget-button, so a site carrying the widget is caught even when it never names ServiceTitan. More than one vendor is a real answer: a business mid-migration carries both, which is worth knowing before a call. It shares the crawl the other audits already made, so it adds no requests.
The prospect-audit skill reports the same thing on its own line (Field software:), kept deliberately separate from the Platform: line — that one says who built the site, this says how the business runs, and a shop can be on WordPress and ServiceTitan at once.
Flags: franchises and lead-gen sites
Two more checks run in the same pass, and they say the opposite of the others. A failing PageSpeed score is the pitch; a failing Franchise or Lead-gen site check is a reason to skip. So they are shown in red rather than amber, kept out of the Audit select (whose verdict labels read failed — worth pitching), and filtered with the Franchises & lead-gen sites control instead, which hides them, shows only them, or shows only one kind. Every saved view has them hidden; it is the common way to run.
Neither one is a lookup; both are scored from evidence, because every sign has an innocent explanation somewhere — a plumber with a DBA answers two names on one number, a forty-year-old Dallas Electric Co owns dallaselectric.com, a family firm tags its second shop Smith Electric - Frisco exactly the way a franchise does. Each sign carries points, one strong sign is a flag, weak ones never add up to one on their own, and every reason is kept on the result and shown on hover so a person can see why and disagree. Nothing is ever rejected automatically. A flag hides a row from a filtered view; it never changes a status.
- Franchise — a franchise or a national brand: the website belongs to head office, so the local listing has nobody who can say yes at any price. It reads only the signs a franchise carries: a franchising link (Own a franchise, Franchise opportunities), the "independently owned and operated" disclaimer a franchise agreement makes the local page print (locally owned and operated is deliberately not a sign — independents say that with pride), a national group credited in the footer (Neighborly, Authority Brands, Wrench Group and the like), and branches in more than one state, read off branch-page slugs and off the list, which a Texas independent never has. A branch-shaped name (Mr. Electric of Plano) and a branch page listed as the website are recorded as context but never flag on their own. A multi-city independent is therefore not a franchise — that is what the Locations count is for.
- Lead-gen site — a lead-generation or directory listing rather than a business of its own: a keyword name on a keyword domain, minted a dozen times to sell the calls on. The list itself carries the strongest evidence: the same website or the same phone number under two or more different business names (under the same name it is one business with branches, and Locations counts it). On the site: pages whose title, site name and headings never name the business, and a domain made of nothing but the trade, a place and filler (planoelectricians.com, dallas-electrician-pros.com, usmapelectricians.org). Each of those is only medium on its own — the two together are a flag, either alone is not — and a site that does name the business anywhere switches both off.
What counts as "the trade" and "a place" is read off the list rather than fixed in code: every term a sweep was run for, every Google category it returned, and every city on the list feed the word set, plurals included. An electrician campaign teaches the checks electrician; a plumbing or dental sweep later teaches them plumber or dentist with no code between the two.
The checks share the crawl the other audits already made, so they add no requests, and businesses checked before they existed are caught up by the same background backfill. A site that cannot be read, with nothing against the business on the list, is a no verdict for both — retried weekly like any other — rather than a pass that would never be revisited.
A business whose listed "website" is a Facebook page gets the strongest version of the service-pages verdict — No website of their own — and nothing is fetched from facebook.com to find that out. Whether a business links its profiles from its own site is not audited: that is a way to reach them, not a reason to pitch them, so the profiles are discovered and shown in the Contact column and left unscored.
An audit that cannot reach a verdict — no website to test, a site that will not respond — is recorded as "no verdict", never as a failure. A failure is something you say out loud on a sales call, and it must never be an artefact of our own tooling breaking. A no-verdict is checked again a week later, and every week after that for as long as it stays one — a firewall that blocks the check this week lifts next month, a dead DNS record gets fixed — while a verdict that exists is never spent again. Correcting a business's website in the contact-details drawer (the "site has moved" case) forgets everything read from the old site and has the new one checked on the next pass.
Contact discovery
Google never gives out an email address, so the one on the business's own site is the only one there is. The same crawl the audits read — sitemap, home page, contact page, each fetched once — also lifts three facts onto the prospect:
-
Email. Every address found, best first: a named person on their own domain over
info@, over a free mailbox, over the web designer credited in the footer. The first is written into the Email field only while that field is empty; an address somebody typed is never overwritten. The others are offered as one-click fills in the contact-details drawer. Addresses hidden behind Cloudflare's obfuscation, written out as info [at] domain [dot] com, or HTML-encoded are all read. -
Contact page — the form to fill in when there is no address at all; see Recording outreach.
-
Social profiles — one canonical link per network, shown in the Contact column. Share buttons are not profiles and are ignored.
-
Owner. The people the site itself names as running the business — read from the About page (About, About us, Meet the owner, Our team, Our story… — one more page fetched per business), the home page and the contact page. Two readings, both narrow on purpose: the page's structured data (a
founder, or a person whose job title is owner, founder, president, CEO or proprietor) and the wording right beside a name — Owner: Martin Elizondo, Martin Elizondo, Owner, a team card with the name over its role, founded by…, owned and operated by Martin and Rosa Elizondo (both people). The phrases that only look like an owner are not people: Family Owned & Operated, Owner Login, a testimonial signed Homeowner, or one signed Owner of Doe Bakery (somebody else's business). Each person is stored with the page and the words they were read from. Nobody is ever written into the contact name by the crawl — a name read off a page is the one guess that sounds like a fact on the phone, so it is offered to you, and lands on the prospect only when you choose it. The About page's addresses join the email list too. When the business has no email yet, the owner's own address there — one built from the name of an owner the site names — is filled in ahead of the shared desk on the contact page; any other person's address that only the About page shows (a team page's office manager or technician) is listed for you to choose, never filled in for you.
Found addresses are also sorted by who probably reads them: a person on the business's own domain, a shared desk (info@, office@, estimates@, dispatch@…), a free mailbox (at a small shop, often the owner's own inbox), or somebody else's domain. From an owner's name the likely addresses at the business's own domain can be built in the eight usual shapes — martin@, martin.elizondo@, martinelizondo@, melizondo@, m.elizondo@, martine@, martin_elizondo@, elizondo@ — with the company's own shape first when its published addresses show one (sarah.jones@ means first.last). Never at a site builder's domain or a free mailbox, and never written into the Email field: a guess is labelled as one.
Facts are only ever written when found: a crawl that came back empty is not evidence an address went away — or that an owner did. Businesses discovered before owner-reading existed go back through the backfill once to have their About page read. A prospect already in the CRM has the new facts carried across — the email onto its contact (or a contact created, if the company-only import had nobody to make), the profiles onto the company.
Prospects audited before discovery existed are backfilled by the same background task, twenty-five at a time: discovery plus whichever audits they are missing, never a PageSpeed re-run.
Finding the owner on the web
A contact form usually reaches an employee. Find owner searches the web for the person who decides — on demand only, one business at a time, because every search is billed by the AI provider. It runs as a background task (prospect-owner-search, one per business: a second click joins the search already running and pays nothing).
What it searches. At most five searches, in order: the business's exact name with its city and "owner"; site:linkedin.com/in with the name; site:facebook.com with the name and "owner"; "@theirdomain.com" -site:theirdomain.com for their addresses written on other sites; and the owner's likely addresses by exact match. The names the business's own site already gives (see Owner above) are handed over, so the likely addresses can be searched from the start.
What is kept — every claim is checked, none is taken on the AI's word:
- A link is kept only if it is one of the provider's own search results. A URL the AI wrote itself is dropped and never fetched.
- A person is kept only when their name is seen in a result's title or on a result page we fetched ourselves (up to eight, one at a time, with the same guarded, size-capped fetch the audits use). They are confirmed only when that same title or page also names the business — its name, its domain or its phone number beside them. A person of the same name somewhere else is kept as possible at most, never confirmed.
- A role ("Owner") is kept only when it is written beside the name.
- LinkedIn and Facebook pages are never fetched — login walls, and their terms. A profile is a search result whose own title names the person (a personal
linkedin.com/in/…profile, never a company page) and names the business too — or is the very profile the AI tied to a person already confirmed elsewhere. A Facebook Page and a personal profile share every modern address shape, so the business's Page is never taken for the owner's profile: not an old/pages/or/pg/address, not the Page the business's site links to or its listed website, and not a result whose title names the owner only as part of the business's name ("Martin Elizondo Electric LLC | Facebook" is the Page; a business called nothing but the owner's name is read by the name alone). A Page has Message, not Add friend. - An address is "found" only when it is written on a page we fetched and is built from the person's name — at the business's own domain, or a free mailbox on a page that names them beside the business. A namesake's work address at another company is not.
- Likely addresses are built for a confirmed person or a name the site gives (never a possible match), only at the business's own domain, and only when that domain has an MX record — guesses at a domain that receives no mail would all bounce. They are labelled as guesses and never written into the Email field.
- PDFs are skipped (nothing here reads one). Gemini cites its results through its own redirect links; each is followed exactly one hop to the page it names, never further, and an answer that cites the redirect link, as the search gave it, is read as citing that page.
- The pages the answer itself cites are always among the results kept. Up to 60 results are kept per search call; five searches can return more than that, so the cited ones are taken first.
Nothing is promoted automatically. The search writes its own record, apart from the crawl's: the people (each with the links it came from and a confirmed/possible flag), the addresses (found with the page, or guess with its shape), the pages read, how many searches ran, and when. A search that found nobody is stored as exactly that, so it never looks like a search that was never run. A name, address or profile reaches the prospect only when someone chooses it. Editing a business's website deletes its search, with everything else read about the old site; a website edited while the search runs stops it, and nothing is stored.
It needs the install's own AI key — Claude, OpenAI or Google. Managed AI is refused: the managed proxy meters tokens, and each search is billed per search on top, so a search through it would be paid from the house account unbilled (the proxy refuses provider-run tools as well). DeepSeek has no web search and is refused too, rather than answering from memory. Each refusal says why, in words. When the AI provider itself turns a search down — a key that no longer works, an account out of credit or quota, too busy — the failure says so in the provider's words — its own error type and message, e.g. The AI provider turned the search down: invalid_request_error: Your credit balance is too low to access the Anthropic API., never the AI library's internal name for the call or the raw response body — rather than asking for another try, which would hit the same wall; an answer that came back unusable (cut off, not the JSON asked for) is the one that says to search again.
What a search costs is the provider's own price: tokens for the answer, plus a per-search fee for up to five searches (Gemini's search does not take a cap, so the five is an instruction there rather than a limit).
Choosing the owner
Find owner (the ID-card button on a row's top line) opens the contact-details drawer on its Owner section and starts a web search — unless one is already on file for the website the business has now, one is still running (the drawer shows its progress, and closing the drawer doesn't stop it), or the last one failed while nobody was watching (the drawer says why). None of those pays for a search again; Search again, inside the drawer, is the one way to. The pencil opens the same drawer without searching. Find owner is on every row — a business with no website included, since its name, town and phone are enough to search on, and a business already in the CRM (below). On an install whose AI can't search (none set up, managed AI, DeepSeek) the drawer says so in place of the button, and still shows what the business's own site says.
The Owner section lists the people from both sources as one list — the site's and the search's, the same person from both folded into one with every source. Each shows their title when one was written beside their name, a Confirmed badge (the name and the business were seen together in something we checked ourselves) or Possible (a namesake somewhere else could look the same), and links to where they were seen, with the words they were read from on hover. A search of a website the business no longer has is never shown. A search that found nobody says Searched the web … — nobody found, never the same thing as no search at all.
Nothing found is ever written in on its own. Use beside a person fills Who to write to and Title (a blank title when they have none, so the last pick's title never describes the next person); Use beside a profile they were found with fills the LinkedIn or Facebook field. Each fills the form only — nothing is stored until Save, and a search finishing while the drawer is open leaves whatever is being typed alone. A person with no profile found gets Search LinkedIn, Search Facebook and Search Google links built from their name (from the business's, when nobody is named), for looking them up yourself.
The addresses under the Email field are every one their site and the search turned up, once each, sorted by who probably reads them: the owner's (built from the name of somebody offered, or of the contact on file — a free mailbox included), a person there, a shared inbox, a free mailbox, another domain; and within each, on their site before found on the web (with a link to the page) before Likely — not confirmed — an address built from a name and never seen anywhere. Likely addresses come only from the search, which built them only at a domain that receives mail; none is ever built in the drawer.
The profile fields take only a person's own profile. A LinkedIn company page, a Facebook Page by its /pages/ or /pg/ address, or a profile from the other network is refused with a message rather than saved as the person's: the friend and connection requests below go to it, and both are carried onto the CRM contact. Anything else is stored in its plain form (www.linkedin.com/in/name/?trk=… is saved as https://linkedin.com/in/name). The Facebook address the business's site links to, or lists as its website, is accepted: a small shop's site often links the owner's personal profile, which only somebody who opened it can tell, and saving it here is how they say so — it then stops being offered as Message the Page (where a message to a person lands in Message Requests) and becomes Add friend. The web search, which cannot tell, never takes it for the owner's.
Once the business is in the CRM — kept, with a contact — the same drawer edits who runs it and nothing else: Who to write to, Title, LinkedIn and Facebook, with the Owner section and its search. The owner is found after a business is kept as often as before, because keeping it is what outreach waits on. The email, website, notes and trade were copied onto the CRM records at import and are changed there (the drawer links to the contact); its addresses found are listed to copy across, without Use, and the save leaves the other fields alone whatever is sent. Save carries the person to the CRM contact through the import (see Importing into the CRM) and says what became of the name: the CRM contact is Martin Elizondo, or — when somebody typed a name there — that the contact keeps it, and to change it there if the owner is who to write to.
In the list, the Contact column shows the person on file whatever the channel — Martin Elizondo · Owner — and, under the address, the ways to reach them on Facebook and LinkedIn (below), apart from the business's other pages listed under those. While nobody is on file, it shows who the site or a search turned up — Owner found: Martin Elizondo (somebody confirmed first), or Possible owner: … — with Choose, which opens the drawer and never starts a search. Only a web address is ever rendered as a link.
Finding owners for a whole page
Find owners on this page, above the list, runs the web search for the businesses on the page on screen — 25 at most, a set you can see in full before paying for it, never the whole filtered view. It shows only where the install can search at all.
Nothing is spent on the click. It opens a confirm that says how many searches will run — Find the owners of 12 businesses? — and what it leaves out, and why: businesses already searched for the website they have now, being searched now, whose last search did not finish (open each to see why, rather than paying to hit the same wall), and rejected ones. A page where nothing is left says so and opens nothing. Run N searches runs only the businesses the confirm named: one searched from its row since then is left out, and one that joined the page — or a change of filter — adds nothing.
One business at a time. Each is its own owner search, exactly as from the row, so a double click, the drawer's progress bar and a resumed run all behave as they do for one. The page run (prospect-owner-search-batch, one at a time on the install — a second click says it is already going) starts the next search only when the last has ended: a page of simultaneous searches on the install's own key would mostly be rate-limited into failures. A business whose search somebody starts from its row while the run is going is waited for, not searched twice. If the AI stops being able to search mid-run (the key removed, the lane switched to managed AI), the run stops there, in the search's own words — and so it does at the first search the AI provider itself turns down (a key that no longer works, an account out of credit), in the provider's words. Every search after it would fail the same way and be marked as not finished, which the next page run leaves out; stopping leaves the rest of the page unsearched, for the next page run once the key works. Its progress bar sits above the list while it runs, and its end is said once: 12 businesses searched — someone found for 8 of them, or — nobody found (and how many did not finish). What each search found waits in that business's Find owner — nothing is chosen for anyone.
It only searches. Friend and connection requests are never sent or marked in bulk (below).
Reaching the owner on Facebook and LinkedIn
A cold message lands differently on each network, so each link is named for what to do there — never Message on a person — and they are offered in order of where the message lands, in the Contact column and in the drawer's Reach out block:
- Message the Page — the business's own Facebook Page: the one its site links to, or the listed website when that is the Page. A message lands in the inbox they read customer messages in, which makes it the best social channel. No Message button on the Page means they have messaging turned off.
- Add friend — the owner's own Facebook profile. A stranger's message lands in Message Requests, often its hidden spam folder, so it is a friend request first and the message once they accept.
- Connect — the owner's own LinkedIn profile. A free account cannot message a stranger at all (InMail needs paid credits), so it is a connection request with a note — up to 200 characters, 300 with Premium — and the message once they accept.
A profile the site links to that is the owner's own is offered once, as Add friend, never as the Page. The Page offered here is not listed again among the business's other links. In the drawer the actions come from what is saved — a profile just picked with Use joins them after Save — and a found person's profiles are labelled Add friend / Connect too.
Opening a link sends and records nothing. Once a request has gone, Mark sent beside it (in the drawer, Mark friend request sent / Mark connection request sent) records the network and the time on the business — not as contacted. Contacted is the clock the not-viewed automation runs on, and starting it on a request would chase somebody, a few days later, about a pitch they were never sent; it is not a CRM touch either. A second click keeps the time the request first went. The Contact column then says Friend request sent 3 days ago — message once they accept until the message is recorded on that network through Record outreach (below), or the × beside it forgets the request (they declined, or it was withdrawn). Replacing or clearing the profile a request went to forgets it too — it was not sent to whoever is on file now. The Outreach filter's Waiting on a friend or connection request finds every business still waiting.
Requests are marked one business at a time and never in bulk: many friend or connection requests to strangers can get the salesperson's own account restricted on either network.
Importing into the CRM
Keeping a business and importing it are the same act. Each row offers exactly one positive decision — the keep button, which imports — and one negative one, Reject. There used to be a separate Qualify step that only set a flag, and the gap between qualifying and importing is where outreach recorded in between went missing from the CRM. One decision, no gap.
Each import creates a CRM company carrying name, website, phone, address, the PageSpeed score, the social profiles, and the trade as the company's Industry — which campaign found a business is what you would segment a later mailing by, so it moves across with everything else. It also creates a CRM contact whenever the business can be reached at all — by email, by phone, or through its own contact form. Only a business with none of those three imports as a company on its own.
Re-importing the same prospect updates both rather than forking a second one, and never blanks a field somebody has since filled in by hand in the CRM. (The contact is matched by the link the prospect stores, not by email address — a business that publishes no email has nothing to match on, so without that link every re-import would leave another copy behind.)
Contacts are tagged source = prospecting, which is what separates them from contact-form leads and what a CRM automation can scope on ("imported → create a follow-up task" is configuration, not code).
Who runs the business goes across too, on the first import and on every one after — which is how an owner chosen once the business is already in the CRM gets there:
- The name. A contact is named for the person to write to. When nobody was known at import, it took the business's own name; an owner chosen later replaces that name — but only on a contact the import created (
source = prospecting) and only while it still is the business's name. A name anybody typed in the CRM stays, and the drawer's save says so. A contact the import was handed by its email address (a form lead, a contact somebody made) is never renamed. - Title → Job title, LinkedIn → LinkedIn, Facebook → Facebook, only onto a contact that is the person named — never the office manager somebody filed under info@ — and only where the contact has none. A LinkedIn address longer than the CRM's field is left behind rather than cut; the Facebook field is as wide as the prospect's, so it always fits. Friend and connection requests are still marked from the prospecting list, never the CRM.
Recording outreach
Record outreach appears on a business once you have kept it, and asks two questions, storing both against the business:
- How you reached them — email, contact form, phone, the business's Facebook Page, or the owner on Facebook or LinkedIn. Pre-filled from what the business exposes: an email address if there is one, otherwise the contact form if they have a website, otherwise the phone. Change it if you did something else; what you say wins over what was guessed. On Facebook or LinkedIn this records the message itself, sent once they accepted the request — which ends that network's wait (see Reaching the owner on Facebook and LinkedIn). The request alone is marked from the Contact column and is not outreach.
- What you sent — a custom video, or the generic message.
The second question is the one that matters and it is not optional. If the plan is a personal recording for the best prospects and a generic message for the rest, then whether that recording earns its two minutes is the most expensive open question in the whole operation — and it is only ever answerable by comparing reply rates between the two. Recorded at the moment of contact it costs nothing. Reconstructed a month later it cannot be done at all.
The same action writes an interaction on the CRM contact's timeline, so the two screens never disagree about whether somebody has been approached. This is why the button waits for the import. An approach recorded before the business was kept has no CRM contact to land on, so it would sit on the prospecting row alone while the CRM went on counting the company as never approached — each screen correct on its own, which is exactly why nobody would notice. Withholding the button makes that order unreachable rather than merely recoverable. Businesses already in that state from before the rule are repaired on import, which carries the earlier touch across and stamps it with the date it actually happened. The interaction type follows the channel — email sends log as an email, phone as a call, and a contact form we filled in as a note, because CRM's form submission means a form came the other way; a Facebook or LinkedIn message is a note too. All of them count as real touchpoints, so the contact stops showing as going cold.
The contact form is opened by a person, never submitted by us. Auto-filling other people's forms breaks most sites' terms, gets the server's IP blocked, and a templated form fill reads worse than a templated email would. The feature finds the URL and hands you a link.
The Outreach filter narrows the list to businesses nobody has written to yet, which is how you work through a long list without losing your place — or to the ones Waiting on a friend or connection request, the list to check for an acceptance.
Emailing a business
The envelope on a kept business opens the CRM's Send Email box, addressed to its contact. The send is the log: the email lands on the contact's CRM timeline, and the row is stamped as contacted by email, in the same step. Like Record outreach it waits for the import, and it appears only when the contact has an email address. A company-only import (no email, no phone) shows the pencil instead, to add one; a phone-only business has a contact with no address, so its email is added on the CRM contact.
The box can start from two kinds of wording, and they answer different questions:
- A view's email template — what the box opens with for businesses on that view. A view is a campaign, and the campaigns argue different things (no website at all cannot be told it needs more service pages), so each view has its own. Edit it with Add / Edit email template in the views bar.
- Saved messages — named emails picked per send: the first email, the follow-up, the version for a slideshow pitch. Saved messages, above the list, keeps them (name, subject, message; all three required, names unique). In the Send Email box, Use a saved message replaces the subject and message with one of them. If that replaced something already in the box, Put back your draft restores it, once. Trying one message after another keeps the draft from before the first pick as the one to put back. The line under the recipient says which saved message the box was started from.
Both are stored with their tokens standing and filled for each business when they are put into the box: {{first_name}}, {{contact_name}}, {{business}}, {{website}}, {{city}} and {{pitch_url}}. {{video_link}} is never filled — it is there to be typed. A token the business cannot fill is left standing, and the send is refused while any {{…}} remains — so a message built around {{pitch_url}} cannot go to a business whose pitch page is not published yet (the refusal says so in those words). There is deliberately no "save this email as a message" in the box: by then its tokens have been filled for one business, and a message saved from it would carry that business's name and pitch link to every one after it.
Saved messages are offered only in the box opened from the prospecting list — the CRM's own contact and company pages have no business to fill the tokens from. They are one set for the install (prospecting.email_messages), like the views. Nothing is shipped as starter copy: the wording names a person and a business.
The mail adds its own greeting (Hi Martin,) when the contact is named for a person. A contact still named after the business — the import's stand-in until an owner is chosen — gets none, so a message can open with its own line (Hello, my name is…) without being greeted twice; once an owner is chosen, Hi Martin, comes back above it.
Site audit and video talking points
Audit their website (the magnifying-glass button on a row) reads one business's site page by page and writes the talking points for the outreach video: one line per problem it measured, with the page, link or words that prove it, then your offer line if you have written one. It is there on any business with a website of its own, kept or not — the video comes before the decision to keep a business and after it. A business whose "website" is a Facebook page, or that has none, gets no button, and the server refuses it too.
The ground rule is that a claim it cannot prove is not made. One wrong claim on camera loses the sale, so:
- Only pages that actually loaded count. A page that answered 404 has no title anybody can see, and is never reported as "missing a title".
- A link is dead only on a 404 or 410 — including one that redirects to a page that answers that way. A refusal, a rate limit, a server error or a timeout is "couldn't check", and the modal says so as a note, never as a problem. A server that mishandles the quick check is asked again properly before anything is called dead.
- Not secure is said only for a certificate a browser would refuse too (expired, self-signed, for a different address) or a site with no secure connection at all — and it is the only finding allowed to say a browser warns visitors. A plain
http://address that doesn't send people on tohttps://is said as a fact about the site, because modern browsers upgrade the address themselves. - When a certificate like that is the only way into the site, the audit stops at the same warning a visitor does. Instead of failing, it stores that one finding, which becomes the single talking point, and notes that none of the pages behind the warning were read (No pages read). Run it again once the certificate is fixed. A site that is simply down (refused, timed out) still fails with Could not read the site: a dead server is not an insecure one.
- A site that builds its pages with script in the browser (the home page, or the typical page, carries under 50 words in its HTML) gets none of the findings that read an absence out of the HTML — no phone number, no headings, no reviews. The modal notes that it is built that way, so you check those by eye.
- The phone number is judged from the home page a phone is served. Pages are read the way a desktop gets them, and some builders — Wix above all — serve phones a different page, so a number that is plain text on the desktop copy can be a tap-to-call link on every phone. The audit reads the home page once more as Safari on an iPhone: not tap-to-call is said only when that copy shows the number as plain text, and no phone number only when neither copy shows one. When the phone copy can't be read, nothing is said about the number and the modal notes why. A number only the phone copy shows still counts as on the website when it is compared with Google's. An audit stored before this check existed judged the desktop copy, so its phone points are no longer shown — run it again to get them.
- Spelling mistakes come from a curated list of misspellings, matched exactly — never a British spelling, a real word in the wrong place, or a brand name. A doubled word is only a small word nobody doubles on purpose (the the, to to) — never a name before its verb (our tech Will will call) or a phrasal verb before the next phrase (log in in seconds). Both lists are English, so both are read only on pages that say they are in English (or say nothing): a site's Spanish copy (
<html lang="es">) is checked for everything else, but profesional is right there. - Template text is a phrase only a template writes. A stand-in name (Your Company Name, Business Name Here) counts only standing alone — a line of its own, a part of the title, a copyright line — because in a sentence it is ordinary English: "you grant us the right to display your company name" is in half the terms of service on the web. An example address the site gives on purpose ("e.g. example.com", "like [email protected]") is an example, not a leftover.
- A picture has no description when its alt text is missing or empty (empty is what WordPress writes when nobody filled it in) — except a picture the page marks as decoration (
role="presentation",aria-hidden="true"), where an empty alt is the right answer. - Titles, headings and the city are about what Google shows, so they read only pages Google is allowed to show. A page that asks to be left out of search is reported as that, once, never also as missing a title.
- The home page's headline — its H1, the heading Google reads as the page's headline — is called keyword-free only when no H1 on the desktop or the phone copy names the work once the business's name is taken out of it as a phrase: Welcome to Acme Electric and a bare Acme Electric both are; Electrical Service by Acme is not. A word counts as naming the work when it is a trade (electrician, plumbing), a kind of work any trade does (repair, installation, wiring), the business's own trade or Google category, or any word of the site's own page addresses that isn't a place or a page every site has — so EV Chargers & Generac on a site with an
/ev-chargerspage is fine. Counting too many words only makes the check quieter, never wrong. It is said as advice — the heading should name the work — because a name like Acme Electric does name a trade. - The towns around the city come from the coverage sweeps, the only geography the list keeps: every small searched section stores its bounds and the town its results mostly sat in. Only the sweeps that found a business in the same city and state are read, so an Arlington, Virginia never borrows the towns of a sweep that covered Arlington, Texas, and a town whose sections lie too far apart to be one place (two towns of one name inside a state-wide sweep) is left out. Towns within 40 km of the business's city are named (the most businesses first), and any town within 60 km counts as a mention. The point is made only when no page read names a town of the area and no page address holds one, written any way people write it (Ft. Worth, GRAND-PRAIRIE, St Paul). An area no sweep searched closely, a city name two sweeps place far apart (an Arlington in Texas and one in Virginia), or a business whose city isn't one Google returned for a section gets nothing. A town whose name is an ordinary word on the page (Mission, Center) reads as mentioned — the safe way round.
- Town pages are the next step up: a site that does name towns of the area (a Service Areas page listing Edmond and Moore) but has no page about any one of them is told so — "Your /service-areas page names towns you work in, like Edmond, Moore, but none of them has a page of its own." A page counts as a town's when its address names a town of the area or the city (/edmond-electrician), sits in a service-area folder (/service-areas/hooks-tx, /locations/…, /areas-we-serve/…), or ends in a state code (/moore-tx; the codes that are also English words, like ok and in, don't count alone). They are looked for among every page the site lists — its sitemap and home-page links, not only the pages read — and every link on the pages read, so one town page anywhere silences the point. The folder itself (/service-areas) is the list, not a town's page. A site naming no town at all gets the point above instead, never both, and an area with no known towns gets neither.
- Slow is said in seconds when the stored PageSpeed run's largest-contentful-paint time is past Google's own four-second poor line ("Google's own speed test took 6.2 seconds to load the main part of your site on a phone…"), and by the score only when that is under 50 and the time isn't known to be poor. Both come from the stored run — the audit never re-runs PageSpeed. The time is the run's exact figure, kept since this check existed; an older run's display text is read only in Lighthouse's own form ("6.2 s", "1,240 ms"), never a decimal comma, and a time of two minutes or more is a stalled run, not a site. An audit stored under an older rule never says a time Google wouldn't call poor.
- A Spanish version is looked for every way a site offers one — a page that is itself Spanish (
<html lang="es">), a declared Spanish alternate (hreflang="es"), a link to a Spanish copy (/es/,/espanol/, anes.subdomain,?lang=es) or one that says Español / ES, or a translation widget or plugin (Google Translate, GTranslate, Weglot, WPML, Polylang and the like). Any one of them means nothing is said — a business whose site has a translate button would rightly say it has Spanish. The point is made only when none is found and the home page reads as English (it says so, or says nothing and its words are English ones), and it is said as a condition, not a pitch: "I didn't find a Spanish version of your site. If anyone on your team speaks Spanish, Spanish versions of your pages let you show up for searches made in Spanish…" - Social proof is a pass when any page read, the home page a phone is served, or a listed reviews or testimonials page shows a review, a testimonial, star ratings or a review widget — including a quote slider the theme marks as testimonials in its markup, with no "review" or "stars" in its words. A site that shows none is said to — in its own Google reviews' words when those are worth showing (4.0 or better from 10 or more: "You have 49 Google reviews averaging 4.8 stars, but I didn't find them on your website."), and plainly otherwise. Google's rating no longer decides whether it is said.
- Word count and meta descriptions are not findings. Google says it has no preferred word count and doesn't rank on the meta description, so "your home page has only 163 words" is a claim a prospect can disprove with one search — exactly the kind that loses the sale. Each page's word count is still in the table of pages read, for whoever records the video. What a short site is really missing — a page for each job — is the service-pages point.
- The home page title is called only the business name when every part of it is the name Google lists or says nothing a search could match: Acme Electric, Home | Acme Electric, Welcome to Acme Electric, acmeelectric.com. A legal suffix (LLC, Inc) on either side doesn't count. Anything more — Acme Electric | Dallas, Acme Electric | Electrical Contractor — is a title that names the work or the place, and nothing is said. A title worded differently from the listed name is never guessed at.
- A link is the address a visitor follows. A script binding (
x-bind:href="result.url") is not a page, and is never read as one.
What it checks
Up to 40 pages — the site root first (Google often lists a city page or a tracking link instead), then the home page's links, then the sitemap, with reviews and testimonials pages read early and blog archives, tag pages, files (PDFs, images) and sign-in, account, cart and checkout pages skipped — then up to 60 more links and 60 pictures, then the findings, in three groups:
A site with a translated copy reads its own language first. The copy's folders are the ones its home page declares as hreflang alternates (/es/ beside an English home page), never guessed from a two-letter folder name, which is as often a state as a language. Those pages only fill places the main language leaves free.
| Group | Findings |
|---|---|
| What their customers see | not secure; http:// not sent on to https://; files browsers block on a secure page; dead links; pictures that don't load; leftover template text (lorem ipsum, Your Company Name, a 555 number); a home page not set up for phones; spelling mistakes and doubled words; a website builder's address instead of a domain |
| What costs them calls | no phone number on the home page; a number that isn't a tap-to-call link on a phone; the number Google lists appearing nowhere on the site (not said when the site runs call tracking); no reviews or testimonials anywhere read, on the phone copy or as a listed page (social proof); a free email address with none on their own domain |
| What Google sees | robots.txt shutting Google out; pages telling Google to leave them out (the home page called out); service pages; the business's city in no page title; template titles (Home, Untitled — but never Home | Acme Electric — and any title carrying a website builder's stand-in for the site's name, Services | Mysite 1, Site Title, Just another WordPress site, which the point names: "3 of your page titles… still carry 'Mysite 1', a placeholder your website builder put there"); a home page title that is only the business name; a home page headline (H1) that doesn't name the work; no town around the city named anywhere; towns named but no page for any of them; no Spanish version; missing and duplicate titles; no main heading; no business markup; business markup of the wrong type — only a generic Organization / LocalBusiness when schema.org has a type for their Google category, or another line of business entirely (judged the same way as the Schema type badge, and said in Google's own words for the business: "Google lists you under 'car repair', and there's a type made for exactly that: 'AutoRepair'"); pictures with no description; a slow load — over four seconds by Google's own test, or a PageSpeed score under 50 |
The service-pages verdict is the one the automatic audits already store; a business nobody has checked gets it during the audit, from the same reading of the site. The speed finding uses only the stored PageSpeed run — its load time past four seconds, or its score under 50 — and the audit never re-runs PageSpeed.
The talking points
Written by code, not AI — every point the audit can say, in a fixed order — what stops a visitor cold first (Google shut out, not secure, template text, dead links, not set up for phones), then the service pages, then what costs calls (the phone number, social proof), the home page's title and headline, the towns around them, a Spanish version, then spelling and the rest. An old copyright year is not a point at all: it says nothing a customer cares about, and a site that writes its year by script would make it wrong. Nor are stock photos: plenty of good sites use them, so knocking them is a point the prospect can turn around. Nothing is capped: the points are the whole story, so a finding the audit makes is never left out of them. The service-pages point is said with their count — "I noticed your site has only two pages dedicated to a single service…" — followed by why a search for one service needs a page about exactly that (its own title, heading and content), with an example from their own trade: panel upgrades for an electrician, water heater installation for a plumber, AC repair for HVAC, and a version naming no service for a trade without one — never a showcase site's. A free email address is on the findings list but never spoken (email is not in the offer). A site with nothing measured against it says exactly that, without filler. The opener promises nothing ("I spent a few minutes on alpha-electric.com…"), and the closer asks them to use the button below the video to book a call or a screen share about a new website, gives the offer line from the pitch modal's Shown on every pitch page word for word (a full stop added if it has none), and thanks them: "I appreciate the chance to earn your business." A blank offer line is simply left out — no price is ever supplied for you.
The points are put together each time the modal opens, from what was stored — so they read in the language of whoever opens it, and a wording fix reaches every audit already on file. Copy copies the points exactly as they appear on screen. Underneath: the notes on what could not be checked, then two folded sections — Where each point was found (every finding in its group with the page, link or picture it is about, linked) and a table of every page it opened with how it answered, its title and its word count.
Running it
The audit runs in the background — a page per step, so it survives being stopped and picks up at the next page — and keeps going if you close the modal; open it again to see how far it has got. Pressing the button twice, or on two screens, joins the run already going. It takes a few minutes on a slow site.
A run that cannot finish says why rather than storing half an audit: the site never answered, it moved to another domain (named), not one page loaded, or the website was edited while it was being read. Opening the modal again after a failure shows the reason and a Try again button rather than starting over on its own — unless the business's website has been changed since, when it starts an audit of the new one instead of repeating a reason about the old. Run again replaces the audit on file when it finishes. Editing a business's website clears its audit, so the old site's problems can never be shown under the new one.
Limitations
- It reads what the server sends, not what a browser draws. Anything a page adds with script after it loads is invisible to it — which is why script-built sites get the treatment above.
- 40 pages, 60 links and 60 pictures per run. A bigger site is reported as "at least N pages listed", and links or pictures past the limit are noted as not checked.
- One audit per business, replaced by each run. It is separate from the automatic audits and never changes their results, filters or backfill.
Sales playbook
Sales playbook (the open-book button beside the audit button) puts everything for the call on one printable page: the one-breath pitch, the prospect card, the offer, the call sequence, the twenty-minute meeting, the fine print, answers to push-back, the after-call checklist and the hand-off. It opens in its own tab, on your dashboard — print it, or keep it beside the phone. It is offered on exactly the businesses the site audit is: a business whose website is a Facebook page, or that has none, gets neither.
On each row the buttons sit in two rows: prepare (the audit, the playbook) above act (edit, keep and reject — or, once kept, email, outreach, the pitch page and the CRM record).
What fills it
- What Prospecting already knows — the business, city, phone, email and website from the row; the platform and page list from the site audit; their service pages from the automatic audit; their Google rating, and whether any page shows reviews. The gaps are the site audit's findings, in the exact words of the video's talking points — never reworded, never AI. Without a site audit of the website the business has now, the page says so.
- Your playbook settings — the modal's Your playbook settings panel, shared by every prospect's playbook: your business name (the site's business name when blank), who makes the calls, who records the video and runs the demo, the price as you say it, why it pays for itself, more to say about price, what the price includes beyond the site, the fine print (a short label and what it means, per term), the video-call app, a services-checklist link, a plan-terms link, and a showcase site (name, link, city, service pages, languages, launch). A blank setting leaves its sentence out — nothing is filled in for you, and no other agency's names, price or terms are ever printed. Links must start with
http://orhttps://. The script speaks the prospect's own trade (from the row), and says "business" when none is on file. - The AI read — the lines that need judgment (below). Until it has run, each is a labelled blank line to fill in from their site before you dial.
The AI read
Read their site, in the playbook modal, reads the business's own pages and photos and writes the prospect card's judgment lines: what they say about themselves, the owner's first name and where the site names them, crew size, the towns or area they serve (the address beside it is the one on file), hours, a note on their market, the one concrete thing to show them first, what their photos prove, two or three specifics to ask about, and the pages you would build.
The ground rule: every line traces to a page or a photo that was read. Each line carries a small tag naming where it came from — a page path (with its ?page_id=… on a site without pretty links, the same way the card's page list names it), or photo N — and:
- A line is kept only when it names a page that was actually sent to the AI, or a photo the AI actually looked at. A full web address counts only when it is on their own site — another site's
/aboutis not their About page. A line with no valid source is dropped and printed as a blank, never guessed. - The owner's first name is asked for only when the site names someone as its owner, founder or president, or as the one who leads or runs it (Led by Dana Reyes). It is kept only when that name appears in the page or photo it cites — written as a name is written, or in capitals (a MEET DANA heading), so we will call you back never names a Will. Words in front (Owner: Dana, Led by Dana) are skipped, and the name itself is what has to appear.
- The photos line needs at least one photo as its source, and is always dropped when the AI could not see images.
- If not one line of the answer can be traced, nothing is stored — the run fails rather than storing a read that looks like "the site said nothing".
- Their site's words go to the AI as source material, never as instructions, and everything the AI wrote is printed as plain text.
What it reads. It starts from the site audit, so a site audit of the website on file comes first — without one the modal offers Audit their website instead. From the pages that audit loaded, up to 10 pages: the home page, About, up to 5 service pages, contact and service-area pages, reviews, then the rest — at most 6,000 characters a page and 36,000 in all (a page past the total is not fetched). A page the site also has without a two-letter folder in front (/es/about-us beside /about-us) is the same page again, so it is read last, only into places left free. The home page is read with its header and footer, where hours and towns usually are. Then up to 12 photos from those pages: not stock, not a logo, icon, badge or map, not marked decorative, at least 200×150 px and at most 5 MB, in JPEG, PNG or WebP. Each is downloaded through the same guard as every remote image (never from a private address), looked at once, and deleted straight after. A Wix site's HTML carries only a blurred 90-pixel stand-in for each picture, swapped for the real one by script; the read asks Wix for a sharp copy of the same file instead, so a crew photo there isn't ruled out as too small.
Which AI. The install's AI text provider — one short look per photo, then one call that writes the card. Managed AI is metered as every other AI feature is. An AI that cannot see images (DeepSeek) reads the pages only: the modal says beforehand that photos won't be looked at, and the page prints that the photos were not looked at — never that they show nothing. When it could see but every picture on the pages read was ruled out as theirs (stock, a logo or icon, too small), the page says no photos of their own were found. When pictures that may be theirs could not be downloaded or looked at, it says they were not looked at instead — never that there are none. With no AI set up, the modal says so and the playbook still opens, its judgment lines left blank.
Running it. The read runs in the background — a page, then a photo, per step, so a stopped run picks up at the next photo instead of paying for every one again — and carries on if you close the modal; open it again to see how far it got. Pressing the button twice, or on two screens, joins the run already going. Opening the modal never starts a read on its own: it is the one step here that costs AI credit, and the modal is also where the settings are edited. Nothing is stored until the end, so a half-read site never shows as a finished read. Read the site again replaces the read on file once it finishes; until then the page keeps showing the old one.
A run that cannot finish says why: no site audit of the website on file, no AI set up, the website was edited while it was being read, none of the site's pages could be read again, the AI gave no usable answer (cut off, or not the JSON it was asked for), or its answer named no page or photo it was given. Opening the modal later shows the reason rather than trying again — unless the business's website has changed since.
Editing a business's website deletes its read, along with its site audit, and the page only ever shows a read of the website the business has now — so one site's owner and photos are never shown under another.
Limitations
- It reads what the server sends, like the site audit — a page drawn by script after it loads reads as nearly empty.
- A source tag is a pointer, not a proof. It guarantees the AI was given that page or photo, not that it read it right. Check a line against its source before you say it on the phone.
- It errs toward saying less. An owner named only in a customer review, or a job photo whose trade isn't obvious at a glance (conduit laid on a slab before the pour), is left blank or called unproven rather than guessed. Look at their photos yourself before the call — the strongest proof of bigger work is often a photo the copy never mentions.
- One read per business, replaced by each run. The settings are install-wide; the read is per business.
Pitch pages
A Pitch page button appears on a business once you have kept it. It builds one page on your own site carrying your video and your offer, on a link only that business has.
The point is not presentation, it is attribution. A view counter on a third-party video player tells you somebody watched; it cannot tell you it was this business, and nothing on your dashboard can act on it. A page on your own site is a signal you own: it lands as a view count on the prospecting row, an entry on the CRM contact's timeline, and a conversion in Analytics — and an automation can be written against it.
The video is the page. The findings are said in the video, so the page does not list them — a written list beside it would only give the business a way to skip pressing play. Above the video go your headline and, below it, an optional note. The only thing you have to type is the video link.
What the page shows
One picker, What the page shows, chooses between:
- YouTube, Vimeo or Loom — paste the link. A Loom
/share/link is rewritten to its embeddable form automatically, which is what makes Loom work here at all. - Video library — the Video Hosting add-on (Cloudflare Stream, Bunny or Mux). Only offered when that add-on is on, because a picker with nothing behind it is worse than no picker. It is the stronger option: as well as the page view, you get play counts and watch time, so you learn whether they watched ten seconds or all of it.
- Video file hosted on this site — pick a video from the media library, or upload one from the picker. The picker shows videos only and opens on a Prospecting category, made the first time it is needed, so pitch videos are uploaded into it rather than among the site's own media. It is stored and played by your own site: no third-party player, no add-on, nothing for the business's browser to fetch from anyone else. You get the page view, but not play counts or watch time — that is the Video Hosting add-on's job. It must be an MP4 in H.264, and saving refuses anything else: a
.mov, a WebM or an iPhone recording (HEVC) all play on the Mac that uploads them, but can show the business a black box in Chrome, Firefox or on an older phone, and nothing here converts them — the embed and hosted options do, through their provider. The codec is read from the file's own header, not its name. - Slideshow of their website — a deck you build on its own page (see below).
A link that no player can open is refused when you save it, rather than discovered by the prospect, and so is an uploaded video that is no longer in the media library. Saving writes only what the chosen option uses: the video link, the library video, the uploaded video and the slides each stay on file when you switch to another, so switching back costs nothing.
A video that goes missing after the pitch is published — the add-on turned off, the hosted video deleted at the provider, the uploaded file deleted from the media library — does not take the page down: the link the business holds still opens, to the headline, note and offer without a player. The media library's Unused only filter never lists a video a pitch holds (even one switched to another option), but deleting a single video does not warn that a pitch uses it, so check before deleting one.
The video is exactly as wide as the headline above it — it fills the top row's column and never goes past it, so its size is the column's. The shipped page sets that column to 80rem (1,280 pixels). A pitch page already on an install keeps the width it was copied with (earlier copies are 1,024 pixels wide): change it in the page editor, on the top row's container width.
A slideshow instead of a video
A pitch can be a slideshow in place of the video: a short deck walking the business through what was found on its website, one marked-up screenshot at a time. A pitch shows one or the other, never both, and switching between them keeps both — the video link and the slides stay on file, and only the chosen one is shown.
The screenshots are taken outside the CMS, by you or by a Claude agent driving a real browser, and uploaded to the deck. Each one is checked and re-encoded before it is kept:
- JPEG, PNG or WebP only, read from the file itself rather than its name, up to 15 MB, at least 200 × 100 pixels.
- Scaled to 1,600 pixels wide. A capture taller than 10,000 pixels at that width is refused — take a shorter part of the page — and so is one too large to process safely (over 24 million pixels).
- Re-encoded, never copied. Whatever the uploading computer wrote into the file (camera or location data, anything hidden after the picture) is left behind.
The screenshots are private. They are not in the media library and have no public address: each is served only through the business's own pitch link, only while the slideshow is the published pitch — and to your staff while they build it. A pitch switched back to a video stops serving its slides even though they are kept. They are included in backups.
A slideshow counts as published only when it has a slide worth showing — a screenshot or at least a headline. Deleting the last such slide from a live slideshow takes the pitch down, keeping its link, so nothing (the outreach email's link, the has not opened automation) points at a page that would not open. A deck holds at most 40 slides; deleting a business deletes its screenshots.
Building the deck
Choose Slideshow of their website in the pitch modal and press Build the slides: the deck opens on its own page in a new tab (Prospecting → a business → Slideshow, Manager and up). Once the pitch is a slideshow, or while Claude is building it or its draft waits for you, the business's row also gets a slides button straight to it. On that page:
- Add screenshots — pick several at once; each becomes a slide at the end of the deck. A file that is refused says why, by name, and the rest still go in.
- Start from the audit — on an empty deck, when the business has a site audit of its current website: a title slide, then one slide per finding in the order the talking points say them, each linked to its finding and waiting for a screenshot and words. The finding's own sentence is shown beside the slide as the reference, never copied onto it. It never runs over a deck that already has slides.
- Words — a headline and a few lines of body per slide, saved as you leave each field. Typing over an agent's draft makes the words yours (the Agent draft badge goes). What the audit found links the slide to a finding.
- Pictures — Replace picture swaps the screenshot and keeps the slide's words and place, but not its marks (see below); Remove picture keeps the words; Add a slide without a picture makes a words-only slide (a title, a summary). Removing a picture and deleting a slide both ask first.
- Marks — Mark up on a slide's picture opens it large, to draw on (see below).
- Order — drag a slide by its handle.
A slide with neither a picture nor a headline is marked Not shown yet. Changes to a published deck show on the business's link straight away. If a change takes the last showable slide away, the page says the pitch was taken down.
The top of the page says where the pitch stands: Published, with the business's link; Not published, with how to publish it — the pitch modal keeps the Slideshow choice only once the deck has a slide to show, so a deck started from a business with no pitch yet still says to choose Slideshow there and save; or, when the business's pitch is a video, that its slides are kept but nobody sees them until Slideshow is chosen.
When the business's website changes, the slides made for the old one are flagged Old website, with a warning above the deck: their screenshots may show a site the business no longer has. Replacing a slide's picture marks it as made for the current website.
Having Claude build the deck
Have Claude build the slides, on the deck's page, hands the deck to a Claude agent running in the cloud — a Claude Code scheduled task — so it gets built while nobody is at the dashboard. On each run the agent asks this site's API which businesses are waiting, reads each one's brief (the website, what the site audit found in the order the talking points say it, each finding's pages and evidence, and the deck so far), opens the pages in a real browser at desktop and phone widths, captures what each finding is about, marks it up and writes the slide. Then it says it is done, with an optional note for you.
What it makes is a draft. It can never publish the pitch: it is refused outright while the slideshow is the business's published pitch, and the business sees nothing of its work until you save the pitch modal. Its slides carry the Agent draft badge. While it waits, the business's row reads Claude is building the slides; when it finishes, Draft ready to review, and the deck's page shows a Claude's draft is ready to review note with anything it wanted you to know. Mark as reviewed clears it, and so does publishing the slideshow.
It is offered only when the agent can do the job: the business is imported, has a website, has a site audit of the website it has now, and its slideshow is not live. One request at a time per business — pressing it again while it waits does nothing — and Cancel the request withdraws it, after which anything the agent was in the middle of is refused. Editing the business's website drops a waiting request: the agent's brief would be of a site the business no longer has. A slideshow cannot be published while Claude is still building it — the pitch modal says to wait for the draft or cancel the request — so its next slide never reaches the business unreviewed.
The agent never takes away your work. It fills slides waiting for a picture (the deck Start from the audit made), changes its own drafts, deletes only slides it made, and when it orders the deck it moves only its own slides around yours. Once you change a slide in the builder — its words, its picture or its marks — it is yours, whoever made it, and the agent leaves it alone. Linking a slide to a finding does not count, so a placeholder you re-link is still Claude's to fill.
Setting it up — the Claude instructions panel under the button has the steps and a prompt to copy:
- Turn on API in Settings → Features, and create a token with only Read slideshow requests and Draft slideshows, and an expiry. The slideshow endpoints refuse any token that can do more: the agent reads text the businesses wrote, so a token able to do anything else would let a page written to trick it do that too.
- At claude.ai/code/routines, create a scheduled task that runs hourly, with the copied prompt as its instructions. Give it a repository (any one) so it runs as a Claude Code task with a shell.
- Give its cloud environment Full network access (the businesses' websites are on any host — the default Trusted level refuses them) and a setup script that installs Playwright and Chromium.
- Add the token to that environment's network secrets as a Bearer secret for this site's host. The token then reaches this site without the agent ever seeing it, and never sits in the prompt.
The task polls hourly; each request also sends the prospect.slides_requested webhook (Webhooks), for anything else you connect. The webhook cannot start the task itself — that needs a bearer token on the request, which outbound webhooks do not send. The API is documented under Slideshow pitches in API.
Marking up a screenshot
Mark up (or Edit the marks, once it has some) opens the slide's screenshot full width, scrolled to its highest mark when the capture is tall. Two kinds of mark:
- Box — drag across the screenshot to draw one around what you want the business to look at. Select it to drag it somewhere else or pull a corner to resize it.
- Numbered pin — click to drop a numbered circle on a point. Pins are numbered in the order they were drawn; boxes are not counted.
Each mark can carry a short label (up to 80 characters, plain text), typed in the list beside the picture, which is also where a mark is deleted. A selected mark moves with the arrow keys (Shift moves it further; Alt or Option with the arrows resizes a box) and Delete removes it. A slide holds up to 12 marks. Preview shows the marks and the slide's words as the business sees them. Nothing is saved until Save marks; Cancel, or closing the window, leaves the slide as it was, and the next opening starts from what was saved. A click outside the window does not close it, so a box dragged past the picture's edge is not lost.
Marks are kept in percent of the picture, so they sit on the same spot at any width the slideshow is shown at, and drawn over the screenshot rather than into it, so they stay editable and sharp. Whatever the browser sends is checked again when it is saved: only boxes and pins, inside the picture, at most 12, labels as plain text. Marking up a slide is not writing its words, so an agent's draft keeps its Agent draft badge.
A new picture takes the marks off. Marks drawn on one screenshot point at whatever happens to be in those places on the next — and replacing the picture is exactly how a slide of an old website is brought up to date, so the old boxes would sit around the wrong things in front of the business. The builder says so when it happens; mark up the new picture. Removing a picture takes its marks too. For the same reason, marks are not saved onto a picture that was replaced (in another tab, or by an agent) while the editor was open — it says so, and you mark up the new one.
To publish, go back to the pitch modal and save. A slideshow with no slide worth showing is refused there, because saving publishes.
What the business sees
The slideshow sits where the video would, so the page's headline, note and Book a free call row are the same for every pitch. It shows one slide at a time, with Back, "3 of 9" and Next beside the headline and again in a row under the slide, so a business that has scrolled down a tall slide never has to scroll back for them — or the left and right arrow keys, or a swipe on a phone. The row under the slide also has First and Last, with the count in the middle. Last goes straight to the closing card, not to the last screenshot, and skipping there never counts as finishing. A button that can't be used where the business is (Back and First on the first slide, Next and Last on the closing card) is greyed out and disabled, never hidden, so the row keeps its shape. Each slide is its screenshot with the marks fading in over it, then its headline and words. A tall screenshot scrolls inside its frame and opens scrolled to the first thing marked on it. Slides not shown yet load as they come up. A slide with neither a picture nor a headline is skipped.
After the last slide comes a closing card: your offer line, the Book a call and (when set) See plans buttons, and under them an Email us link (when set), from the same settings as the page's own Book row. Without JavaScript the page shows every slide in one list, marks and all. The page's description, shown in link previews, says it is slides rather than a video.
How far they got
Each slide the business reaches in order is recorded, and the business's row shows how far they got. A slide counts only once every slide before it was shown, so jumping ahead with Last never reads as finishing: Slide 7 of 9, then Finished the slideshow once they reach the last slide. It is counted against the deck as it is now, so a deck cut down after they looked never reads "12 of 9". Going back never lowers it, and two tabs or a reload never count the finish twice. Finished stays once reached: a business that got to the end of the deck it was shown has finished it, so slides added afterwards don't undo it, and it is not logged again. Reaching the last slide also adds Finished the slideshow to the contact's CRM timeline, once. Like a view, it does not reset the going-cold clock, and it is filtered the same way: link scanners, your own staff, an IP on the Analytics exclusion list or a browser with the tracking opt-out cookie record nothing. Nothing is recorded for a pitch that is not a published slideshow.
The offer at the bottom
Every pitch page closes on Book a free call: a line saying it is a no-obligation call about the project, your offer line after "If you decide to move forward:", a Book a call button, an optional See plans button for a business that wants to compare prices first, and under them an optional Email us link for one that would rather write than talk. All of them are settings, edited from the same modal under Shown on every pitch page: the offer line, where Book a call goes, where See plans goes (your plans page) and where Email us goes (your contact page), each a path on this site such as /book, or a full URL. They are install-wide, not per business. A blank offer line leaves the price off — the page closes on the call and the buttons alone, and the video's closer asks them to book and thanks them with no price in it — because any price shipped as a default would be one agency's price on every other install's pages. The offer line is deliberately separate from the playbook's price: the playbook is your caller's script, said once a prospect asks, and the pitch page is public. A blank Book a call target goes to /book; a blank plans or contact link leaves that button or link off, since no install's plans or contact page is at a path every install shares. Pitch pages copied out before See plans replaced it can still carry a Sign up without a call button; the modal keeps its link setting for them.
Publishing and taking it down
Saving publishes. There is no separate publish step, because the only state it could add is one where the link is live and the page is empty.
Take it down unpublishes without discarding the headline, the note or the link. Re-publishing hands out the same URL — the old one is already inside a video somebody was sent, so rotating it would dead-link a page they may be about to open.
Editing the page
Every pitch page is the same page, and you edit it in the page editor like any other: open a pitch link while logged in and press Edit Template, or, as an Admin, find Prospecting: pitch page under Pages → Templates. Change the copy, the styling and the order, or add rows — testimonials, your work, an FAQ — and every business's page changes with it. Preview as lists the businesses that have a pitch link, so you see the page with a real name and video while you work, unpublished pitches included.
Per-business values are tokens the page fills in for whoever opens it. Insert dynamic data lists them:
| Token | Fills in |
|---|---|
{item.name} |
the business's name |
{item.city} |
its city |
{item.website} |
its website, as a bare domain (alphaelectric.com) |
{item.headline} |
the headline written for this business, or nothing |
{item.note} |
the note written for this business |
{item.offer} |
the offer line (Shown on every pitch page) |
{item.book_url} / {item.signup_url} |
where the two buttons go |
The headline reads {item.headline:default(A couple of things I noticed about your website)}: the business's own headline when one was written, otherwise the text in brackets, which you can change. The video is each business's own and is not page copy, so its row says where it is set. A line or button whose token comes back empty — no note, no offer line, no sign-up link — is left off the page, so nothing reads "If you decide to move forward:" with nothing after it.
Only those tokens exist. A token for anything else on the business — its email, your notes — prints nothing, and the page cannot be used as a data source to list businesses anywhere else on your site.
The page is copied in when Prospecting is turned on, or by the next CMS update on a site that already had it on, and an update never overwrites your edits. If the copy is ever missing, every pitch link keeps working on the page as it shipped. The link, the view recording and the publish rules stay where they were: the page editor changes what a pitch page says, never who can open it or whether the visit is counted.
What a view triggers
Two automation rules become available under CRM → Settings → Automations once prospecting is on:
- Opens their pitch page — fires the first time they look, however many times they reopen it. The rule worth writing is a call task: somebody who just watched is the warmest they will ever be.
- Has not opened their pitch page — a daily sweep, counted in days. It fires once per business, not once a day forever.
The second one counts from when the pitch was sent — outreach recorded on or after the page was published, which the list's email button does for you — not when the page was built. A page built on Monday and sent on Thursday has not been ignored for three days, and a page built and never sent is not chased at all. Re-sending a pitch after it has been chased once starts a fresh wait. An empty days box means two.
Your own visits do not count. Opening the link to check the page while logged in as a Manager or above, from an IP on the Analytics exclusion list, or from a browser carrying the tracking opt-out cookie, records nothing — so the first view stays the prospect's.
A pitch view deliberately does not reset the contact's going-cold clock. That clock measures your silence; their reading something is the opposite of it.
Limitations
- A page load is not proof the video was watched. It means the page opened. Only the hosted option reports play counts and watch time. A slideshow records the furthest slide reached, which says they moved through it, not that they read each slide.
- Anyone holding the link can open it, and a forwarded link reads as the business viewing. The page is unlisted and never indexed — it is not in your nav, your sitemap or the page list — but the link is the only thing protecting it.
- Automated fetches are filtered out, because mail scanners and link previewers open the URL before any human does. Without that filter every prospect would look like they had watched.
- It is one template, not a page per business. Nothing is generated per prospect. Edits to the template reach every business's page at once; there is no changing one business's page alone beyond its headline, note and video.
- Most of the template's page settings do not apply. Its header and footer do — pick a header without your site's navigation there and every pitch page uses it. Everything else is per business or fixed: who can open a page is the pitch's own publish state (a Draft status, a redirect or a login requirement on the template changes nothing), and every pitch page is titled with the business's name and never indexed.
What it costs
Every search is a billed Google request — including each section of a sweep, the one lookup that resolves the area name to a box, and each Add a business lookup.
Google gives the first 1,000 searches of this kind away free every month, and that allowance is larger than it sounds: a suburb sweeps in single-digit searches and a city in tens. A normal month of prospecting therefore lands inside the free allowance and bills nothing at all. The rank-grid probes in SEO Scanner don't eat into it either — those use a cheaper tier that is free without limit.
The search budget on a sweep is a hard ceiling on what it will spend past that: the sweep pauses when it is reached and never goes beyond it. The panel shows searches used against the budget and a running cost estimate.
Treat the money figure as an estimate only, and one that only applies once you are past the free allowance. It is the list price of Google's Text Search Enterprise tier — $35 per 1,000 — which is what asking for rating, review count, website and phone puts a search into. Google reprices, and the rate steps down at higher volumes; the authority on what you are actually paying is the Google Cloud billing console.
A sense of scale: a rural county might sweep in a dozen searches. A large state is hundreds, because the metros subdivide repeatedly. Start with a budget you are comfortable with — the sweep pausing costs you nothing, and raising it resumes without re-searching a single section.
Is a complete list even the goal?
Usually not, and it is worth being honest about. If each prospect gets a personal follow-up, the realistic rate is a handful a day — so a complete list for a large state is years of outreach. The value of sweeping an area is being able to rank it and take the best few hundred, not completeness for its own sake. Sweep wide enough to rank well, then filter hard.
Google's terms
Google permits the place ID to be stored indefinitely; the descriptive fields are a refreshable snapshot and the schema treats them that way (refreshed_at on every prospect). Anything you intend to keep long term should be re-derived from the business's own website. Worth your own read of Google's Places API terms before running at volume.
Limitations
- Google never returns an email address. Plenty of small businesses hide theirs from scrapers and expect the contact form instead — which is a channel, not a disqualification, and arguably the better-read one, since it lands in the inbox they watch for work. Where an email is worth tracking down, the pencil on any un-kept row opens a drawer for it — and for the trade, which that drawer is the only place to edit. Once a business is kept, its CRM record is what you edit instead — all but who runs it, which Find owner still edits here and carries across.
- 60 results per search is Google's limit, not ours, and Google's own documentation warns it may change. Sweeps detect a full section by that count, by Google still offering another page, and by the search reaching Google's last page, so the engine keeps working if the number moves. Sweeps run before 1.8.9 judged by the first two only and could mark a busy section finished;
php artisan prospecting:recheck-full-areasre-opens those sections (--dry-runto see how many first) and the background schedule searches them against the sweep's own budget. - Google's answers vary slightly between searches. The same section searched twice a few minutes apart can come back with a different number of results, so a sweep is a very good list, not a census.
- The rectangle is the place's bounding box, not its border. A state sweep also covers the edges of its neighbours — an Arkansas sweep picks up Memphis and Texarkana, Texas. Each business's address still shows which state it is in, but the list has no state filter yet.
- Sweeps have a detail floor. Sections still full at the finest level are reported by name rather than subdivided for ever.
- An area is swept for one category at a time. "Electrician" and "plumber" over Texas are two sweeps and two bills.
- A sweep of an area already being swept for the same category is refused — it would cost twice and find the same businesses.
How fast the checks catch up
Checking runs in the background, a few businesses at a time, and how many it gets through each pass depends on who is waiting:
| How the pass was started | Each pass gets through |
|---|---|
A real cron (* * * * * php artisan lazy-cron:run), a queue worker, or the command typed by hand |
as many as fit in two minutes — in practice 40–90 businesses |
| A visitor's page load, on a site with no cron | 10 first checks + 25 catch-ups, and no longer than that |
The difference is not a setting, and it is not "does this install have a cron" — an install can have both, and then each pass is judged on its own. What is being protected is a visitor's page load: a pass started by someone browsing the site holds a PHP worker open for as long as it runs, so it stays small. A pass started by a cron holds nobody up, so it uses the time.
A list of 350 businesses therefore catches up in 15–20 minutes on a site with a cron, against about an hour without one. To watch it: php artisan prospecting:audit prints how many it did and how many are still waiting.
Reference
| Dashboard | Prospecting (Manager and up — the same tier as the CRM; Google spend is bounded by the Super-only monthly search cap, not by who can open the page) |
| Feature key | prospecting (requires crm) |
| API key setting | seo_scanner.places_api_key, shared with SEO Scanner |
| Background tasks | prospecting:sweep (every 2 min, the fallback lane), prospecting:audit (every 5 min); task types prospect-sweep, prospect-audit, prospect-site-audit, prospect-playbook and prospect-owner-search (each of the last three one per business, keyed {type}:{id}), and prospect-owner-search-batch (Find owners on this page — one at a time on the install, starting one prospect-owner-search after another) on the task runner |
| Commands | php artisan prospecting:sweep, php artisan prospecting:audit --limit=N --backfill=N --max-seconds=N, php artisan prospecting:recheck-full-areas --sweep=N --dry-run, php artisan tasks:run --id=N |
| Spend guards | prospecting.monthly_search_cap (default 10,000 searches/month, Super only) |
| Tables | prospects, prospect_searches, prospect_search_hits, prospect_audits, prospect_sweeps, prospect_coverage_cells, prospect_site_audits (one per business: pages read and listed, platform, per-page summaries, findings), prospect_playbooks (the AI read, one per business: the website it read, each line with its sources, the photos looked at, how many could not be, the pages read, whether the AI could see, the model), prospect_owner_searches (the owner web search, one per business: the website it searched for, the people with their sources and profiles, the addresses found or guessed, the pages read, the number of searches, the model, when) |
| Playbook | dashboard/prospecting/{id}/playbook (same access as the Prospecting page); settings prospecting.playbook.* |
| Pitch settings | prospecting.pitch_offer (blank = no price), prospecting.pitch_cta_url (blank = /book), prospecting.pitch_plans_url (blank = no See plans button), prospecting.pitch_contact_url (blank = no Email us link), prospecting.pitch_signup_url (older page copies only; blank = no sign-up button) |