LocalBusiness Schema for Multi-Location Companies: 2026 Guide

Master LocalBusiness schema multi location setup in 2026. Learn parentOrganization relationships, openingHoursSpecification, GeoCoordinates, and JSON-LD.

BugViso

16 min read

Configuring structured data for multi-location enterprises, branch networks, and retail franchises requires a multi-tiered entity architecture that connects local branch offices to a centralized corporate parent organization. Relying on isolated, disconnected LocalBusiness blocks creates semantic ambiguity in Google's Knowledge Graph, diluting local search visibility across Google Maps, the Local 3-Pack, and mobile localized search results.

To establish clear geographical and corporate relationships, modern engineering teams implement structured JSON-LD utilizing parentOrganization, subOrganization, or branchOf properties. Each location must declare precise GeoCoordinates (latitude, longitude), granular openingHoursSpecification arrays, unique telephone numbers, and localized address details while avoiding the fatal self-serving review penalty that plagues legacy local SEO configurations.

Diagram
┌─────────────────────────────────────────────────────────────────────────────┐
│                 MULTI-LOCATION ENTERPRISE ENTITY TOPOLOGY                   │
├─────────────────────────────────────────────────────────────────────────────┤
│ Corporate Parent: Organization (Global Headquarters)                        │
│  ├── name: "Apex Logistics Corp", url: "https://example.com"                │
│  └── subOrganization [Array of discrete LocalBusiness entities]             │
│       ├── Branch #1: LocalBusiness (San Francisco Flagship)                 │
│       │    ├── geo: GeoCoordinates, address, openingHoursSpecification     │
│       │    └── branchOf: { @id: "https://example.com/#organization" }       │
│       └── Branch #2: LocalBusiness (New York Regional Hub)                  │
│            ├── geo: GeoCoordinates, address, openingHoursSpecification     │
│            └── branchOf: { @id: "https://example.com/#organization" }       │
└─────────────────────────────────────────────────────────────────────────────┘

This engineering guide details the complete implementation framework for multi-location structured data, covering parent-child entity linking, complex opening hours specifications, department schemas, and automated validation scripts.


1. Technical Mechanics: Knowledge Graph Entity Resolution

When search engine crawlers parse multi-location websites, their entity resolution engines map branch offices into local geographical clusters:

Diagram
┌─────────────────────────────────────────────────────────────────────────────┐
│                    LOCAL ENTITY RESOLUTION PIPELINE                         │
├─────────────────────────────────────────────────────────────────────────────┤
│ 1. Document Extraction    │ Crawls /locations/san-francisco landing page    │
│ 2. Geocoding Validation   │ Verifies latitude/longitude against map address │
│ 3. NAP Parity Check       │ Confirms Name, Address, Phone match visible DOM │
│ 4. Parent Graph Binding   │ Connects branch to global corporate Knowledge Id│
│ 5. Local Pack Injection   │ Feeds Google Maps and Local 3-Pack ranking algo │
└─────────────────────────────────────────────────────────────────────────────┘

1. The NAP Consistency Rule (Name, Address, Phone)

Search algorithms enforce strict verification between the structured data payload and external public directories (Google Business Profile, Bing Places, Apple Business Connect).

If your JSON-LD declares a local phone number as +1-415-555-0144 while the rendered HTML shows a toll-free 800 number, or if the postal address inside schema does not match the official Google Business Profile address down to suite numbers, entity validation confidence drops.

2. The Strict Ban on Self-Serving Reviews for LocalBusiness

In 2019, Google updated its review snippet guidelines to permanently disqualify LocalBusiness and Organization entities from rendering golden review stars on their own websites.

⚠️ Compliance Rule: Never include aggregateRating inside a LocalBusiness schema block on your own website. Google considers marking up customer reviews on your own business to be self-serving and deceptive. The review markup will be ignored, and persistent violations will trigger a manual structured data action against your entire domain.

To explore how review schema operates legally on eligible entities, review our guide on Product and AggregateRating review schema.


2. Production JSON-LD Blueprint: Multi-Location Corporate Graph

The following JSON-LD script represents a production-grade implementation for a multi-location enterprise branch page (e.g., https://example.com/locations/san-francisco). It establishes a bidirectional link between the local branch office and the global corporate headquarters:

html
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "Organization",
      "@id": "https://example.com/#organization",
      "name": "Apex Engineering Group",
      "url": "https://example.com",
      "logo": {
        "@type": "ImageObject",
        "@id": "https://example.com/#logo",
        "url": "https://example.com/assets/logo.png"
      },
      "sameAs": [
        "https://www.linkedin.com/company/apex-engineering-group",
        "https://twitter.com/apex_eng_group"
      ]
    },
    {
      "@type": "ProfessionalService",
      "@id": "https://example.com/locations/san-francisco/#localbusiness",
      "name": "Apex Engineering - San Francisco",
      "url": "https://example.com/locations/san-francisco",
      "image": "https://example.com/assets/locations/sf-office.jpg",
      "telephone": "+1-415-555-0188",
      "priceRange": "$$$$",
      "branchOf": {
        "@id": "https://example.com/#organization"
      },
      "address": {
        "@type": "PostalAddress",
        "streetAddress": "100 Montgomery St, Suite 400",
        "addressLocality": "San Francisco",
        "addressRegion": "CA",
        "postalCode": "94104",
        "addressCountry": "US"
      },
      "geo": {
        "@type": "GeoCoordinates",
        "latitude": 37.7909,
        "longitude": -122.4018
      },
      "hasMap": "https://maps.google.com/?cid=1234567890123456789",
      "openingHoursSpecification": [
        {
          "@type": "OpeningHoursSpecification",
          "dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
          "opens": "08:30",
          "closes": "17:30"
        },
        {
          "@type": "OpeningHoursSpecification",
          "dayOfWeek": ["Saturday"],
          "opens": "09:00",
          "closes": "14:00"
        }
      ],
      "areaServed": [
        {
          "@type": "City",
          "name": "San Francisco"
        },
        {
          "@type": "City",
          "name": "Oakland"
        },
        {
          "@type": "City",
          "name": "San Jose"
        }
      ]
    }
  ]
}
</script>

3. Dynamic Next.js 15 Implementation with TypeScript

In modern enterprise applications, location landing pages are generated dynamically from headless databases. The Next.js 15 React component below serializes localized database records into type-safe JSON-LD schemas:

tsx
// components/locations/LocationSchema.tsx
import React from 'react';

export interface LocationHours {
  days: string[];
  opens: string;
  closes: string;
}

export interface LocationData {
  id: string;
  name: string;
  slug: string;
  phone: string;
  street: string;
  city: string;
  state: string;
  zip: string;
  country: string;
  latitude: number;
  longitude: number;
  imageUrl: string;
  priceRange?: string;
  hours: LocationHours[];
  serviceAreas?: string[];
  corporateOrgUrl: string;
  corporateOrgName: string;
}

export function LocationSchema({ location }: { location: LocationData }) {
  const branchId = `${location.corporateOrgUrl}/locations/${location.slug}/#localbusiness`;
  const orgId = `${location.corporateOrgUrl}/#organization`;

  const schema = {
    '@context': 'https://schema.org',
    '@graph': [
      {
        '@type': 'Organization',
        '@id': orgId,
        name: location.corporateOrgName,
        url: location.corporateOrgUrl
      },
      {
        '@type': 'LocalBusiness',
        '@id': branchId,
        name: location.name,
        url: `${location.corporateOrgUrl}/locations/${location.slug}`,
        image: location.imageUrl,
        telephone: location.phone,
        priceRange: location.priceRange || '$$',
        branchOf: {
          '@id': orgId
        },
        address: {
          '@type': 'PostalAddress',
          streetAddress: location.street,
          addressLocality: location.city,
          addressRegion: location.state,
          postalCode: location.zip,
          addressCountry: location.country
        },
        geo: {
          '@type': 'GeoCoordinates',
          latitude: location.latitude,
          longitude: location.longitude
        },
        openingHoursSpecification: location.hours.map((h) => ({
          '@type': 'OpeningHoursSpecification',
          dayOfWeek: h.days,
          opens: h.opens,
          closes: h.closes
        })),
        ...(location.serviceAreas && {
          areaServed: location.serviceAreas.map((area) => ({
            '@type': 'City',
            name: area
          }))
        })
      }
    ]
  };

  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
    />
  );
}

4. Advanced Entity Configurations: Departments & Specialized Sub-Types

Standard LocalBusiness is often too generic. Schema.org provides dozens of specialized subclasses that supply search engines with immediate industry context:

Diagram
┌─────────────────────────────────────────────────────────────────────────────┐
│                      LOCALBUSINESS SUBCLASS HIERARCHY                       │
├─────────────────────────────────────────────────────────────────────────────┤
│ LocalBusiness (Generic Root)                                                │
│  ├── FinancialService (Banking, Accounting, Insurance)                      │
│  ├── MedicalBusiness (Dentist, Hospital, Pharmacy, Clinic)                  │
│  ├── LegalService (Attorney, Notary)                                        │
│  ├── FoodEstablishment (Restaurant, Bakery, Cafe)                           │
│  ├── AutomotiveBusiness (AutoRepair, AutoDealer)                            │
│  └── ProfessionalService (Engineering, IT Consulting, RealEstateAgent)      │
└─────────────────────────────────────────────────────────────────────────────┘

Always use the most specific applicable subclass. If you operate an engineering consultancy, use ProfessionalService. If you manage dental clinics, use Dentist.

Department Schema for Multi-Service Facilities

For large corporate complexes, retail superstores, or hospitals with distinct internal departments (e.g., a car dealership with a dedicated "Service Department" and "Parts Department"), use the department property:

json
{
  "@context": "https://schema.org",
  "@type": "AutoDealer",
  "name": "Apex Motors - Central Showroom",
  "telephone": "+1-555-0100",
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "500 Main Street",
    "addressLocality": "Austin",
    "addressRegion": "TX",
    "postalCode": "78701"
  },
  "department": [
    {
      "@type": "AutoRepair",
      "name": "Apex Motors Service & Collision Center",
      "telephone": "+1-555-0199",
      "openingHoursSpecification": [
        {
          "@type": "OpeningHoursSpecification",
          "dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
          "opens": "07:00",
          "closes": "18:00"
        }
      ]
    }
  ]
}

This allows Google to display discrete opening hours and telephone lines for different departments inside Google Maps and Knowledge Panels.


5. Holiday Schedules & Special Opening Hours (specialOpeningHoursSpecification)

One of the most frequent customer experience breakdowns during holiday seasons is inaccurate operating hours in search results. Standard openingHoursSpecification defines regular weekly schedules, but Schema.org provides specialOpeningHoursSpecification to override hours for specific calendar dates:

json
{
  "@context": "https://schema.org",
  "@type": "LocalBusiness",
  "name": "Apex Retail - Downtown Branch",
  "specialOpeningHoursSpecification": [
    {
      "@type": "OpeningHoursSpecification",
      "validFrom": "2026-11-26",
      "validThrough": "2026-11-26",
      "opens": "00:00",
      "closes": "00:00",
      "description": "Closed for Thanksgiving Day"
    },
    {
      "@type": "OpeningHoursSpecification",
      "validFrom": "2026-11-27",
      "validThrough": "2026-11-27",
      "opens": "06:00",
      "closes": "22:00",
      "description": "Black Friday Extended Shopping Hours"
    },
    {
      "@type": "OpeningHoursSpecification",
      "validFrom": "2026-12-24",
      "validThrough": "2026-12-24",
      "opens": "08:00",
      "closes": "15:00",
      "description": "Christmas Eve Early Closing"
    }
  ]
}

Googlebot crawls and extracts these seasonal override dates, dynamically updating the business's open/closed status in local Knowledge Panels on those exact calendar days. Setting both opens and closes to "00:00" informs the search engine that the business is closed for the entire day.


6. Curbside Pickup & In-Store Reserve Actions (potentialAction)

For multi-location retail chains, healthcare providers, or restaurant franchises, enabling interactive user actions directly from search results drives immediate foot traffic. Schema.org supports potentialAction declaring OrderAction or ReserveAction:

json
{
  "@context": "https://schema.org",
  "@type": "Store",
  "name": "Apex Hardware - Austin North",
  "potentialAction": {
    "@type": "OrderAction",
    "target": {
      "@type": "EntryPoint",
      "urlTemplate": "https://example.com/order/austin-north?item={item_id}",
      "inLanguage": "en-US",
      "actionPlatform": [
        "http://schema.org/DesktopWebPlatform",
        "http://schema.org/MobileWebPlatform"
      ]
    },
    "deliveryMethod": "http://purl.org/goodrelations/v1#DeliveryModePickUp",
    "result": {
      "@type": "Order",
      "orderStatus": "http://schema.org/OrderProcessing"
    }
  }
}

7. Python Automation: Multi-Location Schema Validator

This automated Python script parses a location landing page, validates that mandatory geographic properties exist, checks for valid decimal coordinates, and verifies that branchOf links to an active Organization:

python
# scripts/audit_local_schema.py
import sys
import json
import httpx
from bs4 import BeautifulSoup

def audit_location_page(url: str):
    print(f"[*] Auditing LocalBusiness schema on: {url}")
    headers = {"User-Agent": "BugVisoLocalValidator/1.0 (+https://bugviso.com)"}
    
    try:
        res = httpx.get(url, headers=headers, timeout=12.0, follow_redirects=True)
    except Exception as e:
        print(f"[X] Request failed: {e}")
        return False
        
    soup = BeautifulSoup(res.text, "html.parser")
    scripts = soup.find_all("script", type="application/ld+json")
    
    if not scripts:
        print("[X] No JSON-LD script blocks found on page.")
        return False
        
    local_found = False
    
    for script in scripts:
        if not script.string:
            continue
        try:
            data = json.loads(script.string)
        except json.JSONDecodeError as exc:
            print(f"[X] Syntax Error: {exc}")
            continue
            
        nodes = data.get("@graph", [data]) if isinstance(data, dict) else data
        
        for node in nodes:
            ntype = node.get("@type", "")
            # Check if type is LocalBusiness or a known sub-type
            if "LocalBusiness" in ntype or ntype in ["ProfessionalService", "Store", "Restaurant", "Dentist"]:
                local_found = True
                name = node.get("name")
                phone = node.get("telephone")
                addr = node.get("address", {})
                geo = node.get("geo", {})
                branch_of = node.get("branchOf") or node.get("parentOrganization")
                
                print(f"[✓] Detected Local Entity: '{name}' (Type: {ntype})")
                print("=" * 65)
                
                # Check Self-Serving Review Violation
                if "aggregateRating" in node:
                    print("  [X] CRITICAL POLICY VIOLATION: Self-serving 'aggregateRating' detected on LocalBusiness!")
                    print("      Google will ignore this markup and may issue a manual action penalty.")
                    
                # Validate Address
                if not addr or not addr.get("streetAddress") or not addr.get("postalCode"):
                    print("  [X] Incomplete PostalAddress: Missing streetAddress or postalCode.")
                else:
                    print(f"  - Address: {addr.get('streetAddress')}, {addr.get('addressLocality')}, {addr.get('addressRegion')} {addr.get('postalCode')}")
                    
                # Validate Geo Coordinates
                if not geo:
                    print("  [X] Missing GeoCoordinates entity!")
                else:
                    lat = geo.get("latitude")
                    lng = geo.get("longitude")
                    if isinstance(lat, (int, float)) and isinstance(lng, (int, float)):
                        print(f"  - Coordinates: Lat {lat}, Lng {lng} [VALID NUMERIC]")
                    else:
                        print(f"  [X] Invalid GeoCoordinates: Expected numeric floats, got '{lat}', '{lng}'.")
                        
                # Validate Corporate Linkage
                if not branch_of:
                    print("  [!] Architecture Warning: No 'branchOf' or 'parentOrganization' linkage detected.")
                else:
                    parent_ref = branch_of.get("@id") if isinstance(branch_of, dict) else branch_of
                    print(f"  [✓] Linked to Corporate Parent: {parent_ref}")
                    
                print("=" * 65)

    if not local_found:
        print("[X] No LocalBusiness entity detected on page.")
        return False
        
    return True

if __name__ == "__main__":
    target = sys.argv[1] if len(sys.argv) > 1 else "https://example.com/locations/san-francisco"
    audit_location_page(target)

6. How BugViso Audits Multi-Location Schema Automatically

Validating enterprise multi-location networks containing dozens or hundreds of regional branch pages cannot be maintained manually. BugViso's Advanced SEO Intelligence Engine audits local schema across your entire site crawl.

Diagram
┌─────────────────────────────────────────────────────────────────────────────┐
│                 BUGVISO MULTI-LOCATION AUDIT CAPABILITIES                   │
├─────────────────────────────────────────────────────────────────────────────┤
│ 1. NAP Consistency Scanner    │ Cross-checks Name, Address, Phone across DOM│
│ 2. Self-Serving Review Shield │ Detects illegal aggregateRating on LocalBiz │
│ 3. Geocoordinate Verifier     │ Confirms latitude/longitude precision       │
│ 4. Parent-Child Relationship  │ Validates branchOf links to Organization    │
└─────────────────────────────────────────────────────────────────────────────┘

When you launch an audit with BugViso:

  1. Self-Serving Review Detection: The crawler automatically scans every LocalBusiness entity across all locations, instantly flagging any prohibited review stars markup that could jeopardize your site's standing.
  2. Geographical Data Validation: BugViso validates that GeoCoordinates are formatted as valid numeric floats, checks that postal codes align with regional standards, and verifies that opening hours syntax is free of invalid time strings.
  3. Multi-Page Site-Wide Crawl: Rather than testing individual branch pages in isolation, BugViso maps your entire location directory, surfacing broken location links, orphan store pages, and missing parent organization references.
  4. Remediation Code Generator: Identified location schema errors are paired with clean, copy-pasteable JSON-LD snippets configured for immediate developer implementation.

To audit your complete multi-location network and dominate local search rankings, run a free BugViso technical scan.


7. Common Implementation Traps & Edge Cases

Avoid these frequent mistakes when structuring multi-location schemas:

1. Declaring All Locations in a Massive Array on the Homepage

An enterprise anti-pattern is injecting an array of 500 distinct LocalBusiness objects into the homepage JSON-LD.

  • Homepage: Should declare a single Organization entity representing the corporate brand, with links to the locations directory.
  • Location Landing Pages: Each dedicated branch page (/locations/dallas) should declare its specific LocalBusiness schema with a branchOf pointer back to the corporate entity.

Declaring hundreds of physical locations on a single document causes schema bloat, slows DOM parsing, and dilutes local relevance.

2. Formatting Coordinates as Strings

Google's parser requires latitude and longitude to be numeric floats. Wrapping coordinates in quotation marks causes validation warnings:

json
// ❌ Broken: Coordinate strings fail Schema.org numeric types
"geo": {
  "@type": "GeoCoordinates",
  "latitude": "37.7909",
  "longitude": "-122.4018"
}

// ✅ Fixed: Raw numeric floats
"geo": {
  "@type": "GeoCoordinates",
  "latitude": 37.7909,
  "longitude": -122.4018
}

For foundational concepts on structuring clean JSON-LD blocks, review our beginner's guide to schema markup.


8. Frequently Asked Questions

What is the difference between branchOf and parentOrganization?

Both properties express parent-child corporate relationships. In Schema.org, branchOf is specifically defined on LocalBusiness to link a local branch back to its parent Organization. parentOrganization is a broader property used between two Organization entities (e.g., a subsidiary company linked to a holding corporation). Both are supported by Google.

Can a service-area business (SAB) with no physical storefront use LocalBusiness schema?

Yes. If you operate a mobile service business (e.g., plumbing or mobile IT repair) without a public storefront, declare areaServed with the relevant cities and omit streetAddress from the PostalAddress entity, retaining only addressLocality and addressRegion.

Should each location have its own dedicated URL?

Yes. Google's Local SEO guidelines strongly recommend creating a unique, dedicated landing page for every physical location. This URL serves as the canonical landing page for both local schema and your Google Business Profile listing.

How do I format 24/7 opening hours in Schema.org?

To specify that a business or emergency service is open 24 hours a day, 7 days a week, set opens to "00:00" and closes to "23:59" across all seven days of the week in your openingHoursSpecification.

Does LocalBusiness schema directly improve Google Maps rankings?

While schema is not the sole ranking factor for Google Maps, providing explicit, verified NAP data, geographic coordinates, and corporate relationships establishes high entity confidence, which directly supports higher rankings in the Google Local 3-Pack and Maps results.


9. Conclusion

Implementing an enterprise-grade LocalBusiness schema multi location setup bridges your physical branch offices into a cohesive, machine-readable corporate network. By deploying clean parent-child entity graphs, declaring accurate geographic coordinates, enforcing NAP consistency, and eliminating self-serving review violations, your locations capture dominant visibility across localized search and Google Maps—which is exactly what an automated BugViso scan verifies across every branch page in your network.

Found this useful? Share it.

See where your site stands

Run a free BugViso audit for SEO, speed, accessibility and AI search readiness — with fixes you can ship today.