Skip to the tool
OnPageKit

Local Business Schema Generator

LocalBusiness JSON-LD for a single branch.

a photo of the premises
this branch, not the homepage
international form
PostalAddress
optional
ISO 3166-1 alpha-2
decimal degrees
decimal degrees
openingHoursSpecification
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "LocalBusiness",
  "address": {
    "@type": "PostalAddress",
    "addressCountry": "GB"
  },
  "openingHoursSpecification": [
    {
      "@type": "OpeningHoursSpecification",
      "dayOfWeek": [
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday"
      ],
      "opens": "09:00",
      "closes": "17:00"
    },
    {
      "@type": "OpeningHoursSpecification",
      "dayOfWeek": [
        "Saturday"
      ],
      "opens": "10:00",
      "closes": "16:00"
    },
    {
      "@type": "OpeningHoursSpecification",
      "dayOfWeek": [
        "Sunday"
      ],
      "opens": "00:00",
      "closes": "00:00"
    }
  ]
}
</script>
  • No image. A photo of the premises is what makes a local panel look real.
  • No url. Point it at this branch page, not at the homepage of a multi-branch site.
  • No telephone. It is half of the NAP trio that local ranking is reconciled against.
  • No priceRange. Restaurants and shops without one lose a line the local panel likes to show.
  • No geo coordinates. They are what pins the branch when the street address is ambiguous.

One block describes one address. Everything here stays in this page; nothing is looked up, and no address is sent anywhere.

Everything runs in your browser. Nothing you type is sent to a server.

Found a bug in this tool? Report it.

Share this tool

Marking up a place rather than a page

This local business schema generator describes one physical location: the subtype it belongs to, the PostalAddress in full, the coordinates that pin it, and the hours it is actually open. Every field is checked as you type — the times against a 24-hour clock, the country against ISO 3166-1, the coordinates against the range each axis really has — and nothing you enter leaves the page.

The address is five fields, not one line

Pasting a whole address into streetAddress is the most frequent failure in this markup, and it is invisible because the block still validates. PostalAddress has separate slots for the street, the locality, the region, the postcode and the country, and each one is a key a parser matches on independently. A postcode buried inside a street string is a postcode nobody can index.

addressCountry wants the two-letter ISO 3166-1 code in capitals. For the United Kingdom that is GB. UK looks right, passes any two-letter check, and is not an ISO code at all — the generator says so rather than letting it through quietly.

Coordinates settle what the street cannot

geo is optional and worth the two minutes. Street addresses are ambiguous far more often than people expect: units inside a retail park, businesses on a road that changes name halfway, two premises sharing a door number on opposite sides of a junction. Latitude and longitude in decimal degrees resolve all of it.

Two rules keep it honest. They are only meaningful as a pair, so the generator refuses to emit half a GeoCoordinates node. And they must use a full stop as the decimal separator — a comma is correct in most of Europe and wrong here, which is why the tool rejects 55,937 instead of silently rounding it to a number in the sea.

Opening hours, including the days you are shut

openingHoursSpecification is the property people search this markup for, and the one most often half-filled. Group days that share a span into a single row, give each unusual day its own, and declare the days you are closed rather than leaving them out.

Two spans on the same day is contradiction rather than detail, so a day appearing in two rows is an error here. A closing time earlier than the opening time is not — a bar open from 18:00 until 02:00 is a real span crossing midnight — but it is flagged, because far more often it is a typo nobody looked at again.

Where this has to agree with everything else

The markup does not compete with your Google Business Profile; it corroborates it. That only works while the two say the same thing. If the profile says the shop shuts at five and the JSON-LD says six, you have not added a signal, you have added a contradiction, and the search engine resolves it by trusting the source it verified.

So treat the name, address and phone as one string each, written the same way in the markup, in the page footer, on the profile and in every directory that lists you. Update them in the same sitting when they change. The rest is discipline: one page per branch, one block per page, and no address in a block that is not the address of that page.

Deploy it, then open the Rich Results Test itself on the branch page: what it shows is what Google's crawler extracted, not what your file says. If the branch page also sells things, product schema covers the items; the canonical tag generator keeps the branch from competing with a filtered copy of itself.

Frequently asked questions

Does LocalBusiness markup replace a Google Business Profile?

No, and treating it as a substitute is the mistake that wastes the effort. The profile is what feeds the map pack and the knowledge panel; the markup is corroboration — a second, machine-readable statement of the same facts sitting on your own domain. Where they disagree, the profile usually wins and the disagreement itself costs you confidence.

How do I mark up a chain with several branches?

One page per branch, one block per page, each with its own address, coordinates, telephone and hours. A single block listing five addresses describes a business that exists in five places at once, which is not a thing any parser can resolve. The branch pages then need internal links from a locations index so a crawler can reach every one of them.

Which subtype should I pick?

Pick the most specific one that is unambiguously true, and stop there. Restaurant and Dentist earn nothing extra by themselves, but they let a parser reason about the entity; guessing between two near-identical subtypes to seem more precise is worse than staying on plain LocalBusiness. A café that also sells beans is a CafeOrCoffeeShop, not a Store pretending to be one.

How are days the business is closed declared?

As an explicit specification with opens and closes both set to 00:00. Omitting Sunday entirely is ambiguous: it can read as closed or as forgotten, and a parser has no way to tell which. Tick Closed on the row and the generator writes the 00:00 pair for you.

What does NAP consistency actually mean here?

Name, address and phone written identically everywhere they appear — the markup, the footer, the profile, the directories. Not similar; identical. "Suite 4, 42 Warrender Park Rd" and "42 Warrender Park Road, Suite 4" are two strings, and reconciling them into one business is work the search engine may decline to do.

Related tools

Updated