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.