SEO
LocalBusiness schema generator (JSON-LD)
By Charles Summers · Updated · Free, no signup
Short answer
LocalBusiness markup is a statement about a real-world entity, which makes it the only type here graded against an external record: your Google Business Profile. Name, address and phone have to agree between the two exactly, character for character, because a disagreement is a reason to trust neither. Beyond that, openingHoursSpecification is the property with the most day-to-day maintenance, areaServed is what you use instead of a street address if customers never visit you, and multi-location businesses need one block per location page with a stable @id, not one block listing every branch.
Use the schema markup generator
Why local business need a different approach
Almost every other schema type describes something that only exists on the page. LocalBusiness describes a shop, a clinic or a workshop that exists in the world, has a door, and is already described in several other databases: your Business Profile, an aggregator or two, whatever a franchise head office publishes, and the listing a previous agency created and forgot. The markup joins that crowd rather than overriding it, and its usefulness depends almost entirely on whether it agrees with the rest.
That makes this a data-consistency job wearing a technical SEO costume. The properties are simple. Getting the same suite number, the same trading name and the same phone number to appear identically across sources you do not all control is the hard part, and it is why the audit that precedes writing this block matters more than the block. A perfectly formed LocalBusiness object contradicting the profile that actually drives the map pack is worse than no markup, because now there are two versions of you.
It runs entirely in your browser. Nothing you type is sent to a server, no account is required, and there is no usage limit, because there is no cost per run to control.
The markup is graded against a record it does not control
The instinct with structured data is to treat the JSON as the source of truth and everything else as downstream. For a local business it is the other way round. Your Business Profile is the record that feeds the map pack, and it is verified by a physical mechanism, a postcard, a phone call, a video, which gives it a kind of evidential weight the markup on your website cannot claim. The markup's job is corroboration: it says the website and the verified listing describe the same organisation.
Which is why exact agreement matters more than completeness. If the profile says "Northfell Outfitters" and the markup says "Northfell Outfitters Kendal, Hillwalking and Climbing Specialists", those are two names for a system trying to resolve one entity. The same applies to unit numbers, to Street versus St, and to whether the phone number is the local line or the tracking number your paid team swapped in last quarter. Call tracking is the single most common cause of NAP drift, because it is introduced by a team who never sees the markup.
The practical routine is boring and effective. Pick the profile as the canonical record, write down its exact name, address and phone, and make the markup match it character for character. Then check the other places you appear: your contact page, your footer, the aggregator listings, and the one directory entry from six years ago that still has the old address. You are not chasing a ranking factor, you are removing reasons for a system to doubt that these records describe one business.
Two structural properties help here and are usually missing. sameAs, carrying the URLs of your profiles elsewhere, is an explicit statement that those records are you rather than a coincidence of names. An @id set to a stable URI, typically your location page URL with a fragment, gives every other block on the site something durable to point at, so a Product or an Event on that page can reference the business rather than restating it.
Opening hours, and the properties that need maintaining
openingHoursSpecification is the only part of a LocalBusiness block that changes routinely, which makes it both the most valuable and the most likely to go stale. Its structure is a list of specifications, each carrying a dayOfWeek array and opens and closes times in 24-hour HH:MM form. Group days that share hours and split out the ones that do not, rather than emitting seven near-identical objects.
- Open around the clock is 00:00 to 23:59. That is the documented convention for a 24-hour operation. Leaving both as 00:00 reads as a zero-length window rather than an all-day one.
- Closed days are omitted, not represented as zero. A day absent from every specification is closed. Inventing a closed entry with equal opens and closes is the reading that causes hours to display wrongly.
- Holiday hours use validFrom and validThrough. A specification carrying those two dates overrides the regular pattern for that window, which is how you express a bank holiday closure or extended December trading without rewriting the base hours.
- Split shifts are two specifications for the same day. A kitchen serving lunch and dinner with a gap between is not one long window, and modelling it as one is how a customer arrives at 4pm to a locked door.
- Times are local to the business. There is no offset in an opening hours specification, which is exactly why the address has to be right: the address is what establishes which local time you mean.
- Whatever you emit has to match the profile and the page. Three sources of hours means three chances to be wrong, and customers only need to be misled once.
No walk-in address, more than one location, and choosing the type
A large share of local businesses have no premises a customer would ever visit. Plumbers, mobile groomers, electricians and consultants work at the customer's address, and the standard advice to publish your address is actively wrong for them, particularly when that address is somebody's home. The vocabulary handles this with areaServed, which takes a geographic scope such as a city, a region or a set of postcodes, and it is the honest way to say we cover this patch rather than we sit at this coordinate.
Get this aligned with the Business Profile rather than deciding it independently. Profiles for service-area businesses can hide the address while retaining the service area, and a site that hides the address in the profile but publishes it in JSON-LD has done exactly the thing the profile setting was meant to prevent. Decide once whether you are an address business or a service-area business, then represent that decision consistently everywhere.
Multi-location is the other structural question and it has a clean answer. One block per location, on that location's own page, each with its own @id, its own address, its own hours and its own phone. What breaks is the shortcut: a single LocalBusiness block on the homepage naming the head office, and a store locator built from a JavaScript widget that emits nothing per branch. Every branch is then invisible as an entity, which is precisely the opposite of the goal. If you have twelve shops, you need twelve indexable location pages, and the markup follows the pages rather than substituting for them.
Finally, subtype selection. LocalBusiness has a deep hierarchy beneath it, and using the most specific accurate type is free precision: Restaurant, Dentist, HomeAndConstructionBusiness, AutoRepair and dozens more. Some subtypes unlock properties that only make sense there, such as servesCuisine and menu for a restaurant or acceptsReservations for anything taking bookings. Picking the specific type costs one word and tells a reader more than every adjective in your description field.
Numbers worth knowing
| Metric | Typical | What it means |
|---|---|---|
| What drives the map pack | the Business Profile, not this markup | The verified profile is the record with evidential weight behind it. The markup corroborates that record from your own site, which is useful, and does not replace it, which people persistently hope it will. |
| Opening hours time format | 24-hour HH:MM, local time | No timezone offset exists on an opening hours specification, so the address is what establishes which local time is meant. 09:00 and 17:30, never 9am or 5.30pm. |
| Open 24 hours convention | 00:00 to 23:59 | The documented way to express an all-day window. Setting opens and closes both to 00:00 reads as a zero-length window and is a common reason hours display wrongly. |
| Correct multi-location pattern | one block per location page | Each branch gets an indexable page, its own address and hours, and a stable @id. A single homepage block naming head office leaves every other branch with no entity at all. |
Mistakes that quietly cost you results
- Publishing the call-tracking number in the markup
- A swapped number is the most common cause of name, address and phone drift, because it is introduced by a paid media team who never see the JSON-LD. Keep the verified line in the markup and in the profile, and let tracking numbers live in the ad platform where they belong.
- Publishing a home address for a service-area business
- If customers never visit, use areaServed with the cities or regions you cover and align that with the service-area setting on the profile. Hiding the address in the profile while publishing it in JSON-LD defeats the point of hiding it.
- Using the generic LocalBusiness type when a specific one exists
- Restaurant, Dentist, AutoRepair and dozens of other subtypes are free precision, and some carry properties that only exist there, such as servesCuisine or acceptsReservations. The specific type describes you better than any sentence in your description field.
- Leaving last year's holiday hours in the block
- A specification with validFrom and validThrough is an override for a window, and once the window passes it is dead weight that eventually contradicts reality. Generate holiday overrides from the same calendar the shop actually uses, and let them expire out of the output.
What does the output look like?
This is the exact output the tool produces from the example inputs. It is generated by the same code that runs when you click the button, so what you see here is what you get.
Frequently asked questions
If the Business Profile drives the map pack, why bother with the markup at all?
Because the profile is one record and your site is another, and the value comes from them agreeing. The markup gives anything reading your website, including systems with no access to profile data, an unambiguous statement of who and where you are. It also gives other blocks on the page something to reference by @id. What it does not do is move you up the map pack, and any agency selling it on that basis is misrepresenting the mechanism.
Should each branch page use LocalBusiness or Organization?
LocalBusiness on each branch page, describing that branch with its own address, hours and phone, and Organization for the parent company on your about or homepage. The relationship can be expressed with parentOrganization or branchOf pointing at the parent's @id. Marking every branch as the parent Organization collapses twelve entities into one and loses exactly the location-level detail the pages exist to publish.
How do I mark up a business inside another business, like a cafe in a garden centre?
The department property exists for this. The host business carries the main block and lists the concession or in-store service as a department, each with its own name and, where they genuinely differ, its own hours and phone. This is common in retail and hospitality, and the alternative people usually reach for, an entirely separate top-level block at the same address, creates two competing businesses at one location.
Does priceRange need to be a currency amount?
No, and a symbolic band is usually more sensible. A string of currency symbols is the conventional shorthand, and a numeric range is acceptable where the business genuinely has one. What causes trouble is a precise figure that reads as a promise, since a customer treating it as a quoted price and finding otherwise is a worse outcome than an approximate band. If nothing meaningful can be said, leave it out rather than inventing a value.
Do I need geo coordinates if the address is complete?
Not usually, and a wrong one is worse than none. Geocoding a complete, well-formed address is something mapping systems already do reliably. Coordinates earn their place when the address is genuinely ambiguous: a large campus, a rural property with no useful street line, an entrance on a different street from the postal address. Supply them as decimal numbers rather than strings, and check them on a map before shipping.
Related free tools
- Schema Markup Generator SEO
- Schema Markup Generator for article pages SEO
- Schema Markup Generator for product pages SEO
- Schema Markup Generator for faq pages SEO
- Schema Markup Generator for events SEO
- Schema Markup Generator for software apps SEO
- Schema Markup Generator for breadcrumbs SEO
- URL Slug Generator SEO
- Readability Scorer SEO
- SEO Meta Tag Generator for ecommerce product pages SEO
- Google Ads Headline Generator for healthcare Paid ads
- Google Ads Headline Generator for education Paid ads
Some links on this site are affiliate links, which means Hacking Demand may earn a commission if you buy through them at no extra cost to you. This does not influence which tools are listed. The tools on this page are free and have no affiliate relationship of any kind.