Local Business AI Visibility: What 'Near Me' Answers Need

Local business AI visibility depends on facts engines can read. Of 130 local sites, 29% had LocalBusiness markup and 18% stated hours in it. What to fix first.

· BugViso Team

12 min read

Local business AI visibility comes down to whether an AI engine can confirm four facts about you: what you do, where you are, when you're open and what it costs. Those facts need to be on your website as plain text and structured data, and identical on your listings. Most local sites leave gaps. Of 130 local business websites we checked, 29% had LocalBusiness markup, 18% stated opening hours in it, and 28% showed any price on the homepage.

"Near me" questions are filtered questions: open now, in this area, under this price. By the end of this post you'll know what local AI answers draw on, how local sites measured up, what to fix first, and how to check a site in seconds with a script.

Data used: 200 local businesses with a website listed in OpenStreetMap, sampled at random (seed 20261029) from London, Manchester, Chicago, Austin and Sydney: restaurants, cafés, bakeries, hairdressers, beauty salons, dentists, vets, car repair shops, florists and opticians. Each homepage was fetched as plain HTML on 2026-10-29; 130 returned a readable page. Businesses aren't named. Chain branches were excluded and studied separately in multi-location AI search.

What Local AI Answers Draw On

A local answer combines the user's location, map and listing data, and the pages a search returns about each business. The engines expose parts of this in their APIs: OpenAI's web search tool and Anthropic's take an approximate user location, so "near me" can be resolved to a city before searching.

Then the engine needs facts per business, and it reads them from three places:

  1. Listings. Google Business Profile is the obvious one. Bing Places and Apple Business Connect feed other maps and assistants. Their hours, categories and reviews are structured and current, if you keep them so.
  2. Your website. Pages that state services, prices, hours and area in plain text, plus LocalBusiness structured data with the same name, address and phone.
  3. Third-party pages. "Best [service] in [city]" lists, review sites and local press. List pages carry a lot of weight in AI shortlists, as our listicle data shows.

When these agree, an engine can answer confidently. When your site says one thing and your listing another, the engine either picks one or leaves you out.

How 130 Local Websites Measured Up

Here's what the readable homepages stated, split into food and drink (restaurants, cafés, bakeries) and services (hairdressers, salons, dentists, vets, car repair, florists, opticians):

On the homepageAll (130)Food & drink (87)Services (43)
LocalBusiness markup29%30%28%
— with address28%29%26%
— with phone18%15%23%
— with opening hours18%20%16%
— with geo coordinates7%8%5%
— with price range8%8%9%
Phone number as text64%62%67%
Postcode or ZIP as text69%67%74%
Opening hours as text54%57%47%
Any price as text28%28%28%
Prices or menu page linked21%25%12%
Link to a Google Maps listing30%29%33%
Thin page (under 500 characters of text)12%11%12%

Another 70 of the 200 sampled businesses didn't return a readable homepage at all: the listed domain no longer resolved, returned an error, or showed a bot challenge. A listing that points to a dead website sends both buyers and engines nowhere.

Three things stand out:

  • Most local sites state their facts as text but not as data. 64% showed a phone number and 54% their hours, but only 18% put either in structured data.
  • Prices are the biggest gap. Only 28% showed any price on the homepage and 21% linked to a prices or menu page. "Cheap haircut near me" or "dentist check-up cost" can't be matched to a business that never states a price.
  • Social profiles beat map profiles. 52% linked to Facebook and 59% to Instagram, but only 30% linked to their Google Maps listing, and 5% to Yelp.

Blocking AI crawlers wasn't the problem: 5 of 130 sites blocked GPTBot, ClaudeBot and Google-Extended in robots.txt, and none blocked OAI-SearchBot or PerplexityBot.

💡 Rule of thumb: for every question a customer asks on the phone (are you open Sunday, how much is X, do you cover my area), the answer should be on your website as text and on your listing in the matching field.

The Facts a 'Near Me' Answer Filters On

Four facts decide whether a local business qualifies for a filtered question. Each needs a plain-text home and a structured one:

FactQuestion it answersOn the pageIn LocalBusiness markupOn your listings
Category and services"emergency plumber", "gluten-free bakery"A services or menu page@type subtype, e.g. DentistPrimary and secondary categories
Location and area"near me", "in Hackney"Address and areas servedaddress, geo, areaServedAddress, service area
Hours"open now", "open Sunday"Hours block with holidaysopeningHoursSpecificationRegular and special hours
Price"cheap", "under £30"Prices or menu pagepriceRange, or Offers for servicesPrice range, menu or services list

The markup isn't magic. Google's documentation describes it as a way to tell Google about business details; it doesn't promise rankings. What it does is state each fact once, unambiguously, in a form any parser can read. That matters when your site has hours in an image or prices in a PDF.

Fix a Local Site in This Order

Start with the facts every question needs, then the ones that win filtered questions.

  1. Make the listed website work. Check the URL on your Google, Bing and Apple listings loads your homepage. In our sample, 35% of listed websites didn't return a readable page.
  2. Put name, address, phone and hours in text on every page, usually the footer, and make them match your listings exactly.
  3. Add LocalBusiness markup with the most specific type, address, phone, hours (including special hours) and sameAs links to your profiles. Our LocalBusiness schema guide has copy-ready examples.
  4. Publish prices. A prices or menu page in HTML, not a PDF or an image, with real numbers or honest "from" prices.
  5. Write a page per main service, with the area you cover. "Boiler repair in North London" is a page an engine can cite for that question.
  6. Link your map listing from your site and your site from your map listing, and reply to reviews on the platforms engines cite for your category.

None of this is new SEO advice. What's new is how literal AI answers are: an engine assembling "open now, under £30, near me" checks each condition, and a missing fact is a failed condition.

Check a Local Site Yourself

This script fetches a homepage and checks for LocalBusiness markup and its key fields, phone, hours and prices as plain text, links to a Google Maps listing and review or social profiles, a prices or menu page, and AI crawler rules. Standard library only.

python
#!/usr/bin/env python3
"""Can an AI engine answer "near me" questions about this business from its website?
Checks LocalBusiness markup and its fields, whether name/address/phone, hours and prices are
plain text, links to map and review profiles, a prices/menu page, and AI crawler rules.

Usage: python3 local_ai_check.py https://example-dental.co.uk
"""
import json, re, sys, urllib.request, urllib.robotparser
from html import unescape
from urllib.parse import urljoin, urlsplit

UA = "Mozilla/5.0 (compatible; local-ai-check/1.0)"
LOCAL = re.compile(r"LocalBusiness|Dentist|Restaurant|CafeOrCoffeeShop|Bakery|Plumber|Electrician|HairSalon|BeautySalon|"
                   r"AutoRepair|VeterinaryCare|Florist|Optician|Store|MedicalClinic|HomeAndConstructionBusiness|FoodEstablishment")
CHECKS = {"address": r'"address"', "telephone": r'"telephone"', "opening hours": r'"openingHours',
          "geo coordinates": r'"geo"', "sameAs profiles": r'"sameAs"', "price range": r'"priceRange"', "rating": r'"aggregateRating"'}

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 nodes(html):
    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 re.findall(r"<script[^>]+application/ld\+json[^>]*>(.*?)</script>", html, re.I | re.S):
        try:
            walk(json.loads(b))
        except ValueError:
            pass
    return out

def main(url):
    final, html = get(url)
    text = re.sub(r"\s+", " ", unescape(re.sub(r"(?s)<[^>]+>", " ", re.sub(r"(?is)<(script|style|svg)[^>]*>.*?</\1>", " ", html))))
    local = [n for n in nodes(html) if LOCAL.search(json.dumps(n.get("@type", "")))]
    blob = json.dumps(local)
    mark = lambda ok: "✓" if ok else "✗"
    print(f"{mark(local)} LocalBusiness markup ({', '.join(sorted({str(n.get('@type')) for n in local})) or 'none'})")
    for label, pat in CHECKS.items():
        print(f"   {mark(re.search(pat, blob))} {label}")
    phone = re.search(r"\+?\d[\d ().-]{8,}\d", text)
    hours = re.search(r"(?i)\b(mon|tue|wed|thu|fri|sat|sun)[a-z]*\b[^.]{0,40}?\d{1,2}(:\d{2})?\s?(am|pm)?", text)
    prices = re.search(r"[$£€]\s?\d", text)
    print("As plain text on the page:")
    print(f"   {mark(phone)} phone number\n   {mark(hours)} opening hours\n   {mark(prices)} prices")
    hrefs = re.findall(r"""<a\b[^>]*href=["']([^"'#]+)""", html, re.I)
    maps = any(re.search(r"google\.[a-z.]+/maps|maps\.app\.goo\.gl|g\.page|goo\.gl/maps", h) for h in hrefs)
    reviews = sorted({m.group(1) for h in hrefs if (m := re.search(r"(yelp|tripadvisor|trustpilot|facebook)\.", h))})
    print(f"{mark(maps)} link to a Google Maps / Business Profile listing")
    print(f"{mark(reviews)} links to review or social profiles: {', '.join(reviews) or 'none'}")
    internal = {urljoin(final, h).split("?")[0] for h in hrefs if urlsplit(urljoin(final, h)).netloc == urlsplit(final).netloc}
    pricepage = sorted(u for u in internal if re.search(r"/(prices?|pricing|price-list|fees|menu|rates)(/|$|\.)", u.lower()))
    print(f"{mark(pricepage)} prices or menu page: {pricepage[0] if pricepage else 'not linked from the homepage'}")
    origin = f"{urlsplit(final).scheme}://{urlsplit(final).netloc}"
    rp = urllib.robotparser.RobotFileParser(origin + "/robots.txt"); rp.read()
    blocked = [b for b in ("OAI-SearchBot", "GPTBot", "PerplexityBot", "ClaudeBot", "Google-Extended") if not rp.can_fetch(b, final)]
    print(f"{mark(not blocked)} AI crawlers allowed" + (f" (blocked: {', '.join(blocked)})" if blocked else ""))

if __name__ == "__main__":
    main(sys.argv[1]) if len(sys.argv) == 2 else sys.exit(__doc__)

On a restaurant from the sample:

text
✗ LocalBusiness markup (none)
   ✗ address
   ✗ telephone
   ✗ opening hours
   ✗ geo coordinates
   ✗ sameAs profiles
   ✗ price range
   ✗ rating
As plain text on the page:
   ✓ phone number
   ✓ opening hours
   ✗ prices
✓ link to a Google Maps / Business Profile listing
✓ links to review or social profiles: facebook
✓ prices or menu page: https://<restaurant>/menu/
✓ AI crawlers allowed

That's the typical profile from our sample: the facts are on the page as text, the structured version is missing, and prices live one click away. The text checks are heuristics, so read the page when a result surprises you.

How BugViso Plans Local Business AI Visibility

BugViso's AI Visibility Tracker recognises a local business from its site (one or two named places; three or more make it a multi-location brand) and builds the "what to do" plan for that type. The local plan's listings item covers Google Business Profile, Bing Places, Apple Business Connect, Yelp and Facebook, review volume and replies, and trade or city directories. It points to LocalBusiness markup with the same name, address and phone as the listings, and to pages for your area, hours, prices and services.

Tracking runs in the market you choose, with buyer questions drafted from your site, so "near me" style questions are asked for your city where that fits the business. The plan's comparison item targets the "best [service] in [city]" lists and the review sites that answers for your questions actually cite. The local plan is part of the AI visibility tracker.

Local AI Visibility Mistakes

Hours in an image or a PDF. Engines and parsers read text. A photo of your opening times on the door is not your opening times.

A listing that points to an old website. 35% of listed websites in our sample didn't load a readable page. Check your listing's URL after any rebuild or domain change.

Different hours on different platforms. Update holiday hours everywhere at once, from one list.

"Call for prices". It's honest for complex jobs, but it guarantees you're filtered out of price questions. Publish "from" prices where you can.

Social profiles instead of a website. A Facebook page is a listing, not a website. Engines need a crawlable page to cite.

Frequently Asked Questions

Make your core facts readable and consistent: services, address, area, hours and prices as plain text on your site, the same facts in LocalBusiness markup, and identical details on Google Business Profile, Bing Places and Apple Business Connect. Then earn reviews and mentions on the local lists engines cite for your category.

It states your business facts in a form any system can parse, which helps when the same facts are hard to extract from the page. It isn't a ranking switch. In our sample only 29% of local sites had it, and 18% included hours in it.

Why doesn't AI mention my business for "near me" questions?

Usually a missing fact: no prices, hours only in an image, no page for the service asked about, or a listing with a broken website link. Check what your site states as text, then check that your listings match it.

Do AI engines use Google Business Profile?

Google's own AI features draw on its business data, and other engines read map and listing data through their search sources. Keep your profile complete and consistent with your website either way, since that's what any engine will cross-check.

Should local businesses publish prices online?

Yes, where possible. Only 28% of local homepages in our sample showed any price. Questions like "affordable" or "under £30" can't be matched to a business that never states one.

Answer the Phone Questions on the Page

The facts customers ask about on the phone (open when, how much, which area) are the facts local AI answers filter on, so put each one on your site as text and markup and keep your listings identical. BugViso's AI visibility tracking builds a local plan from the questions where rivals were named instead of you.

Written by

· 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.

Found this useful? Share it.

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.