Multi-Location AI Search: Consistent Facts for Every Branch
Multi-location AI search needs one readable page per branch. Across 90 chains, 87% of listings linked a location page, but only 55% of pages had local markup.
Kazi Khalid Hasan Tamim · BugViso Team
12 min read
In AI search, a multi-location brand is judged branch by branch: each location needs its own readable page with its own address, phone and hours, linked from its listings and tied to the parent brand. Most chains get the linking right and the pages half right. Across 90 chains, 87% of branch listings pointed to a location page. But of the location pages we could read, only 55% had local structured data, 30% linked to the parent brand, and at 1 in 5 chains two branch pages shared over 80% of their text.
A location question ("nearest X open late") is answered from one branch's facts. By the end of this post you'll know where chain location pages fall short, the three failures that hide a branch from answers, a page standard that fixes them, and a script that compares two location pages side by side.
Data used: 205 brands with three or more branches carrying a website in OpenStreetMap (London, Manchester, Chicago, Austin and Sydney, extracted 2026-10-29). We sampled 90 brands at random (seed
20261029), covering 1,054 branch listings, and fetched up to three location pages per brand as plain HTML: 223 pages. Brands aren't named. OpenStreetMap data © OpenStreetMap contributors, ODbL.
How Multi-Location AI Search Answers Are Built
An engine resolves the user's area, finds branches near it, and reads each branch's facts from listings and from the branch's own page. Search-enabled APIs take an approximate user location: OpenAI's web search tool and Anthropic's both accept one. From there, the answer needs per-branch facts: which branch, at what address, open when, offering what.
For a chain, the brand homepage can't supply those. It describes the company. The branch page, and the listing that points to it, carries the facts a location answer filters on. That's why per-location pages matter more in AI answers than in classic search, where a store locator and a map pack did much of the work.
Where Branch Listings Point
Most chains already link each branch listing to a location-specific page. Across 1,054 branch listings in our 90-chain sample:
| Measure | Result |
|---|---|
| Branch listings linking to a location page (not the homepage) | 919 of 1,054 (87%) |
| Chains where every branch links to a location page | 32 of 90 (36%) |
| Chains where every branch links to the homepage | 6 of 90 (7%) |
The six chains that send every branch to the homepage give engines no branch page to read. The rest have the structure; the question is what's on the pages.
What Location Pages Actually State
We fetched 223 location pages. Only 150 returned a page, and 148 of those were real location pages (two redirected to the homepage):
| Fetch outcome | Pages |
|---|---|
| Readable location page | 148 |
| Refused our plain HTTP client (403) | 50 |
| Dead link (404) | 11 |
| Timeouts, rate limits, other errors | 12 |
| Redirected to the homepage | 2 |
On the 148 readable pages:
| Fact | In structured data | As plain text |
|---|---|---|
| Local business markup with an address | 81 (55%) | — |
| Phone | 70 (47%) | 107 (72%) |
| Opening hours | 74 (50%) | 100 (68%) |
| Postcode or ZIP | — | 121 (82%) |
| Geo coordinates | 53 (36%) | — |
Link to the parent brand (parentOrganization or branchOf) | 44 (30%) | — |
| Rating | 14 (9%) | — |
Chains do better than independent local businesses on markup (55% against 29% in our local business study), because one template serves every branch. But the same template that adds markup everywhere can also leave it out everywhere.
Three Failures That Hide a Branch
A branch drops out of location answers when its page is unreadable, interchangeable or disconnected from the brand.
Unreadable: blocked or dead pages
Fifty of 223 location pages (22%) refused a plain HTTP client with a 403, and at 17 chains every location page we tried did. That's usually bot protection. AI crawlers send different user agents and some are allowlisted, so a 403 for us doesn't prove a 403 for them. But it's a warning to test: if your bot protection challenges unknown clients on location pages, check the live response for OAI-SearchBot, PerplexityBot and Googlebot, not just your robots.txt.
Eleven pages (8 chains) were dead links: the listing pointed to a location URL that no longer exists, usually after a site migration. Every listing that carries a dead URL is a branch an engine can't verify.
Interchangeable: template pages
We compared two location pages from each of the 49 chains where two were readable. At 10 chains (20%), the pages shared 80% or more of their text (five-word shingles), and the median overlap was 46%. A page that's 86% identical to its sibling, with only the address swapped, gives an engine nothing to choose that branch for: no local services, staff, parking, accessibility or neighbourhood details.
Disconnected: no link to the brand
Only 30% of location pages declared their parent brand in markup. Without parentOrganization or branchOf, a branch page is a business with a similar name. With it, the engine can connect the branch to the brand's reviews, reputation and other branches. The parentOrganization property exists for exactly this.
💡 Rule of thumb: a location page should pass the "screenshot test": if you hid the logo, could someone tell which branch it is and why they'd pick it over the next branch? If not, neither can an engine.
The Location Page Standard
One page per branch, reachable, specific and connected. A checklist your template should meet:
| Element | Requirement |
|---|---|
| URL | One stable URL per branch, linked from that branch's listings and from a crawlable store locator (plain links, not only a map widget) |
| Name, address, phone | As text and in local business markup, identical to the branch's Google, Bing and Apple listings |
| Hours | Regular and special hours, as text and openingHoursSpecification, updated from the same source as the listings |
| Brand link | parentOrganization (or branchOf) pointing to the brand's Organization |
| Branch-specific content | Services at this branch, staff or manager, parking, accessibility, nearby landmarks, local offers |
| Reviews | The branch's own rating and reviews where you have them, not the chain average |
| Access | Returns the page to crawlers you want in answers; no challenge pages on location URLs |
Google's LocalBusiness structured data documentation covers the fields; our multi-location LocalBusiness schema guide shows the per-branch JSON-LD with the parent link.
Keep one source of truth for branch facts (a locations database or feed) and generate the pages, the markup and the listing updates from it. Facts that drift between platforms are where AI answers go wrong, as our AI brand accuracy audit found for prices.
Make the Store Locator Crawlable
A store locator built only as a JavaScript map hides every branch link from crawlers that don't run scripts. Many locators load branches from an API after the page renders, so the raw HTML contains a search box and nothing else. Engines that read raw HTML, and plenty of crawlers do, see no branches at all.
The fix is cheap and old-fashioned:
- A plain HTML list of every location, grouped by region or city, each a normal link to the branch page. Keep the interactive map on top if you like.
- One clean URL per branch, such as
/locations/leeds-briggate, not/stores?id=4821. Parameter URLs get dropped, merged or ignored. - Every branch URL in the XML sitemap, with a real
lastmodthat changes when that branch's hours or services change. - Region pages that link down to branches, so a branch is never more than two clicks from the homepage.
Compare Two Location Pages
This script fetches two location pages of the same brand and shows, side by side, whether each has local markup with address, phone and hours, a parent-brand link, and phone and hours in text. It then measures how much text the two pages share. Standard library only.
#!/usr/bin/env python3
"""Compare two location pages of a multi-location brand: does each state its own name, address,
phone and hours in markup and text, link to its parent brand, and how much of the text is shared?
Usage: python3 location_pages_check.py https://brand.example/locations/leeds https://brand.example/locations/york
"""
import json, re, sys, urllib.request
from html import unescape
UA = "Mozilla/5.0 (compatible; location-pages-check/1.0)"
def get(url):
req = urllib.request.Request(url, headers={"User-Agent": UA})
with urllib.request.urlopen(req, timeout=20) as r:
return r.geturl(), r.read(2_500_000).decode("utf-8", "replace")
def text(html):
return re.sub(r"\s+", " ", unescape(re.sub(r"(?s)<[^>]+>", " ", re.sub(r"(?is)<(script|style|svg)[^>]*>.*?</\1>", " ", html))))
def shingles(t, k=5):
w = re.findall(r"\w+", t.lower())
return {" ".join(w[i:i + k]) for i in range(max(0, len(w) - k + 1))}
NOT_LOCAL = {"Organization", "Corporation", "WebSite", "WebPage", "PostalAddress", "Person", "BreadcrumbList"}
def nodes(blocks):
out = []
def walk(x):
if isinstance(x, dict):
out.append(x); [walk(v) for v in x.values()]
elif isinstance(x, list):
[walk(v) for v in x]
for b in blocks:
try:
walk(json.loads(b))
except ValueError:
pass
return out
def check(url):
final, html = get(url)
blocks = re.findall(r"<script[^>]+application/ld\+json[^>]*>(.*?)</script>", html, re.I | re.S)
ld, t = " ".join(blocks), text(html)
types = lambda n: {n["@type"]} if isinstance(n.get("@type"), str) else set(n.get("@type") or [])
local = any("address" in n and types(n) and not types(n) <= NOT_LOCAL for n in nodes(blocks))
rows = {"local markup": local, "address in markup": '"address"' in ld, "phone in markup": '"telephone"' in ld,
"hours in markup": '"openingHours' in ld, "parent brand link": bool(re.search(r'"(parentOrganization|branchOf)"', ld)),
"phone in text": bool(re.search(r"\+?\d[\d ().-]{8,}\d", t)),
"hours in text": bool(re.search(r"(?i)\b(mon|tue|wed|thu|fri|sat|sun)[a-z]*\b[^.]{0,40}?\d{1,2}(:\d{2})?", t))}
return final, rows, shingles(t)
def main(a, b):
(fa, ra, sa), (fb, rb, sb) = check(a), check(b)
print(f"{'check':<20}{'page A':>8}{'page B':>8}")
for k in ra:
print(f"{k:<20}{'✓' if ra[k] else '✗':>8}{'✓' if rb[k] else '✗':>8}")
j = len(sa & sb) / max(1, len(sa | sb))
print(f"\nshared text (5-word shingles): {100 * j:.0f}% -> "
+ ("near-duplicate template: add location-specific content" if j >= 0.8 else "pages are distinct enough"))
if __name__ == "__main__":
main(*sys.argv[1:3]) if len(sys.argv) == 3 else sys.exit(__doc__)On two branch pages of one chain from the sample:
check page A page B
local markup ✓ ✓
address in markup ✓ ✓
phone in markup ✓ ✓
hours in markup ✓ ✓
parent brand link ✗ ✗
phone in text ✓ ✓
hours in text ✓ ✓
shared text (5-word shingles): 86% -> near-duplicate template: add location-specific contentA good template with two gaps: no link to the parent brand, and pages that differ only in their address block. The 80% threshold is a judgement call; anything above it means the branch-specific content is a small fraction of the page.
How BugViso Plans Multi-Location AI Visibility
BugViso's AI Visibility Tracker treats a brand that names three or more places on its site as multi-location and builds its "what to do" plan for that type. The plan covers listings for every location (Google Business Profile, Bing Places, Apple Business Connect, Yelp, Facebook) and location pages: one non-template page per location, linked to its profile, with LocalBusiness markup that names the parent organisation, identical name, address and phone everywhere, and a crawlable store locator. Review and comparison items target each market's Google and Yelp reviews and its "best [service] in [city]" lists.
Each brand is tracked in one market, so a chain operating in several countries tracks its main market first. The brand brief's key facts, such as branch count, regions served or delivery areas, feed the accuracy check on what engines say. The multi-location plan is part of the AI visibility tracker.
Multi-Location Mistakes
One page for all branches. A store locator map with no per-branch URLs gives engines nothing to cite for a specific location.
Bot protection on location pages. Test the live response for the crawlers you want; a challenge page is an empty page to them.
Listings pointing to old URLs after a migration. Redirect every old location URL to its new page, then update the listings.
Chain-wide reviews on every branch. Show each branch's own rating where you have it; a chain average on every page is another identical block.
Holiday hours updated on the website only. Update listings and pages together, from one source.
Frequently Asked Questions
How do multi-location businesses show up in AI search?
Branch by branch. An engine answering a location question needs that branch's address, hours and services, so each location needs its own readable page with those facts in text and markup, linked from its listings and tied to the parent brand.
Do I need a separate page for every location?
Yes. In our sample, 87% of chain branch listings already linked to a location page. The six chains that sent every branch to the homepage gave engines no branch-level page to read or cite.
How do I avoid duplicate content on location pages?
Add content that's true only for that branch: services offered there, staff, parking, accessibility, local offers and the branch's own reviews. At 20% of chains we compared, two location pages shared over 80% of their text.
What schema should a location page use?
The most specific LocalBusiness type, with address, telephone, opening hours, geo coordinates and a parentOrganization link to the brand's Organization. Only 30% of the location pages we read declared the parent brand.
Why can't AI find some of our locations?
Check that the location URL loads for crawlers (50 of 223 pages in our sample returned 403 to a plain client), that listings don't point to dead URLs (11 did), and that the page states its own facts rather than a template.
Give Every Branch a Page That Answers for Itself
Location answers are built from one branch's facts, so every branch needs a reachable page with its own address, hours and services, linked to the brand and matched by its listings. BugViso's AI visibility tracking builds a multi-location plan from the questions where rivals' branches were named instead of yours.
Written by
Kazi Khalid Hasan Tamim · BugViso Team
Kazi writes BugViso’s guides on website audits, Core Web Vitals, technical SEO, accessibility and AI search. The data in them comes from audits BugViso runs in a real Chromium browser, including our 219-site study of website health.
Summarise with AI
Open this guide in your assistant.
Follow BugViso
New guides and audit data as we publish them.
See where your site stands
Run a free BugViso audit for SEO, speed, accessibility and AI search readiness — with fixes you can ship today.