Most local business sites have schema markup. Most of it is wrong, duplicated, or marking up types that stopped doing anything years ago.
Schema is not a ranking factor you turn up. It is a translation layer. You are handing search engines and AI assistants a machine readable version of facts that are already on your page, so they do not have to guess. Done right, it makes you easier to classify, easier to retrieve and easier to cite. Done wrong, it contradicts your page and makes you less trustworthy, which is worse than having none.
Here is what earns its keep, what to drop, and how to check your work.
Use JSON-LD, and put it in one place
Three formats exist. Use JSON-LD. Microdata and RDFa still parse, but JSON-LD is what the documentation is written for, it is separate from your markup so it survives a design change, and it is easier to audit.
Put it in a single script block, ideally in the head, and use an @graph array so all the entities on the page live in one structure and can reference each other. One block per page. Not one from your theme, another from a plugin and a third you pasted in manually. Conflicting blocks is the single most common problem we find.
The types that are worth your time
LocalBusiness, using the most specific subtype available. Do not default to the generic LocalBusiness or, worse, Organization, if a real subtype fits. Schema.org has Plumber, Electrician, RoofingContractor, HVACBusiness, Dentist, LegalService, Attorney, MedicalBusiness, AutoRepair, RealEstateAgent, HomeAndConstructionBusiness and many more. Specificity is the point. You are telling the engine what kind of thing you are, which is exactly the judgment it is trying to make when deciding whether to name you.
Give this entity a stable @id, something like https://yoursite.com/#organization, and reference that same @id from every other page. Without it, each page looks like a brand new business.
Service. One per real service, with provider pointing at your organization @id, plus serviceType and areaServed. This is how you connect "we do this work" to "we do it here" in a way a machine can follow. On a service page this is the most under-used markup there is.
FAQPage. Here is the honest state of this one. Google pulled back FAQ rich results years ago, so you should not expect the expandable questions in search results anymore unless you are a government or health authority site. FAQPage markup is still worth adding, because it explicitly pairs a question with its answer, which is precisely the structure retrieval systems and AI assistants want. You are marking it up for machine comprehension now, not for stars.
BreadcrumbList. Still fully supported, still shows in results, and it communicates your site hierarchy. Cheap to add, keeps working.
Article or BlogPosting on your blog posts, with a real author, a real datePublished and an honest dateModified. Point the author at a person with an actual bio page on your site. Anonymous content is a weak signal and getting weaker.
Review and AggregateRating, carefully. You can mark up real testimonials on your site. What you cannot do is self-serve an aggregate rating for your own business and expect stars in search results. Search engines discount ratings a business reports about itself. Your real review signal lives on your Google Business Profile and other third party platforms. Mark up genuine reviews because they are genuine content, not because you expect a rich result.
The types to skip
HowTo. Rich results for this were retired. If you have HowTo markup left over from 2021, it is doing nothing.
Sitelinks search box. Also retired. Remove it.
Speakable. Limited to approved news publishers in practice. Not for your plumbing site.
Product schema on a service page. You are not selling a SKU. Using Product to chase a price or rating snippet on a service is misuse and it gets flagged.
Event, unless you really hold events. Marking up a "spring special" as an Event is the kind of thing that costs you a manual action.
Anything describing content that is not on the page. This is the rule under all of the above. Schema must describe visible page content. Markup that invents facts is a structured data violation, not a clever trick.
The mistakes that quietly break everything
- Duplicate LocalBusiness blocks. Theme plus plugin plus manual. Pick one source of truth.
- No
@id, so every page mints a new entity. You end up with forty slightly different versions of your business instead of one confident one. - Markup that disagrees with the page. Hours in the schema that do not match the hours in the footer. A phone number nobody answers. Engines cross-check, and a mismatch is a confidence hit.
- Fake address for a service area business. If you do not have a location customers visit, do not invent one. Use
areaServedand follow the platform rules about hidden addresses. A bogus address is the fastest way to get a Google Business Profile suspended. - Placeholder values left in. We regularly find
YOUR_BUSINESS_NAMEand+1-555-555-5555live on real sites. Read what you paste. - Marking up every page as the homepage. A service page should describe the service, reference the organization, and not restate the whole business as if it were the front door.
- Stale
dateModified. Bumping it without changing anything is a pattern engines can see across a site. Update it when you actually update the page.
A working example
Here is a complete, valid JSON-LD block for a roofing contractor's service page. It declares the business once, the service once, a breadcrumb trail and a short FAQ, all in one graph.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "RoofingContractor",
"@id": "https://example-roofing.com/#organization",
"name": "Hill Country Roofing",
"url": "https://example-roofing.com/",
"telephone": "+1-210-555-0100",
"email": "office@example-roofing.com",
"image": "https://example-roofing.com/images/shop-front.jpg",
"foundingDate": "2011",
"address": {
"@type": "PostalAddress",
"streetAddress": "118 Main St, Suite B",
"addressLocality": "Boerne",
"addressRegion": "TX",
"postalCode": "78006",
"addressCountry": "US"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": 29.7947,
"longitude": -98.7320
},
"areaServed": [
{ "@type": "City", "name": "Boerne" },
{ "@type": "City", "name": "San Antonio" },
{ "@type": "City", "name": "Fair Oaks Ranch" }
],
"openingHoursSpecification": [
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday"],
"opens": "07:30",
"closes": "17:30"
}
],
"sameAs": [
"https://www.google.com/maps/place/?q=place_id:EXAMPLE_PLACE_ID",
"https://www.facebook.com/exampleroofing",
"https://www.yelp.com/biz/example-roofing-boerne"
]
},
{
"@type": "Service",
"@id": "https://example-roofing.com/roof-replacement/#service",
"name": "Residential roof replacement",
"serviceType": "Roof replacement",
"provider": { "@id": "https://example-roofing.com/#organization" },
"areaServed": [
{ "@type": "City", "name": "Boerne" },
{ "@type": "City", "name": "San Antonio" }
],
"description": "Full tear-off and replacement of residential asphalt shingle and standing seam metal roofs, including decking repair, permits and haul-off."
},
{
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://example-roofing.com/"
},
{
"@type": "ListItem",
"position": 2,
"name": "Services",
"item": "https://example-roofing.com/services/"
},
{
"@type": "ListItem",
"position": 3,
"name": "Roof replacement"
}
]
},
{
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "How long does a roof replacement take?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Most single story homes are torn off and re-roofed in one to two days. Add a day if we find damaged decking that has to be replaced before the new system goes on."
}
},
{
"@type": "Question",
"name": "Do I need a permit to replace my roof in Boerne?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes. A city permit is required for a residential re-roof. We pull it as part of the job and the fee is included in your written quote."
}
}
]
}
]
}
</script>
Swap in your real values. Every fact in that block should also be visible on the page.
How to validate
Do all four. They check different things.
- validator.schema.org. Checks whether your markup is valid against the vocabulary. This is your syntax and type check.
- Google Rich Results Test. Checks whether Google recognizes the types it supports and shows what it would render. Important caveat: it only reports on Google-supported types, so a valid type that does not appear there is not broken, it just has no rich result.
- Search Console enhancement reports. Shows errors across your whole site after crawling, which is the only way to catch a template-level problem on four hundred pages.
- Bing Webmaster Tools markup validator. Worth running because of what reads Bing.
Then do the manual check nobody does: load the page, read the schema, read the page, and confirm they say the same thing.
A short deployment checklist
- [ ] One JSON-LD block per page, using
@graph - [ ] Most specific LocalBusiness subtype that fits your trade
- [ ] Stable
@idfor the organization, referenced from every page - [ ]
sameAspointing at your real profiles - [ ] Service markup on each service page, with
providerandareaServed - [ ] BreadcrumbList on every page below the homepage
- [ ] Article markup with a real author on blog posts
- [ ] No HowTo, no search box, no Product on services, no self-serving aggregate rating
- [ ] Hours, phone and address identical to the visible page and your Google Business Profile
- [ ] Validated in all four tools, with the Search Console report clean
If you would rather not hand-edit JSON
Fair. It is finicky, and the cost of a silent error is that it quietly does nothing for a year.
Heights Digital builds and maintains this as part of ongoing SEO and AI search work for service businesses, which mostly means it gets audited every month instead of once at launch. If you want someone to look at what your site is currently declaring, call 210-729-4008. A schema audit is a fast conversation and you will know where you stand.
Related reading.
Find out what you are actually missing.
Send the site over. We will tell you what is broken, what you could rank for, and whether AI search knows you exist. No charge and no pitch deck.
- A real look at your site and your Search Console
- What your competitors are ranking for that you are not
- Whether ChatGPT and AI Overviews mention you
- Pricing in plain numbers, on the call
