Fixing LocalBusiness Schema Gaps
LocalBusiness schema is a small block of code that states, in a form machines can read without guessing, what kind of business you are, where you are, when you are open and which other profiles belong to you.
Most small-business sites either have none, or have a stub that a theme or plugin wrote years ago: a generic type, half an address, no hours and nothing tying it to the Business Profile. This guide takes you from that stub to a complete, accurate block that agrees with the visible page, with patterns for awkward opening hours and the usual platforms.
- What counts as a schema gap
- Choosing the right type
- A complete block for a single location
- Opening hours that parse, including the awkward ones
- Connecting the block to everything else: @id, url and sameAs
- Where the markup comes from on common platforms
- Validator messages and what they usually mean
- The fix, step by step
- Keeping it right after today
- Questions and answers
What counts as a schema gap
A gap is any difference between what your business really is and what the structured data says it is. The block might be missing altogether, or it might exist but be too vague to be useful. The Local SEO Checker reports LocalBusiness schema completeness, and the gaps it surfaces tend to fall into a handful of patterns.
| Gap | What you typically see in the code | Why it matters |
|---|---|---|
| No local markup at all | Only a WebSite or Organization block
from the theme. | Nothing on the site states an address or opening times in structured form. |
| Generic type | "@type": "LocalBusiness" for a dentist, garage or cafรฉ. | A specific type says what you do; the generic one only says you are local. |
| Partial address | A town and nothing else, or the whole address crammed into streetAddress. | Search engines cannot reliably match a partial or merged address to a map location. |
| No hours, or hours as prose | "openingHours": "Mon-Fri 9-5 ish" | Free text does not parse, so the hours are ignored. |
| No coordinates | No geo block, or coordinates rounded to two decimal places. | A rounded point can sit a kilometre away from the door. |
| Isolated entity | No @id, no sameAs, no url. | Nothing links this block to your Business Profile, social profiles or other pages. |
| Contradicts the page | Markup phone number differs from the one in the header. | Structured data that disagrees with visible content is treated with suspicion. |
Google's documentation for local business structured data lists name and address
as required, and recommends fields including telephone, url, geo,
openingHoursSpecification, priceRange and department. None of
this guarantees a special search feature. What the markup does is remove ambiguity about basic facts,
and ambiguity is exactly what local search punishes.
Choosing the right type
Schema.org has a long tree of types beneath LocalBusiness. Pick the deepest one that
is honestly true. If nothing fits exactly, go one level up rather than picking something close but wrong.
| Business | Good type | Notes |
|---|---|---|
| Dental practice | Dentist | Sits under both MedicalBusiness and LocalBusiness. |
| Plumber, electrician, roofer | Plumber, Electrician, RoofingContractor | All under HomeAndConstructionBusiness. |
| Garage or MOT centre | AutoRepair | Under AutomotiveBusiness. |
| Cafรฉ, restaurant, takeaway | CafeOrCoffeeShop, Restaurant, FastFoodRestaurant | Food
types also accept servesCuisine and menu. |
| Hair salon, barber | HairSalon | Under HealthAndBeautyBusiness. |
| Solicitor | LegalService | Schema.org also has Attorney,
which is US-flavoured; LegalService is the neutral choice. |
| Accountant | AccountingService | Under FinancialService. |
| Independent shop | Store or a subtype such as BookStore, HardwareStore | Many subtypes exist; check the Store page on schema.org. |
| Consultancy, agency | ProfessionalService | A catch-all for offices that clients visit. |
When two types are true
You can give an array: "@type": ["Restaurant", "BarOrPub"]. Only do this when both describe
the whole business. A pub that serves food qualifies; a hairdresser that sells a few bottles of shampoo
does not become a Store.
When LocalBusiness is the wrong type
If customers never meet you at a place, for example an online-only shop or a software company, Organization
is the honest choice. A home-based trade that travels to customers can still use a LocalBusiness subtype,
but how to handle the address and the areas covered is a website design question covered in the service-area
coverage guide.
A complete block for a single location
This is a full block for a fictional practice. Every value here should be copied from your canonical business details, the same ones shown on the page. The phone number uses an Ofcom drama number range, so it is safe as an example.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Dentist",
"@id": "https://example.co.uk/#practice",
"name": "Millbank Lane Dental",
"url": "https://example.co.uk/",
"telephone": "+44 114 496 0321",
"email": "hello@example.co.uk",
"image": "https://example.co.uk/images/practice-front.jpg",
"logo": "https://example.co.uk/images/logo.png",
"priceRange": "ยฃยฃ",
"address": {
"@type": "PostalAddress",
"streetAddress": "14 Millbank Lane",
"addressLocality": "Sheffield",
"addressRegion": "South Yorkshire",
"postalCode": "S10 2AB",
"addressCountry": "GB"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": 53.38115,
"longitude": -1.49872
},
"hasMap": "https://www.google.com/maps?cid=0000000000000000000",
"openingHoursSpecification": [
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday"],
"opens": "08:30",
"closes": "17:30"
},
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": "Friday",
"opens": "08:30",
"closes": "16:00"
},
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": "Saturday",
"opens": "09:00",
"closes": "12:30"
}
],
"sameAs": [
"https://www.facebook.com/example-dental",
"https://www.instagram.com/example_dental"
]
}
</script>
A few details in that block are easy to get wrong:
- Telephone is written in international format with the leading zero dropped. The
visible page can show
0114 496 0321; both describe the same number. - addressCountry is the two-letter code
GB, not "UK" and not "United Kingdom". GB is the ISO 3166 code used for the whole United Kingdom, Northern Ireland included. - addressRegion is optional in the UK. Include a county only if you show it on the page and it is part of how you write your address.
- Sunday is simply absent. Days not listed are read as closed; you do not need to spell that out for a normal week.
- hasMap should be the link to your own place in Google Maps. Replace the placeholder with the share link from your listing, or leave the field out.
Opening hours that parse, including the awkward ones
Hours are where most blocks fail quietly. The old openingHours text property, such as
"Mo-Fr 09:00-17:00", is valid schema.org, but it is easy to mistype and cannot express exceptions.
Use openingHoursSpecification with 24-hour times and full English day names.
| Situation | How to write it |
|---|---|
| Closed for lunch | Two specifications for the same day: one closing at 12:30,
one opening at 13:30. |
| Open past midnight | One specification, e.g. "dayOfWeek": "Saturday", "opens": "18:00", "closes": "02:00".
The closing time is read as the following morning. |
| Open 24 hours | "opens": "00:00", "closes": "23:59" for each day that applies. |
| Closed all day on a date | A dated specification with "opens": "00:00", "closes": "00:00". |
| Bank holiday or seasonal hours | A specification with validFrom and validThrough
set to the dates, alongside the normal weekly ones. |
A Christmas closure, for example, looks like this inside the array:
{
"@type": "OpeningHoursSpecification",
"opens": "00:00",
"closes": "00:00",
"validFrom": "2026-12-25",
"validThrough": "2026-12-26"
}
Connecting the block to everything else: @id, url and sameAs
A LocalBusiness block that stands alone describes a business. One that carries a stable identifier and points to your other profiles describes your business, and lets other blocks on your site refer back to it.
@id
Give the business a permanent identifier: your homepage address followed by a fragment, such as https://example.co.uk/#practice.
It is not a page a visitor opens; it is a name. Use exactly the same string everywhere the business is
mentioned in markup, for example as the provider of a Service block or the
publisher of an article. Change it once and every reference breaks.
url
The page that best represents this location. For a single-location business that is usually the homepage. For a branch it is the branch page, not the homepage.
sameAs
A list of profile addresses that are definitely this business: Facebook, Instagram, LinkedIn, a trade
body membership page, a Companies House entry if you want to point at it. Keep the list short and accurate.
A directory listing that shows an old address should be corrected before it goes into sameAs,
not after. Checking what those listings say is the job of the NAP
consistency guide, and this guide assumes that work is done.
geo
Take the coordinates from your pin in Google Maps: right-click the entrance and the first item in the menu is the latitude and longitude, which copies when clicked. Google recommends at least five decimal places. Put the point on the customer entrance, not the middle of the building or the car park behind it.
@id. Parsers treat nodes with the same identifier as one entity, which
is often the quickest way to stop two half-complete blocks competing.Where the markup comes from on common platforms
Before you add anything, find out what already writes structured data on your site. Duplicated or conflicting LocalBusiness blocks are a common outcome of adding a plugin on top of a theme that already had one.
WordPress
Yoast SEO outputs Organization or Person in its schema graph; LocalBusiness
comes from its paid Local SEO add-on. Rank Math has a Local SEO setting that switches the site entity
to a LocalBusiness type with address and hours, with multi-location support in its paid tier. Some themes
and page builders add their own block too. Pick one source, fill it in completely and switch off the
others. If you hand-code instead, add the JSON-LD through a small site-specific plugin or the theme's
header hook, not by pasting into a post.
Shopify
Most themes output Organization and WebSite on the homepage and Product
on product pages, but not LocalBusiness. A shop with a physical counter can add a snippet to theme.liquid,
guarded so it only renders on the homepage or a store page, or use a schema app. Check the rendered source
afterwards; some apps inject a second Organization.
Wix and Squarespace
Both builders fill in some business information automatically. Wix lets you add custom structured data to a page in its SEO settings. Squarespace supports code injection on plans that include it. In both cases, view the published page source first so you do not end up with a second, contradictory block.
Custom sites and frameworks
Generate the block from the same data that fills the visible contact details, and output it with a
JSON encoder: json_encode() in PHP, JSON.stringify() in JavaScript, json.dumps()
in Python. In Next.js or Nuxt, render it on the server so it is present in the initial HTML rather than
added after hydration.
Validator messages and what they usually mean
| Message or symptom | Likely cause | Fix |
|---|---|---|
| Missing field "address" | The address is plain text in address, or absent. | Use
a PostalAddress object with the separate parts. |
| Invalid value in "opens" or "closes" | 12-hour times, "9am", or times with seconds in an odd format. | Use HH:MM in 24-hour form, such as 17:30. |
| "dayOfWeek" not recognised | Abbreviations such as "Mon" or lowercase names in the specification. | Full
names like Monday, or the full URL form https://schema.org/Monday. |
| Two LocalBusiness items detected | Theme plus plugin, or plugin plus hand-coded block. | Keep
one; or give both the same @id and remove conflicting values. |
| Parsing error, whole block ignored | A trailing comma, a curly quote, or an apostrophe in the business name breaking a hand-built string. | Output through a JSON encoder; test with the Schema Markup Validator. |
| Stars do not appear for your business | aggregateRating about your own business
on your own site. | Expected. Google does not show review stars for self-serving LocalBusiness or Organization reviews. See below. |
About ratings in LocalBusiness markup
Since 2019 Google has not shown review rich results for reviews a business publishes about itself
on its own site when the item is a LocalBusiness or Organization. You can still include aggregateRating
if the page genuinely shows those reviews, but do not expect stars from it, and never mark up ratings
copied from Google, Trustpilot or another platform. The rules for review markup in general are covered
by the review schema guide.
The fix, step by step
Step 1
Find the markup you already have
Open
your homepage and contact page, view the source and search for application/ld+json, then
also search for itemtype= in case an older theme uses microdata. Paste the URL into the
Schema Markup Validator to list every item it detects. Write down which blocks exist, which tool or template
produces each one, and every value they state for name, address, phone and hours. You are looking for
duplicates and for values that differ from your real details.
Step 2
Choose the most specific true type
Look
up your trade in the schema.org type tree and pick the deepest type that honestly describes the whole
business, such as Dentist, Plumber or AutoRepair. Use an array
only when two types are both fully true. If nothing fits, step up one level, to ProfessionalService
or Store for example, rather than choosing a near miss. Replace the generic LocalBusiness
value in whichever single source you decided to keep.
Step 3
Complete the address and contact fields
Split the address into a PostalAddress with streetAddress, addressLocality,
postalCode and addressCountry set to GB, written exactly as your
canonical address. Add telephone in +44 format, url pointing to
the page for this location, and an image of the premises or team. Leave out any field you
would have to guess, since a missing value does less harm than a wrong one.
Step 4
Write opening hours in a form that parses
Replace free-text hours with an openingHoursSpecification array. Group days
that share the same times, use full day names and 24-hour HH:MM times, and add a second
entry for any day with a lunch break. For late closing, put the after-midnight time in closes
on the same entry. Add dated entries with validFrom and validThrough for bank
holidays and seasonal changes, and remove them once the dates pass.
Step 5
Add coordinates, @id and sameAs
Right-click
your entrance in Google Maps, copy the coordinates and add them as a GeoCoordinates object
with at least five decimal places. Give the business a permanent @id such as your homepage
address followed by #business, and reuse that exact string anywhere else your markup refers
to the business. Add a short sameAs list of profiles you control and have already checked
for correct details, leaving out any listing that still shows old information.
Step 6
Make the markup match the visible page
Read the page as a visitor would and compare it field by field with the block. The phone
number, address, hours and business name in the markup must all appear on the page in the same form,
allowing only for the +44 formatting of the number. If the page does not show hours, add
them to the page rather than keeping hours that live only in code. Remove any markup value that a visitor
cannot see or confirm.
Step 7
Validate, publish and re-check
Test the live URL in the Schema Markup Validator for full schema.org correctness and in Google's Rich Results Test to confirm Google can read it, fixing any parsing error before anything else. After publishing, use URL Inspection in Search Console to request indexing of the page. Run the Local SEO Checker again to confirm the completeness finding has cleared, and put a reminder in your calendar to review hours before each bank holiday.
Keeping it right after today
- One source of truth. The markup, the footer and the contact page should all read from the same settings or data file. If someone edits the phone number in one place, all three change.
- Re-test after redesigns and plugin changes. Theme switches are the most common way a working block disappears or gets duplicated.
- Diary the hours. Bank holidays, Christmas and summer closures are when markup and reality drift apart. Update the page, the markup and the Business Profile together.
- Keep sameAs honest. When you close a social account or leave a trade body, take the link out.
- More than one branch? Each branch needs its own block on its own page. That pattern is set out in the multi-location guide.
1. Does LocalBusiness schema improve my local rankings?
Google has not said that LocalBusiness markup is a ranking factor, and adding it will not lift a weak site on its own. What it does is state your type, address, hours and coordinates in an unambiguous form, which helps search engines connect your website to the right place and the right Business Profile. Think of it as removing a source of doubt rather than adding a boost. The businesses that benefit most are those whose details were previously unclear or inconsistent.
2. Which page should the LocalBusiness markup go on?
For a single-location business, put it on the homepage, and optionally on the contact page as well, using the same @id so both describe one entity. Do not add it to every page through a site-wide template unless your platform does so with identical values, because blog posts and product pages are not about the business location. For several branches, each branch page carries its own block for that branch only, rather than a list of every branch on the homepage.
3. Should I use JSON-LD or microdata?
Use JSON-LD. Google supports JSON-LD, microdata and RDFa, but recommends JSON-LD because it sits in one block that is easy to read, test and maintain without touching the visible HTML. Microdata spreads attributes through the page markup, so a template change can break it without anyone noticing. If an old theme outputs microdata and you add JSON-LD, remove the microdata version so you do not end up with two descriptions that slowly drift apart.
4. What schema type should I use if my trade is not listed?
Choose the nearest parent type that is still true rather than a specific type that is wrong. A dog groomer has no dedicated schema.org type, so LocalBusiness or ProfessionalService is more honest than PetStore unless you actually sell pet supplies. You can describe what you do in the description field and through Service blocks for each service. Check the schema.org type list occasionally, because new types are added from time to time.
5. How do I mark up bank holiday or Christmas opening hours?
Add extra OpeningHoursSpecification entries alongside your normal weekly ones, each with validFrom and validThrough set to the dates in question. For a day you are closed, set opens and closes both to 00:00. For shorter hours, give the actual times. Remove the dated entries after the period ends so the block stays tidy, and change the hours shown on the page and on your Business Profile at the same time, because each one is updated separately.
6. Can I include my Google reviews in aggregateRating?
No. Review markup should describe reviews that are collected and shown on your own site, not ratings copied from Google, Trustpilot or another platform. In addition, Google does not show review stars for reviews a local business publishes about itself on its own site, so even genuine first-party ratings will not produce stars for a LocalBusiness item. If you want to display third-party reviews, embed or link to them as visible content and leave them out of the markup.
7. Do I need both an Organization block and a LocalBusiness block?
Usually not for a single-location business, because LocalBusiness is itself a kind of Organization and can carry the logo, sameAs links and contact details. Multi-branch businesses often use one Organization for the brand and a LocalBusiness per branch, linked with parentOrganization. If a plugin already outputs an Organization and you add a LocalBusiness, give them the same @id or merge them, so search engines see one business rather than two similar ones.
The Local SEO Checker looks at LocalBusiness schema completeness alongside NAP consistency across major citation sources, Business Profile signals, review velocity, geo-content coverage and your position in the map pack.
Open Local SEO Fixes โ