Back to Blog
Guides12 min read24 July 2026

Local Business Schema for Multiple Locations & Service Areas

How to structure LocalBusiness schema across multiple locations, service-area businesses, and franchises, with @id strategy, areaServed, and examples.

By AI Schema Gen Team

Basic LocalBusiness markup is well covered elsewhere: name, address, phone, hours, done. The situations that actually generate questions are the ones the introductory guides skip, a chain with twelve branches, a plumber with no storefront who covers three counties, a franchise where each location is independently owned, a clinic that both sees patients on site and does home visits.

These cases are where implementations go wrong, and where the mistakes are expensive: diluted entity signals, locations competing with each other, or markup that overstates where you actually operate. This guide covers the architecture for each.

If you need the single-location basics first, our local business schema guide covers them.

The core decision: LocalBusiness or Organization?

Before anything else, get this right, because it determines everything downstream.

LocalBusiness (and its subtypes) is for businesses with a physical location customers can visit. A shop, a restaurant, a clinic, an office with a waiting room.

Organization is for companies without a public storefront, online-only businesses, or companies that operate nationally without a specific local presence customers walk into.

The distinction matters because using LocalBusiness for a business with no visitable premises misrepresents what you are. If you don't display a public address because customers never come to you, you shouldn't be asserting one in markup either.

The rule worth internalizing: match what's visible on your site. If you don't display a full address, don't include one in the markup. If you describe a service area in your content, describe the same area in your structured data, not a larger one. Local search runs on trust, and overstating coverage is an easy way to lose it.

Multiple locations: one page each

The architecture for a multi-location business is settled, and the mistake is almost always the same one: stacking every location into a single block on the homepage.

Do this instead:

Give each location its own page. A dedicated landing page per branch with unique content, that location's address, phone number, hours, staff, and anything genuinely specific to it. Not a template with the city name swapped.

Give each page its own LocalBusiness markup, with that location's actual address, geo-coordinates, and opening hours.

Give each location a unique, stable @id. This is the part people get wrong. Reusing the same @id across every location page tells search engines these are all the same entity, which is precisely the opposite of what you want. Each branch needs its own identifier, typically the location page URL plus a fragment.

Link locations to the parent brand using parentOrganization or branchOf, and optionally list branches from the parent using subOrganization.

Here's a location page in full:

{
  "@context": "https://schema.org",
  "@type": "Dentist",
  "@id": "https://example.com/locations/austin-south#business",
  "name": "Bright Smile Dental - South Austin",
  "url": "https://example.com/locations/austin-south",
  "telephone": "+1-512-555-0142",
  "image": "https://example.com/images/austin-south.jpg",
  "priceRange": "$$",
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "1200 South Lamar Blvd",
    "addressLocality": "Austin",
    "addressRegion": "TX",
    "postalCode": "78704",
    "addressCountry": "US"
  },
  "geo": {
    "@type": "GeoCoordinates",
    "latitude": 30.2545,
    "longitude": -97.7688
  },
  "openingHoursSpecification": [
    {
      "@type": "OpeningHoursSpecification",
      "dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
      "opens": "08:00",
      "closes": "17:00"
    }
  ],
  "hasMap": "https://maps.google.com/?cid=EXAMPLE",
  "parentOrganization": {
    "@id": "https://example.com/#organization"
  },
  "sameAs": [
    "https://www.facebook.com/brightsmiledental"
  ]
}

Two details worth noting. hasMap points at the location's Google Maps listing, which helps correlate your site with the map entity. And parentOrganization is a bare @id reference to the brand-level Organization defined once on your site, not a duplicated object.

The brand-level entity, defined once:

{
  "@context": "https://schema.org",
  "@type": "Organization",
  "@id": "https://example.com/#organization",
  "name": "Bright Smile Dental",
  "url": "https://example.com",
  "logo": "https://example.com/logo.png",
  "subOrganization": [
    { "@id": "https://example.com/locations/austin-south#business" },
    { "@id": "https://example.com/locations/austin-north#business" }
  ]
}

This gives you a clean graph: one brand, several branches, each independently identifiable and each able to build its own local signals.

Why stacking locations on one page fails

Worth being explicit about the failure mode, since the instinct to put everything on the homepage is common.

A single page carrying an array of twelve locations gives search engines one URL associated with twelve addresses. No individual branch has a page to rank for its own city. Local queries, which are overwhelmingly city-specific, have nothing granular to match. And the entity signals that would accumulate to a specific branch instead spread thinly across an undifferentiated blob.

A store locator page listing all your branches is fine and useful, but it should link out to individual location pages, and those pages carry the LocalBusiness markup. The locator itself is a directory, not twelve businesses.

Service-area businesses

Plumbers, mobile mechanics, electricians, home-care providers, mobile dog groomers. Businesses that travel to the customer rather than the reverse.

The awkwardness is that these businesses often have an address, an office, a yard, sometimes a home, that isn't a place customers visit. Asserting it as a business address misrepresents how you operate.

The approach: use areaServed to describe where you work, and be careful about address. If you have no premises customers visit, consider Organization rather than LocalBusiness, and describe coverage through areaServed rather than a storefront address you don't really have.

areaServed can be expressed several ways, in increasing precision:

As plain text or a list, simplest, and fine for a handful of towns:

"areaServed": ["Austin", "Round Rock", "Cedar Park"]

As administrative areas, more explicit about what kind of place you mean:

"areaServed": [
  { "@type": "City", "name": "Austin" },
  { "@type": "AdministrativeArea", "name": "Travis County" }
]

As a geographic radius using GeoCircle, useful when you work within a distance of a base rather than by administrative boundary:

"areaServed": {
  "@type": "GeoCircle",
  "geoMidpoint": {
    "@type": "GeoCoordinates",
    "latitude": 30.2672,
    "longitude": -97.7431
  },
  "geoRadius": "48000"
}

geoRadius is in metres by default, so 48000 is roughly 30 miles.

As a defined shape using GeoShape, for genuinely irregular coverage areas.

Whichever you use, list your service areas in visible text on the page as well. Structured data should describe what's on the page, not assert something a visitor can't verify.

One realistic caveat: Google recognizes areaServed in structured data, but your Google Business Profile is the more direct lever for how service areas appear in Google's local results. Treat the markup as supporting and clarifying, not as a substitute for setting your service area in your Business Profile.

Hybrid businesses

Plenty of businesses are both, a clinic with a waiting room that also does home visits, a bakery with a shopfront that delivers across the city, a law firm with an office whose clients are regional.

These should include both address (the physical premises) and areaServed (the wider region served). There's no conflict: one says where you are, the other says who you serve. A law firm doesn't only serve clients who walk through the door, and areaServed is how you say so.

For services specifically, makesOffer and hasOfferCatalog let you structure what you actually provide in machine-readable form, which is more precise than leaving it to prose.

Franchises

The hardest case, because the ownership structure and the brand structure diverge.

Each franchise location is typically an independent business, separate legal entity, separate ownership, separate Google Business Profile, operating under a shared brand. Model it that way:

Each franchise location gets its own LocalBusiness markup with its own @id, address, and hours, exactly like a branch.

Use parentOrganization or branchOf to express the brand relationship without implying the franchisor operates the location.

Keep NAP per location, matching that location's own Google Business Profile rather than the franchisor's corporate details.

Where this goes wrong: franchisee sites that copy the franchisor's corporate markup wholesale, ending up with the corporate address and phone number on a local page. That produces a mismatch between the site, the markup, and the location's Business Profile, exactly the inconsistency that erodes confidence in your local data.

Worth a brief word, since local queries are increasingly answered rather than listed.

When someone asks an assistant for a dentist in South Austin, or a plumber covering Round Rock, the system answering has to resolve a specific business entity in a specific place. That's an entity disambiguation problem, and it's precisely what the architecture above supports: distinct identifiers per location, explicit relationships to a parent brand, and coverage areas stated in machine-readable form rather than inferred from prose.

The honest limit is the same one that applies everywhere: Google states no special structured data is required for its AI features, and no markup guarantees you'll be named in an answer. What clean multi-location markup does is make each branch unambiguously identifiable as a distinct thing in a distinct place, which is the prerequisite for being resolved correctly, not a guarantee of being chosen. We covered the broader picture in does schema markup help with AI Overviews.

The practical implication for a multi-location business is reassuring: there's no separate AI work to do here. The same page-per-location structure with unique identifiers that serves traditional local search is what makes your branches legible to answer engines.

NAP consistency

Across every location, the Name, Address, and Phone in your markup should match that location's Google Business Profile and directory citations exactly. Not approximately, exactly.

"Suite 200" versus "Ste 200," a tracking phone number in the markup versus the real number on the profile, or a slightly different business name per location are the kinds of small divergences that make it harder for search engines to confidently resolve your locations as the entities they are.

This is unglamorous maintenance work rather than a clever tactic, and it's the thing most likely to be quietly wrong on a multi-location site that's been through a rebrand or a phone system change.

Where the markup should and shouldn't go

A question that comes up constantly: does LocalBusiness markup go on every page?

No. Put it where it describes the page. A location page describes that location, so it carries that location's markup. Your homepage can carry the brand-level Organization. Service pages and blog posts generally shouldn't carry LocalBusiness markup at all, they're not a business location.

The exception worth noting: a single-location business can reasonably carry its LocalBusiness markup on the homepage, because the homepage is the business's page. That doesn't generalize to putting it everywhere.

Managing this at scale

Twelve locations is a manual job. Two hundred is a systems problem, and the failure mode changes accordingly.

Generate from a single source of truth. Location data should live in one place, a CMS collection, a custom post type, a database table, and both the visible page and the markup should render from it. The moment addresses live in page copy and separately in a schema field, they diverge. Someone updates a phone number on the page and not in the markup, and now your structured data contradicts your content.

Template the markup, not the data. One template that takes a location record and produces the JSON-LD, applied across every location page. This means a fix, adding hasMap, correcting a subtype, happens once rather than two hundred times. Our docs cover the exact setup for generating this from live location data rather than maintaining templates by hand.

Watch for the seasonal and temporary cases. Holiday hours, temporary closures, and locations that shut for renovation are where multi-location markup most commonly goes stale. openingHoursSpecification supports date-bounded entries with validFrom and validThrough, which is the correct way to express seasonal hours rather than editing the standard hours twice a year and forgetting to revert.

Handle closures properly. When a location closes permanently, don't leave its markup live. Remove the page or mark it clearly, and update the parent's subOrganization list. Stale location markup pointing at a closed branch is worse than none, because it actively misinforms.

Audit periodically rather than at launch only. Multi-location data decays, phone systems change, suites get renumbered, hours shift. A quarterly check against your Google Business Profiles catches drift before it compounds across every location at once.

Common mistakes

Reusing the same @id across location pages. Tells search engines every branch is the same entity.

All locations stacked on the homepage instead of individual pages.

Generic LocalBusiness where a subtype exists. Google recommends the most specific applicable type: Dentist, Restaurant, Plumber, AutoRepair, see our schema types directory for the full list of options.

Asserting an address for a business customers can't visit.

Overstating service area beyond where you genuinely operate.

Copying corporate markup onto franchise or branch pages.

Markup that contradicts the visible page. Service areas in the markup that aren't listed in the content, or hours that differ from what's displayed.

Location-specific sameAs links that don't exist. If individual branches don't have their own social profiles, reference the brand's profiles at the brand level rather than fabricating per-location ones.

Validation

Run the Rich Results Test against a representative sample, one location page, your homepage, and if you have them, a service-area page and a franchise page. Testing one URL on a multi-location site tells you almost nothing about the other forty.

Search Console's reporting shows how Google parsed markup across the whole site after crawling, which is where a template-level mistake affecting every location page will surface.

For generic syntax checking on properties that don't produce rich results, use the Schema Markup Validator at validator.schema.org.

One thing to keep in perspective: LocalBusiness markup makes you eligible for local features and helps search engines confirm details already on your page. It isn't a ranking trick, and it doesn't guarantee rich results on its own. Accurate, consistent, machine-readable business details are the goal, see our guide to what Google currently supports for the broader picture.

Frequently Asked Questions


AI Schema Gen generates location-specific LocalBusiness markup across multi-location sites, correct subtypes, unique identifiers, and consistent entity relationships. Start free, or check pricing if you're evaluating this for multiple locations, or see the local business use case.

Generate perfect schema in 30 seconds

AI Schema Gen handles everything automatically, free to start.

Get Started Free