Local Schema Markup: A Working Example You Can Check Yourself

Home Blog Local Schema Markup: A Working Example You Can Check Yourself
Structured data and local search results for a Birmingham business

Most of local SEO is visible. You can look at a competitor's site and see their service pages, read their city pages, count their reviews. Schema markup is the exception. It's a block of code in the page that no visitor ever sees, written for Google rather than for people, and because it's invisible it tends to be either ignored completely or bolted on badly.

I want to show you a real one. Not a generic template off a documentation page — the actual markup running on a client's site right now, which you can open and verify yourself while you read this.

What schema actually does (and doesn't)

Schema markup is structured data: a standardized vocabulary, published at schema.org, for stating facts about a page in a format a machine can parse without guessing. Your address is in your footer, and Google is usually smart enough to figure that out. Schema removes the "usually."

Here's the honest version of what it buys you. Adding schema does not make you rank. There's no switch. What it does is eliminate ambiguity — and ambiguity is expensive when a search engine is deciding whether your page is the right answer to "lawn care in Hoover." If your site makes Google infer which town a page is about, some percentage of the time it will infer wrong, or won't be confident enough to act. Schema is how you stop making it guess.

That's a narrower claim than most SEO writing makes about structured data, and it's the one I'm comfortable defending.

The example

Big Time Lawn & Home is a lawn care and landscaping company working across the Birmingham suburbs — eleven towns, nine services. I built the site, which means I can tell you exactly why every field is there, and you can open view-source:bigtimelawn.com and confirm I'm not making it up.

Here's the core of the homepage markup:

{
  "@type": "HomeAndConstructionBusiness",
  "@id": "https://www.bigtimelawn.com/#business",
  "name": "Big Time Lawn & Home LLC",
  "telephone": "+12059832268",
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "201 Applegate Trace, Suite A",
    "addressLocality": "Birmingham",
    "addressRegion": "AL",
    "postalCode": "35124",
    "addressCountry": "US"
  }
}

Three things in there are worth more than they look.

HomeAndConstructionBusiness, not LocalBusiness. Schema.org has a tree of business types beneath LocalBusiness, and most sites stop at the generic root. Using a more specific one costs nothing and says more.

The catch is that the tree is narrower than you'd expect. There's no type for landscapers specifically — the nearest real one is HomeAndConstructionBusiness, the branch that also covers Plumber, Electrician, RoofingContractor, HVACBusiness, HousePainter, Locksmith, MovingCompany and GeneralContractor. A lot of plausible-sounding types simply aren't in the vocabulary, and an invented one is worse than the generic: a parser that looks up LocalBusiness finds a definition and knows what it's dealing with, while one that looks up a type that doesn't exist gets a 404 and nothing to fall back on.

So verify before you ship. Open the LocalBusiness page, find the closest match in the list of more specific types, and confirm schema.org/YourType actually loads.

"@id" — the part almost nobody does. That #business identifier gives the business a permanent name that other parts of the markup can point at. The address, phone and name get declared once, on one page, and every other page just references the ID instead of repeating them. One source of truth, no drift. If the address changes, it changes in one place and the whole site follows.

addressRegion and postalCode as separate fields. Not "Birmingham, AL 35124" as a string. Broken out, they're unambiguous.

The part that matters most: one field, changed per page

If you take one idea from this post, take this one.

The homepage lists all eleven towns the business serves:

"areaServed": [
  { "@type": "City", "name": "Hoover",
    "containedInPlace": { "@type": "State", "name": "Alabama" } },
  { "@type": "City", "name": "Mountain Brook",
    "containedInPlace": { "@type": "State", "name": "Alabama" } }
  // ...nine more
]

But the Hoover page doesn't. It narrows to one:

{
  "@type": "Service",
  "name": "Lawn Care & Landscaping in Hoover, AL",
  "provider": { "@id": "https://www.bigtimelawn.com/#business" },
  "areaServed": {
    "@type": "City",
    "name": "Hoover",
    "containedInPlace": { "@type": "State", "name": "Alabama" }
  }
}

Same site, same template, one field with a different value. The homepage says we serve these eleven towns. The Hoover page says this page is about Hoover. Note the provider line, too — it doesn't restate the business, it points at that #business ID from earlier.

This is where most local schema falls down. A site will put identical markup on every page, listing every city on all of them, which tells Google that all eleven pages are equally about all eleven towns — which is another way of saying none of them is clearly about any of them. You built separate city pages precisely so each one could be the specific answer to a specific search. Copying the same markup across all of them undoes that in the one place Google reads most literally.

And containedInPlace is doing real work here. Homewood is a Birmingham suburb. It's also a village in Illinois. "Homewood" alone is a guess; "Homewood, contained in Alabama" isn't.

The tag to leave off

While writing this post, I removed this from that same site:

"aggregateRating": {
  "@type": "AggregateRating",
  "ratingValue": "5.0",
  "reviewCount": "41"
}

Those are real reviews — 41 of them, genuinely five stars. The markup still had to go.

Google's structured data documentation is explicit that self-serving review markup isn't eligible for rich results: a business marking up ratings about itself, on its own site, doesn't qualify. Which means the tag was never going to produce the star rating it looks like it should — it sat there doing nothing, while being out of policy for as long as it was up.

The reviews themselves didn't go anywhere — they're still on the site, still visible, still on the Google Business Profile where they actually count. Only the invisible markup came off. This is the most common thing I find on small business sites that have schema at all, usually added by a plugin that promised star ratings in search results and never delivered them.

How to check your own site

Takes about two minutes:

  1. Paste your homepage into Google's Rich Results Test and see what it detects.
  2. Run it again on one of your city or service pages. Compare the two. If the areaServed is identical on both, you've found the problem this post is about.
  3. Check whether your business type is the specific one or the generic LocalBusiness.
  4. If you find aggregateRating pointed at your own business, take it out.

Schema is the layer that sits on top of the work — it makes a well-built site legible, and it can't rescue a site that doesn't have the pages to begin with. If you're not there yet, start with giving every service and every city its own page; the markup is what you add once those exist.

If you'd rather not think about any of this, it's part of what I do on local SEO work — including the monthly plan, where it's maintained rather than set up once and forgotten. Either way, run the Rich Results Test on your own site today. It's free, and what it shows you is usually worth the two minutes.

Ready to Grow Your Business Online?

Get a free, no-obligation quote today. Based in Birmingham — working with businesses everywhere. I'll review your online presence and show you exactly where you're leaving money on the table.

Get My Free Quote →