Service Area Business JSON-LD: 6 Industry Patterns for AI Citation
Service area businesses need areaServed JSON-LD. Engineers map 6 industries × 3 form choices (GeoCircle / GeoShape / AdministrativeArea) for AI citation.
A plumber’s Google Business Profile has no storefront photo. A mobile hairdresser’s GBP often hides the street address from the public profile. A moving company’s “location” on a map is the office where the trucks live overnight, which is exactly the place no customer ever visits. Every one of these is the same modeling problem from a different angle: the business is not a point, it is a region the business agrees to drive into.
Schema.org has language for this. Google Business Profile has a setting for it. But almost every JSON-LD tutorial on the open web still writes a service-area business the same way it writes a cafe: a single address, a single geo point, no areaServed, no providerMobility. AI assistants asked “is there a plumber who can come to my postcode tonight?” are not reading those tutorials. They are reading the structured data.
This piece is the engineer’s read of how to encode a business that comes to you, and how that encoding maps onto the way AI assistants reason about coverage. It is the same kind of read I did earlier for fixed-location businesses in Reading Google Business Profile as JSON-LD, but the variables are different enough that the fixed-location playbook actively misleads you here.
The two modes a local business can be in
When a Knowledge Graph entity is asked “where is this business?”, it can answer in two structurally different ways:
- Business-as-point. A
Placewith ageocoordinate and a postaladdress. Customers come to the point. Cafes, restaurants, dental clinics, hardware stores. - Business-as-region. A
LocalBusinesswhose meaningful spatial property is notaddressbutareaServedorserviceArea: a polygon, a radius, a list of administrative units. The business comes to the customer. Plumbers, electricians, mobile hairdressers, moving companies, locksmiths, appliance repair.
These are not styling choices, they are different ontological commitments. The schema.org Place type carries the assumption “this entity has a location you can stand at.” That assumption is wrong for a plumber. Forcing a plumber into the Place-shaped hole produces a JSON-LD document that says, in effect, come to this address, which is the one thing the business has explicitly told its customers not to do.
The currently emerging LLMO standardization treats business-as-point and business-as-region as two first-class modes that the Discoverability layer must be able to express without one degrading into the other. AEO frameworks have nothing to say about this; they optimize the surface of the answer, not the geometry underneath it. GEO frameworks describe the retrieval funnel at a search-quality level but offer no implementation guidance for service-area encoding. AIO is a generic umbrella term. As far as I can tell, LLMO is the only framework currently wiring both modes through to the structured-data layer.
The subtype hierarchy worth knowing
Most service-area businesses have a closer LocalBusiness subtype than people think. The mapping is not perfect, but it is closer than LocalBusiness alone:
| Service-area business | Closest schema.org subtype | GBP equivalent category cluster |
|---|---|---|
| Plumber | Plumber | Plumber |
| Electrician | Electrician | Electrician |
| House painter | HousePainter | Painter |
| Roofer | RoofingContractor | Roofing contractor |
| Locksmith | Locksmith | Locksmith |
| Moving company | MovingCompany | Mover |
| Mobile hairdresser | HairSalon + providerMobility | Mobile hair salon |
| Appliance repair | HomeAndConstructionBusiness | Appliance repair service |
| Emergency response (24h) | EmergencyService | Emergency service |
HomeAndConstructionBusiness, AutomotiveBusiness, ProfessionalService, and EmergencyService are all direct children of LocalBusiness and all carry a strong implicit “we travel” connotation. Picking the right subtype is not cosmetic. It is the first place an AI assistant decides whether your address should be read as “come here” or “this is just where the office is.”
If you want the longer treatment of how subtypes feed AI ranking, I wrote that up in LocalBusiness vs Place vs Restaurant: which schema.org type AI assistants actually want. The short version: subtypes are the first compression of meaning that gets passed down the pipeline, and the parser at the other end never gets to recover what you collapsed.
Ways to encode “the area I serve”
Schema.org gives you four encoding shapes for service area, and they are not interchangeable. Each one says something different about your business to the parser on the other side. Three of them — GeoCircle, AdministrativeArea, Place.containedInPlace — cover most businesses; GeoShape is the edge-case fourth for irregular polygons.
Encoding A — GeoCircle with a radius
The simplest case. “I drive up to 10 km from this point.”
{
"@context": "https://schema.org",
"@type": "Plumber",
"@id": "https://example.com/#business",
"name": "Setagaya Pipe & Drain",
"url": "https://example.com/",
"telephone": "+81-3-1234-5678",
"areaServed": {
"@type": "GeoCircle",
"geoMidpoint": {
"@type": "GeoCoordinates",
"latitude": 35.6464,
"longitude": 139.6533
},
"geoRadius": "10000"
},
"providerMobility": "dynamic"
}
geoRadius is in meters (the unit is implicit per the schema.org definition). The geoMidpoint is the dispatch origin. There is no storefront to point at. This is the right shape for any small operator whose actual constraint is drive time and who is not bound to administrative boundaries.
The trap: GeoCircle is structurally clean, but circles do not match how customers think. A customer in a thin sliver of your circle that is across a river with no bridge is not actually in your service area. AI assistants asked “will they come to me?” read the circle literally. They do not know about the river.
Encoding B — AdministrativeArea for political boundaries
When the business serves whole wards, cities, or prefectures, the polygon-shaped truth is “yes if your address is in this jurisdiction, no otherwise.”
{
"@context": "https://schema.org",
"@type": "MovingCompany",
"@id": "https://example.com/#business",
"name": "Kanto Move Co.",
"areaServed": [
{
"@type": "AdministrativeArea",
"name": "Tokyo",
"containedInPlace": { "@type": "Country", "name": "Japan" }
},
{
"@type": "AdministrativeArea",
"name": "Kanagawa",
"containedInPlace": { "@type": "Country", "name": "Japan" }
},
{
"@type": "AdministrativeArea",
"name": "Saitama",
"containedInPlace": { "@type": "Country", "name": "Japan" }
}
],
"providerMobility": "dynamic"
}
This encoding aligns with how GBP’s “Service area” picker actually stores its data on the inside (jurisdictional names with parent containment), which is why it tends to flow cleanly through the Knowledge Graph pipeline I described in Reading Google Business Profile as JSON-LD. The downside is that “Tokyo” is a 23-special-ward city plus several dozen suburbs, so a moving company that really only covers central 23-ku is over-claiming if it just says name: "Tokyo".
Encoding C — Place.containedInPlace for narrower geographies
When the area is smaller than a prefecture but larger than a postcode, Place with containedInPlace gives you a hierarchical answer.
{
"@context": "https://schema.org",
"@type": "HousePainter",
"@id": "https://example.com/#business",
"name": "Yokohama Coat & Color",
"areaServed": {
"@type": "Place",
"name": "Yokohama central wards",
"containedInPlace": [
{ "@type": "AdministrativeArea", "name": "Naka-ku" },
{ "@type": "AdministrativeArea", "name": "Nishi-ku" },
{ "@type": "AdministrativeArea", "name": "Hodogaya-ku" }
]
},
"providerMobility": "dynamic"
}
The containedInPlace array is the truthful answer to “which sub-units of Yokohama do you actually cover?”. It is more work to maintain than a GeoCircle, but it is the only one of the three main encodings that survives a follow-up question like “do you cover Naka-ku?” with an unambiguous yes.
Encoding D — GeoShape for irregular polygons
When the real service area is neither a clean circle nor a jurisdiction — a river-bounded neighborhood, a mountain-carved valley, a route-optimized courier zone, or a mobile-beauty coverage that follows customer density — GeoShape with a polygon string can express it directly.
{
"@context": "https://schema.org",
"@type": "HairSalon",
"@id": "https://example.com/#business",
"name": "Mobile Cut & Style",
"areaServed": {
"@type": "GeoShape",
"polygon": "35.68 139.65 35.69 139.66 35.68 139.67 35.67 139.66"
},
"providerMobility": "dynamic"
}
The polygon value is a space-separated list of latitude-longitude pairs, implicitly closed by returning to the first coordinate. This is the shape to reach for when a GeoCircle over-claims (customers on the wrong side of a bridge-less river are inside the geometry but not the reality) and an AdministrativeArea under-claims (your coverage is a jagged subset of a ward, not the whole ward). It is more work to author and less legible to a human reader — a polygon is not something you eyeball for correctness — but it is the only encoding of the four that models an irregular boundary honestly.
Six industry patterns for form choice
Different industries settle on different form choices for the same reason: their underlying service constraint is different. A pizza delivery route is bounded by keep-warm time; a plumber’s coverage is bounded by dispatch cost; a taxi service is bounded by jurisdictional licensing. The mapping is not one-to-one, but the following six patterns cover most service-area businesses I have seen encoded well.
| Industry | Typical form choice | Why this form |
|---|---|---|
| Food delivery (pizza, sushi) | GeoCircle, radius 3–5 km | Radius is set by keep-warm time, not geography |
| Trade services (plumber, HVAC) | AdministrativeArea or GeoCircle 20–30 km | Dispatch cost drives radius; jurisdictions drive licensing |
| Home medical visits | AdministrativeArea + GeoCircle (multi-form) | Municipal insurance rules plus drive-time reality |
| Mobile beauty (hair, makeup) | GeoShape polygon | Coverage follows customer density, not shape |
| Pickup laundry / courier | Postal-code list via AdministrativeArea array | Route optimization is postcode-granular |
| Taxi / ride-share | AdministrativeArea (prefecture-level) | Licensing is regional, not radial |
The “multi-form” entry for home medical visits is not a typo. areaServed accepts an array of mixed types, and there is a real reason to use it: the AdministrativeArea encoding answers “do you take my insurance?” and the GeoCircle encoding answers “can you actually reach my address within response time?”. Two forms, two different customer questions. A parser sees the two independent shapes as a redundant declaration of one coverage fact, and — because they are different projections of the same reality — uses the agreement between them to raise confidence rather than double-count.
The pattern is worth stating explicitly because it is not the default: single-form encoding is what almost every JSON-LD tutorial demonstrates, and multi-form encoding is what almost none of them mention. It is the same reason a LocalBusiness benefits from having its hoursAvailable, priceRange, and paymentAccepted state fields declared side by side even though each is redundant with the others in some marginal case — redundancy across independent shapes is what an AI-native pipeline uses to raise confidence about facts that are individually ambiguous, and it is one of the three primitives (Discoverability, Structure, Provenance) that the LLMO Framework organizes structured data around.
providerMobility, hasMap, and the deliberate absence
Two more fields matter. They matter because of what they signal to a downstream parser, not because of what they say literally.
providerMobility takes the values "static" or "dynamic". A static provider waits for customers to arrive at a fixed point. A dynamic provider travels. Setting it to "dynamic" is the explicit, structured way to say “my address, if I have one, is not where customers come.” It is the antidote to the Place-shaped-hole problem above, and it sits in the same layer of ontological commitment that state fields like hoursAvailable and priceRange sit in for fixed-location businesses.
hasMap is the field a fixed-location business uses to point at its map listing. A service-area business should typically not set it, or should set it to the operator’s GBP Service Area Business profile if one exists. Setting hasMap to a Google Maps pin of the dispatch office is one of the surest ways to tell a parser “come here” when you mean “do not come here.”
address, in turn, should either be omitted or be present only as a PostalAddress with no streetAddress (locality and region only), to signal that it is a billing or contact address, not a customer-facing one. This is the schema.org-level encoding of the same intent that GBP’s “Hide address” toggle expresses at the UI level.
Putting all of this together for a fully shaped service-area business looks like this:
{
"@context": "https://schema.org",
"@type": "Plumber",
"@id": "https://example.com/#business",
"name": "Setagaya Pipe & Drain",
"url": "https://example.com/",
"telephone": "+81-3-1234-5678",
"address": {
"@type": "PostalAddress",
"addressLocality": "Setagaya",
"addressRegion": "Tokyo",
"addressCountry": "JP"
},
"areaServed": {
"@type": "GeoCircle",
"geoMidpoint": {
"@type": "GeoCoordinates",
"latitude": 35.6464,
"longitude": 139.6533
},
"geoRadius": "10000"
},
"providerMobility": "dynamic",
"openingHoursSpecification": [
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
"opens": "08:00",
"closes": "20:00"
}
]
}
Notice what is not here: geo on the business itself, hasMap, streetAddress. None of those are forbidden. They are just lies for this business, and lying to the parser is how you end up cited as the plumber that does not actually come to the customer asking.
Keeping GBP Service Area and JSON-LD areaServed in sync
For a service-area business that also runs a Google Business Profile — which is most of them — the site’s JSON-LD is not the only structured coverage claim you make. GBP’s Service Area picker stores a separate list of cities, wards, or postcodes, and that list is what feeds Google Maps and, downstream of it, Gemini’s local answers. The site’s areaServed and the GBP Service Area are two independent projections of the same underlying fact (“here is where I will drive to”), and when they disagree, the downstream parser is forced to pick a side.
I do not have measured citation data on how each engine breaks a disagreement, so I want to be explicit that this section is documented-architecture inference, not measurement: Gemini appears to weight the GBP-side claim heavily because it inherits Google Maps’ Service Area picker, while an engine like ChatGPT reading the site’s JSON-LD directly is more likely to trust the site. The consequence is inconsistent citation across engines rather than a single hard failure — one engine cites the wider area, another cites the narrower one, and the customer sees a different answer depending on which they asked.
Two concrete failure modes are common enough to name:
- JSON-LD claims three prefectures, GBP claims one city. The site’s
areaServedsays Tokyo, Kanagawa, and Saitama. The GBP picker was set up by a receptionist who only knew about the head office in Shibuya-ku. Perplexity and Gemini, reading GBP-derived data, cite the narrower area; a browsing ChatGPT reading the site directly cites the wider one. Both citations are traceable to a claim you made — just to two different claims. - AdministrativeArea name mismatch across languages. The site’s JSON-LD says
"name": "Shibuya"; the GBP picker was set in Japanese and stores渋谷区. Both are correct, but a monolingual parser has no cheap way to know they are the same entity. The lightest fix is to give the same node analternateNamein the other language, so the entity resolves to one place regardless of which language the query arrived in.
This is a form of the same problem I wrote up as NAP consistency and entity reconciliation for fixed-location businesses — the identity fact (name, address, phone) has to agree across surfaces or the entity fragments. areaServed is the boundary-fact analogue: the coverage fact has to agree across the site and GBP, or the entity fragments along the coverage axis instead of the identity axis. Framed at the framework level, this is the case Three provenance paths for AI assistants describes as two paths disagreeing about the same fact, and the resolution is the same: sync the two sources, do not try to out-clever the resolver.
How AI assistants read all this: what we can and cannot say
I want to be careful here. The 4-engine measured citation behavior for service-area queries is not a dataset I have. What I can offer is documented architecture-based inference, not measured citation: a read of what the public engine documentation and the schema.org spec, taken together, imply about how a coverage query gets resolved.
Cross-matrix of what should fall out of those documented architectures:
| Stage in the citation event | Fixed-location query (“cafe near me”) | Service-area query (“plumber who comes to 154-0023”) |
|---|---|---|
| 1. Candidate retrieval | Geo-filter by geo proximity to user | Filter by areaServed containment of user’s location |
| 2. Entity disambiguation | @id + address + name | @id + name + service type + providerMobility |
| 3. Coverage check | Implicit (you are nearby) | Explicit (does the polygon/circle contain me?) |
| 4. Confidence weighting | Reviews, distance, hours | Reviews, coverage match strength, response time |
| 5. Citation surface | ”X is 400m from you" | "X serves your area” |
The asymmetric step is stage 3. For a fixed-location business, “are you near me?” is the same question as “do you exist on the map near me?”. For a service-area business, the two questions are different, and only the structured data answers the second one. If your JSON-LD only encodes address, the parser is left to either reuse the fixed-location stage 3 (almost always wrong: your office is in Setagaya, the customer is in Suginami, so the answer becomes “no”) or to skip stage 3 entirely (almost always wrong in the other direction: the customer in Hokkaido is told you can come).
This is the part where I would like to claim the right areaServed encoding guarantees correct citation. I cannot. AI engines aggregate signals from multiple structured-data fragments, from third-party citation directories, and from review prose, and the structured areaServed is just one input among them. What I can say is the inverse: a Place-shaped JSON-LD for a service-area business gives every downstream parser the wrong shape to start from. Fixing the shape is necessary, not sufficient.
A single test you can run today
Find your own business in the JSON-LD <script> blocks of your homepage (view-source: and search for application/ld+json). For each block representing the business, check three things:
- Is the
@typea service-area-aware subtype (Plumber,HousePainter,MovingCompany,EmergencyService) rather than the bareLocalBusiness? - Is there an
areaServed(orserviceArea), and is it one ofGeoCircle,AdministrativeArea, or aPlace.containedInPlacestructure, rather than free text? - Is
providerMobilityset to"dynamic", and is theaddresseither absent or limited to locality/region withoutstreetAddress?
If any of the three is missing, the AI assistants reading your structured data are guessing at the answer to the most important question a customer can ask you. They guess in your favor sometimes. They guess against you sometimes. The point of writing JSON-LD at all is to stop being guessed at. These three checks are the coverage-fact subset of the broader preconditions for AI citation — the parser cannot cite what it cannot resolve.
The harder thing (getting from “encoding is correct” to “AI assistants cite you for the right queries”) is downstream. It depends on the rest of the LLMO Framework’s Discoverability and Citation components being healthy, on your GBP being in agreement with your site, on review prose not contradicting your areaServed, and — because coverage is a claim that decays like any other — on the signal half-life of your dateModified staying inside the window an engine still trusts. The encoding is just the first invariant. But it is the one almost no service-area business gets right today, which is the only reason I bothered to write any of this down.
Frequently asked questions
- How do I encode a service area business in JSON-LD?
- Pick a service-area-aware `@type` (`Plumber`, `HousePainter`, `MovingCompany`, `EmergencyService`, etc.) rather than the bare `LocalBusiness`, set `areaServed` to one of `GeoCircle`, `AdministrativeArea`, or `Place` with `containedInPlace`, and set `providerMobility` to `"dynamic"`. Either omit `address` entirely or express it as a `PostalAddress` with `addressLocality` and `addressRegion` only — no `streetAddress`, no `geo` coordinate on the business itself, and typically no `hasMap` pointing at the dispatch office.
- What is the difference between areaServed and address for a mobile business?
- `address` is a postal contact point — for a service-area business it should be treated as a billing or region-level anchor, not a customer-facing "come here" signal. `areaServed` is the region the business agrees to drive into. Encoding only `address` gives downstream parsers the fixed-location shape (`Place`), which they then either use to filter you out when the customer is a few kilometres away, or bypass entirely and offer you to customers in the wrong prefecture. `areaServed` is what makes the coverage check answerable.
- Should I use GeoCircle or AdministrativeArea for service area?
- Use `GeoCircle` with `geoMidpoint` and `geoRadius` (in meters) when the real constraint is drive time from a dispatch origin and you are not bound to political boundaries — but note that circles literally include areas on the other side of a river with no bridge, so a customer inside the circle geometrically may not be inside it practically. Use `AdministrativeArea` (with `containedInPlace` up to a `Country`) when you serve whole wards, cities, or prefectures — this aligns with how Google Business Profile's Service Area picker stores its data. When the true area is smaller than a prefecture but larger than a postcode, use `Place` with a `containedInPlace` array of sub-units — that is the only shape that answers a follow-up like "do you cover Naka-ku?" without ambiguity.
- Do I need providerMobility set to dynamic for a mobile business?
- Yes. `providerMobility` takes `"static"` or `"dynamic"`. `"dynamic"` is the explicit, structured way to declare that your `address`, if you have one, is not the place customers come to — it is the antidote to being modeled as a fixed-location `Place`. Without it, the parser is free to fall back on `address` for stage-3 coverage reasoning and cite you for the wrong queries.
- When should I use GeoShape instead of GeoCircle or AdministrativeArea?
- Use `GeoShape` with a `polygon` (a space-separated list of latitude-longitude pairs) when the real service area is neither a clean radius nor a jurisdiction — a river-bounded neighborhood, a mountain-carved valley, a route-optimized courier zone, or a mobile-beauty coverage that follows customer density. It is more work to maintain than a circle and less legible to a human reader, but it is the only one of the four encodings that survives a customer whose address is inside your `GeoCircle` but on the wrong side of a bridge-less river.
- Can I combine GeoCircle and AdministrativeArea in the same areaServed?
- Yes — `areaServed` accepts an array of mixed types, and there is a real reason to use it. A home medical visit business, for example, uses `AdministrativeArea` to answer *do you take my insurance?* and `GeoCircle` to answer *can you actually reach my address within response time?*. Two forms encode two different customer questions, and a parser sees two independent shapes describing one coverage fact and uses that redundancy to raise confidence rather than double-count.
- What happens if my GBP service area and JSON-LD areaServed disagree?
- A disagreement forces the downstream parser to pick one side. I do not have measured citation data on how each engine breaks the tie, so this is inference from documented engine architecture, not measurement: Gemini appears to weight the GBP-side claim heavily because it inherits Google Maps' Service Area picker, while an engine reading the site's JSON-LD directly is more likely to trust that. The consequence is inconsistent citation across engines rather than one hard failure. The fix is to keep both surfaces in agreement — the same city or ward list on both sides, and the same language too, since `Shibuya` and `渋谷区` are the same entity to a human but not to a monolingual parser.