Read it, chapter by chapter
The full 7-chapter guide for law firms — pick any chapter to read it here.
What Is LocalBusiness Schema? (And Why It's Different from LegalService)
LocalBusiness is a schema.org type designed to mark up location-specific, customer-facing businesses: a single office, a restaurant, a retail store, a hotel. For a multi-office law firm, LocalBusiness is the right choice for each individual office—the physical location a client can visit, with real hours, a real address, a real phone number, and real reviews.
This is subtly different from LegalService, which is a generic service node that represents a service offering (e.g., 'medical malpractice defense') without inherent location-specificity. A LegalService node usually lives at `/practice-area` or describes a service; a LocalBusiness node lives at `/office/[city]` and describes a place. The distinction matters because LocalBusiness has required and recommended fields specific to place-based businesses—geo-coordinates, address, phone, hours, and review aggregates—that LegalService does not assume.
| Property | LocalBusiness | LegalService |
|---|---|---|
| What it represents | A physical, location-specific office or presence | An abstract service offering (e.g., 'divorce defense') |
| Geo-coordinates | Required (real building address, lat/lng) | Optional; can be a service area |
| Address | Yes, byte-identical to real office address | Usually omitted or generic firm address |
| Phone | Location-specific phone number (preferred) or firm main line | Firm-wide contact info or missing |
| Hours | Local business hours (e.g., 9–5, closed Sundays) | Not applicable |
| Review aggregates | Real, location-specific ratings from GBP | Rarely used |
| Canonical URL | /office/[city] or /locations/[city] | /practice-area/[practice] or /service/[service] |
Google's Search Central documentation recommends LocalBusiness for physical locations and LegalService for service-based businesses. For a law firm, the clearest approach: use LocalBusiness for every office page, anchored to a single firm-level Organization node that carries the brand, logo, and leadership. This structure immediately tells Google (and AI search engines) that you are one firm with multiple places, not several unrelated firms.
Single-Office vs. Multi-Office Architecture: When You Need Multiple Nodes
If your law firm has one office, you declare one LocalBusiness node (or one Organization node with address/geo) and you are done. If your firm has multiple offices—say, Los Angeles, San Francisco, and San Diego—you need a different strategy: one firm-level Organization node plus one LocalBusiness node per office. This is the standard practice for multi-location businesses (think McDonald's: one McDonald's Corporation; thousands of Restaurant nodes, each a different location).
The key insight: do not replicate the firm's identity at every office. A common mistake is declaring a full Organization node for each office with a different name or address: `[{"@type": "Organization", "name": "Law Firm LLP — San Francisco", "address": {...}}]` on the SF office page, then `[{"@type": "Organization", "name": "Law Firm LLP — Los Angeles", "address": {...}}]` on the LA page. This splits the entity: search engines see three separate organizations instead of one firm with three locations. Over time, each "organization" competes with the others for the firm's brand searches, diluting authority.
The correct structure: (1) One Organization node (typically in the site footer or root layout), declaring the firm name, logo, main address (usually the headquarters or a representative office), phone, founding date, and leadership. Give it a stable, top-level @id like `'#organization'` or `'https://yourfirm.com/#organization'`. (2) One LocalBusiness node per office page, each with its own @id like `'#office-san-francisco'`, `'#office-los-angeles'`. (3) Each LocalBusiness node carries the location-specific address, phone, geo, hours, and reviews. (4) Each LocalBusiness node's `provider` field references the firm's Organization node by @id, not by re-declaring it. This tells search engines: 'The San Francisco office is run by the same firm as the LA office; they are not separate entities.'
Why this matters: authority is a zero-sum game in entity search. A single, well-established Organization node accumulates links, citations, and mentions. Each mention ("Call Law Firm LLP at..." on a local directory) reinforces that one entity. If you split it across three organization nodes, each one gets 1/3 the cumulative authority. Additionally, search engines can only rank one entity per entity in featured snippets and knowledge panels. One firm with three LocalBusiness locations will show up with all three office locations in the knowledge panel (or at least in local pack); three separate organizations will compete and dilute the visibility.
The Entity Graph Architecture: Connecting Firm, Offices, and Attorneys
The entity graph for a multi-office law firm is a directed acyclic graph with four layers: (1) Firm (Organization), (2) Offices (LocalBusiness, one per location), (3) Attorneys (Person, one per attorney), and (4) Services/Practice Areas (Service or specific LegalService nodes, if you want them). The edges (relationships) are the @id references. Here is what a real graph looks like:
Root Layer (Firm): One Organization node, @id = '#organization', living in the site footer or root layout, declaring `name` (firm name), `logo` (firm logo URL), `address` (headquarters address), `telephone` (firm main line), `sameAs` (GBP firm listing, Avvo, LinkedIn, Justia, etc.), `knowsAbout` (practice areas as keywords or entities), and `founder` or `member` pointing to the founding/senior attorneys by their Person @ids.
Office Layer: One LocalBusiness node per office, @id = '#office-city' (e.g., '#office-sf'). Each carries `name` (firm name or "[Firm] — [City]"), `address` (real, byte-identical to GBP), `telephone` (location-specific or main line), `geo` (Point with `latitude` and `longitude`, geocoded to the actual building address), `openingHours` (if available), `sameAs` (the GBP URL for that specific location), and `aggregateRating` (real review data from GBP: `ratingValue` and `ratingCount`, but ONLY if the location has actual reviews). Critically, the LocalBusiness node's `provider` field is `{'@id': '#organization'}`, referencing the firm node. Not a full Organization object; just the @id reference.
Attorney Layer: One Person node per attorney, @id = '#attorney-firstname-lastname' (or '#person-attorney-id'). Each carries `name`, `sameAs` (LinkedIn, bar profile, etc.), `worksFor` (reference to firm: `{'@id': '#organization'}`), `hasCredential` (bar admission, if applicable), `knowsAbout` (practice areas they handle), and `jobTitle` (Partner, Associate, Counsel, etc.). This layer makes individual attorneys machine-readable entities with their own identity, supporting attorney-level authority for AI citation.
Service Layer (Optional): If you want to mark up individual practice areas or services (e.g., 'Medical Malpractice Defense'), create Service nodes with @id like '#service-medical-malpractice', carrying `name`, `description`, `provider` (reference to '#organization'), and `areaServed` (geographic scope, if location-specific).
The graph is connected by @id references: LocalBusiness → Organization (provider), Person → Organization (worksFor), Service → Organization (provider). This creates one unified entity graph. Every page can then reference any of these nodes by their @ids without re-declaring them. For example, an office page declares the LocalBusiness node but references the Organization and attorney Person nodes; an attorney bio page declares the Person node and references Organization and LocalBusiness nodes. The references are efficient and prevent duplication.
Common Mistakes: Duplicate Entities and Broken @ids
The most frequent schema errors in multi-office law firm sites are entity duplication and inconsistent @id naming. Here are the canonical mistakes and how to fix them.
Mistake 1: Re-declaring the Organization node on every page. Wrong: Every office page contains a full Organization node with the firm's name, logo, address, and phone. This results in three (or fifty) separate Organization entities in the schema, each slightly different (different addresses, maybe different phone numbers). Search engines see this as three firms that happen to have the same name, or as spam/duplication. Fix: Declare the Organization node once (typically in the root layout or footer) and reference it by @id on every page.
Mistake 2: Inconsistent @id naming across pages. Wrong: The office in San Francisco is declared as `'#sf-office'` on one page and `'#sanfrancisco'` on another. Schema validators treat these as two different entities. Fix: Establish one naming scheme (e.g., '#office-city-state' or '#location-city') and use it consistently across the entire site. Document the scheme in your site's schema guidelines.
Mistake 3: Using absolute domain URLs as @ids instead of fragments. Wrong: LocalBusiness nodes have @ids like `'https://yourfirm.com/office/san-francisco/'` (a full URL). This is valid but creates a new problem: if your domain changes (e.g., migration, domain restructuring, cutover from staging to production), the @ids break. Also, absolute URLs make the schema less portable. Fix: Use fragment identifiers (`'#office-sf'`) and let the `@context` determine the base URL. If you need absolute @ids for external identity (e.g., publishing on a directory), include both: `'@id': '#office-sf'` internally, plus a `'url': 'https://yourfirm.com/office/san-francisco/'` field. The @id is for your graph; the url is for external references.
Mistake 4: Fabricating or outdated review data. Wrong: Manually entering `aggregateRating: {ratingValue: 4.9, ratingCount: 156}` without verifying it matches the Google Business Profile. If GBP shows 4.5 stars and 89 reviews, but your schema claims 4.9 and 156, you are lying—and search engines know it. Real offices with real GBP listings have real review data. Mismatches trigger quality flags. Fix: Pull aggregateRating data directly from GBP API or manually verify it matches GBP every time you publish. If an office has no reviews yet, omit aggregateRating (do not fabricate it).
Mistake 5: Generic or fabricated addresses and geo-coordinates. Wrong: Using the firm's headquarters address for every office page. Wrong: Geocoding the address to a neighborhood instead of the actual building. Wrong: Using placeholder coordinates (e.g., the city's geographic center) instead of the real building address. All of these break the trust signal: search engines expect LocalBusiness to represent a real, verifiable place. If a visitor shows up at the coordinates on your schema, they should find the office. Fix: Use Google Places API or a geocoding service to get real coordinates for the actual building address. Update coordinates annually if the office moves.
Mistake 6: Missing sameAs link to Google Business Profile. Wrong: A LocalBusiness node for the San Francisco office with no link to its GBP listing. Correct: Every LocalBusiness node includes `'sameAs': 'https://www.google.com/maps/place/[place-id]/'` or the GBP URL specific to that location. This is how search engines cross-reference your schema with real business data and verify your NAP, hours, photos, and reviews. Without it, the schema is unverified.
Implementing LocalBusiness for a Multi-Office Firm: Step-by-Step
Here is a practical implementation guide for a multi-office law firm.
Step 1: Audit your offices and establish naming conventions. List every office: city, state, address, phone (local or main line), hours (if different from others). Decide on your @id naming scheme. Example: `'#office-' + city.toLowerCase().replace(' ', '-')`. So San Francisco is `'#office-san-francisco'`, Los Angeles is `'#office-los-angeles'`. Document this in your schema style guide.
Step 2: Declare the top-level Organization node once. In your root layout (footer, header, or a shared schema component), declare one Organization node with @id `'#organization'`. Include `name` (the official firm name), `logo` (the firm logo URL, root-relative), `address` (headquarters address, byte-identical to GBP firm listing if one exists), `telephone` (firm main line), `email`, `sameAs` (firm GBP listing, Avvo, LinkedIn, etc.), `founder` or `member` (references to senior attorney Person nodes by @id), and any other firm-wide info. This node is referenced but never re-declared elsewhere.
Step 3: Create one LocalBusiness node per office page. On `/office/san-francisco`, `/office/los-angeles`, etc., declare a LocalBusiness node with @id `'#office-san-francisco'`, etc. Include: `@type: 'LocalBusiness'` (or more specifically, 'LawFirm', which inherits from LocalBusiness); `name` (firm name or 'Firm Name — City'); `address` (the full, real, byte-identical address for that office, matching GBP); `telephone` (local number if available, otherwise firm main line); `geo` (a Point object with real `latitude` and `longitude`); `openingHours` (if available, using BusinessHours schema: days and times); `image` (office photo or firm photo); `url` (the canonical URL of the office page); `provider` ({'@id': '#organization'}, not a full Organization node); and if the office has GBP reviews, `aggregateRating` ({'ratingValue': X, 'ratingCount': Y, 'reviewCount': Y}, matching real GBP data). Also include `sameAs` with the GBP URL for that specific location.
Step 4: Link attorneys to the firm via Person nodes. On attorney bio pages and in your site footer/nav, declare Person nodes for each attorney. Include @id (e.g., '#attorney-john-smith'), `name`, `sameAs` (LinkedIn, bar profile), `worksFor` ({'@id': '#organization'}, referencing the firm by @id, not re-declaring it), `hasCredential` (bar admission, if public), `knowsAbout` (practice areas they handle). On the attorney's page, you can also include `officeLocation` ({'@id': '#office-san-francisco'}) if the attorney is based in one office.
Step 5: Test and validate. Run your schema through Google's Schema.org Validator (structured-data syntax check) and Google's Rich Results Test (rich-result eligibility). Fix warnings, even minor ones (e.g., dates must be full ISO 8601 with timezone). Use GEO audit tools to verify that your entity graph is coherent: every LocalBusiness node references the same Organization @id; every Person node's worksFor points to that same @id; every office's address and phone are byte-identical to their GBP listing.
Step 6: Connect to Google Business Profile. In Google Business Manager, claim or create a GBP listing for each office. Ensure the address, phone, hours, and photos match your schema exactly (byte-identical NAP is mandatory). Once claimed, Google will provide a URL for each listing (e.g., `https://www.google.com/maps/place/law-firm-sf/...`). Add that URL as the `sameAs` value in your LocalBusiness schema. This link is what allows search engines to validate your schema against real business data.
LocalBusiness + Google Business Profile: The Critical Bridge
Google Business Profile (GBP) is the authoritative source for local business information: address, hours, phone, reviews, photos, and opening status. When you claim or create a GBP listing, Google assigns it a unique ID and URL. This listing is what appears in Google Maps, Google Search local pack, and local 3-pack results.
Your schema's job is to mirror and reinforce GBP. Specifically: (1) The address and phone in your LocalBusiness node must be byte-identical to GBP (character-for-character, including abbreviations, spacing, punctuation). Any mismatch confuses search engines about whether you are the same business. (2) The review aggregate (`aggregateRating`) must come from GBP, not from your own website or a third-party review site. Google owns GBP review data and trusts it; your own "5-star reviews" are self-serving. (3) The hours (`openingHours`) should match GBP if you publish them. (4) The location URL in your schema's `sameAs` field should point to the GBP listing for that specific office, not to the firm homepage or a generic Maps search.
Why this matters: Google uses GBP as ground truth. When a user searches for your office, Google checks: (1) Does this website's schema claim to be the business at this address? (2) Does GBP have a listing for this address? (3) Do they match? If your schema address is "123 Main Street, Suite 400" but GBP says "123 Main St Unit 400", that mismatch lowers trust. If your schema claims 50 reviews with a 4.8 rating but GBP shows 30 reviews with 4.5 stars, search engines may suppress your snippet or mark you as potentially deceptive.
For AI search engines (Perplexity, Gemini, Claude), GBP is a signal of legitimacy. RAG systems (retrieval-augmented generation) weight sources more heavily if they appear verified across multiple trusted sources. A firm with matching schema and GBP data is ranked higher in retrieval than a firm with only schema or only GBP.
Implementation: (1) Claim or create a GBP listing for each office. (2) Complete all mandatory fields: address, phone, hours, at least one photo. (3) After Google verifies the listing, copy the GBP URL from the "Business details" page (the full Maps URL, or the short code if one is available). (4) In your LocalBusiness schema, set `'sameAs': '[gcp-url]'` and `'url': '[your-canonical-page-url]'`. (5) Set `aggregateRating.ratingValue` and `aggregateRating.ratingCount` to match GBP's data exactly. (6) Update GBP immediately if your office's address, phone, or hours change, then update your schema to match. (7) Disable GBP posting or ensure any posts on GBP (updates, offers, photos) are professional and consistent with your brand.
Testing, Validation, and Ongoing Maintenance
Schema does not ship and stay stable. As your firm grows (new offices, new attorneys, office closures), your entity graph must evolve. Here is a maintenance checklist.
Before publishing: Run schema through validator.schema.org for syntax errors and warnings. Use Google Rich Results Test to check eligibility (this tool checks for rich-result types; LocalBusiness can be eligible for "Local Business" rich results if all required fields are present). Manually verify that (a) the Organization @id is referenced, not re-declared, on every page; (b) every LocalBusiness node has a unique, consistent @id; (c) every office's NAP is byte-identical to its GBP listing; (d) geo-coordinates are real and match the actual building address (use Google Maps to confirm); (e) aggregateRating data matches GBP.
Quarterly (or on office/attorney changes): Audit your schema against your current business. Add new LocalBusiness nodes for new offices. Remove nodes for closed offices (use a 301 redirect on the old URL, not a 404). Update attorney Person nodes when attorneys join or leave. Verify that GBP listings still match your schema (hours may change seasonally, reviews update continuously, addresses might change).
Annually: Run a full entity-graph audit. Check that all @id references resolve (no orphan nodes). Verify that the Organization node's sameAs links still work (directories update, URLs sometimes move). Check that GBP data still matches your schema. Update coordinates if any office has moved. Confirm that the firm's Google Business Profile (not the office profiles) is also claimed and up-to-date.
A common technical check: use a schema extraction tool to crawl your site and extract all declared @ids and references. Verify that (1) there is exactly one Organization @id; (2) there are N LocalBusiness @ids (one per office); (3) every LocalBusiness's provider points to the Organization @id; (4) every attorney Person's worksFor points to the Organization @id. Any orphans or inconsistencies will surface immediately.

