Quick Answer: Service schema markup is JSON-LD describing an intangible offering: a Service object with serviceType, a provider reference to your business @id, areaServed, and optionally hasOfferCatalog. Google shows no rich result for it, but it gives search and AI engines an unambiguous machine-readable statement of what you sell.

Here is the uncomfortable fact most schema tutorials skip: marking up a service page will not earn you stars, prices, or any visible decoration in Google's results. There is no Service rich result. And yet we put Service schema on every money page we build, including every service page on this site. Both things are true because the payoff moved. The systems that now decide whether your plumbing company, law office, or agency gets recommended are reading pages, resolving entities, and paraphrasing what they find. A page that states its offering in structured form is simply harder to misread. This guide gives you the full pattern: the decision logic between schema types, a complete valid example you can adapt, and the mistakes that quietly cancel the effort. In the 12 Vectors framework this is Vector 6, Structure, applied to the pages that pay the bills.

What Service schema is, in one working definition

The Service type on Schema.org describes "a service provided by an organization," which covers nearly everything sold as work rather than as a shippable object: foundation repair, bookkeeping, massage therapy, web design, snow removal, legal representation. The object answers four machine-readable questions. What kind of work is this (serviceType)? Who performs it (provider)? Where is it available (areaServed)? What specific jobs does it include (hasOfferCatalog)?

That last sentence is the whole design. Everything else in the type, and it accepts dozens of properties, is refinement on those four questions. A Service object that answers all four cleanly beats one that answers ten questions vaguely. When a parser, whether Google's or an AI assistant's retrieval layer, hits your drain repair page, the markup lets it file the page as "drain repair, performed by this specific business, available in these specific places" without inferring any of it from prose.

Service vs LocalBusiness vs Product: the decision in three questions

Most implementation confusion on service pages comes from choosing between three types that overlap in casual descriptions but not in meaning.

TypeWhat it describesWhere it belongs
LocalBusinessThe business entity itself: name, address, phone, hours, geoHomepage and contact page; referenced by @id everywhere else
ServiceOne intangible offering that business performsEach individual service page, one Service per page
ProductA tangible or packaged thing with its own price, condition, and availabilityE-commerce and fixed-package pages where Offer review and price rich results apply

Three questions settle any borderline case. First: is the page about who you are or what you do? Who-you-are pages get LocalBusiness or Organization; what-you-do pages get Service. Second: does the offering have a fixed price and a defined deliverable a customer takes away? If yes, Product with an Offer may fit better and actually qualifies for price and review rich results, which Service does not. Third: is the work customized per client? Custom work is the clearest Service signal there is.

The common wrong answer is stamping the full LocalBusiness block, address and hours and all, onto every service page. That tells parsers the same entity lives at eight URLs and says nothing about what any of those pages sell. Keep the business entity defined once and referenced by @id; our LocalBusiness schema guide covers that side of the graph in detail.

A complete, valid Service JSON-LD example

The block below is a full working example for a fictional Ontario plumbing company's drain repair page. It validates as written; swap the names, URLs, and regions for your own. Note the structure: the Service references the business by @id, the business is defined once in the same @graph, and the OfferCatalog itemizes the actual jobs.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "Plumber",
      "@id": "https://example-plumbing.ca/#business",
      "name": "Example Plumbing Co.",
      "url": "https://example-plumbing.ca",
      "telephone": "+15195550142",
      "address": {
        "@type": "PostalAddress",
        "addressLocality": "Brantford",
        "addressRegion": "ON",
        "addressCountry": "CA"
      }
    },
    {
      "@type": "Service",
      "@id": "https://example-plumbing.ca/drain-repair/#service",
      "name": "Drain Repair",
      "serviceType": "Drain repair and drain cleaning",
      "description": "Diagnosis and repair of blocked, collapsed, or root-damaged residential drains, including camera inspection and trenchless options.",
      "provider": { "@id": "https://example-plumbing.ca/#business" },
      "areaServed": [
        { "@type": "City", "name": "Brantford" },
        { "@type": "City", "name": "Paris" },
        { "@type": "City", "name": "Cambridge" }
      ],
      "availableChannel": {
        "@type": "ServiceChannel",
        "serviceUrl": "https://example-plumbing.ca/drain-repair/",
        "servicePhone": "+15195550142"
      },
      "hasOfferCatalog": {
        "@type": "OfferCatalog",
        "name": "Drain repair services",
        "itemListElement": [
          {
            "@type": "Offer",
            "itemOffered": { "@type": "Service", "name": "Camera drain inspection" }
          },
          {
            "@type": "Offer",
            "itemOffered": { "@type": "Service", "name": "Hydro jetting" }
          },
          {
            "@type": "Offer",
            "itemOffered": { "@type": "Service", "name": "Trenchless drain replacement" }
          }
        ]
      }
    }
  ]
}
</script>

Two structural choices deserve a comment. The provider is a bare @id reference, not a repeated business definition, so every service page on the site points at the same entity and parsers can merge the signals. And the @type of the business is Plumber, a LocalBusiness subtype, because the most specific accurate type always beats the generic one. Paste your adapted version into Google's Rich Results Test and the Schema.org validator before it ships; JSON-LD fails silently in production, and a missing comma means the parser sees nothing at all.

The four properties doing the actual work

serviceType is the classification field, and plain language wins here. Write "furnace installation and replacement," not your brand name for the package. Machines match this string against what people ask for, and nobody asks an assistant for your trademarked bundle name.

provider is the connective tissue of the whole graph. Set it to the @id of your Organization or LocalBusiness node and every service page you publish strengthens one entity instead of scattering credit across disconnected blobs. Get the entity definition right once; our Organization schema example shows the reference pattern FD uses.

areaServed declares where the work is available, as City, AdministrativeArea, or GeoCircle objects. For trades that drive to the customer this is the property that maps markup to the market. Keep it honest: listing forty municipalities you have never worked in reads as spam to humans reviewing the page and adds nothing for machines, because the claim has no corroboration anywhere else on the web.

hasOfferCatalog turns "we do plumbing" into an itemized inventory. The property's own definition links a Service to an OfferCatalog, a list of Offer items each wrapping a named sub-service. Build it from your real service menu, keep it to one or two levels of nesting, and update it when the menu changes. Stale catalogues teach machines an outdated version of your business.

The per-service-page pattern for trades and professionals

The architecture that works, for a roofer, an accountant, an RMT, or an agency, is the same shape. One page per distinct service, each carrying one Service object, all pointing at one business entity. The homepage or about page defines the entity in full. Service pages reference it and describe their single offering.

For a foundation repair company that might look like: /services/crack-injection/, /services/basement-waterproofing/, /services/underpinning/, each with its own Service block, its own serviceType string, and its own OfferCatalog naming the jobs inside it. For an accounting firm: /services/corporate-tax/, /services/bookkeeping/, /services/cra-audit-support/. The URL, the H1, the visible copy, and the serviceType should all agree on what the page sells.

That agreement is the part practitioners underweight. In Matt's audits of Ontario trades and professional-services sites, one pattern repeats: the businesses that AI assistants describe accurately are the ones whose schema names the same services, in the same words, as the visible headings. Where the markup says one thing and the page copy says another, the assistants side with the visible copy every time, and the markup becomes dead weight. Schema is a confirmation layer, not a substitute for the page saying what it means.

Professionals get one extra move trades do not: the Person node. A lawyer's or therapist's service pages can set provider to the LocalBusiness while the business's own markup names the practitioner with credentials via a Person object. Credential-bearing entities are exactly what YMYL-adjacent recommendation systems look for when deciding whom to name.

Wiring Service into the rest of your schema graph

A Service object floating alone on a page is legible; a Service object connected into a coherent @graph is convincing. The full stack on a well-built service page is: the Service itself, a provider reference to the business entity, a BreadcrumbList locating the page in the site hierarchy, a WebPage or Article node when the page carries editorial depth, and FAQPage when visible questions and answers exist on the page.

The rule that keeps graphs coherent is single definition, multiple reference. Define the business once, at a stable @id like https://yoursite.ca/#organization, and have every other node point at it. Parsers merge everything that shares an @id, so ten service pages built this way accumulate into one richly-described entity rather than ten thin ones. If you are implementing from scratch, our walkthrough on how to add schema markup covers placement, testing, and deployment for each site platform.

Six errors that cancel the markup

The honest part: what Service schema does and does not get you

Google's structured data documentation defines a fixed gallery of types that produce visible rich results, and Service is not in it. Anyone selling Service markup as a snippet tactic is selling something the documentation contradicts. We do not sell magic ranking dust, so here is the actual return profile.

What you get is entity precision at the layer where recommendations are now assembled. Retrieval systems fetch your page as a candidate; structured statements of serviceType and areaServed reduce the interpretation burden when the reading model decides whether you match the user's request. The Princeton group that formalized generative engine optimization (Aggarwal et al., arXiv:2311.09735) measured visibility lifts of up to 40 percent in generated answers from source-clarity tactics that changed nothing about classic rank. Structured data belongs to the same family: it does not move the old scoreboard, it makes you easier to select and harder to misquote on the new one. Results vary by industry and by how contested your service categories are; markup is one input among many, not a lever that works alone.

The worked example you can inspect right now

Every service page on formativedigital.com carries this exact pattern, which means you do not have to take the article's word for anything. Open the source on our schema markup service page, find the ld+json block, and you will see a Service node with its serviceType, a provider reference to the Formative Digital organization @id, and areaServed declarations, wired into the same @graph shape this guide describes. We build client pages the way we build our own, and publishing the working pattern where anyone can read it is what transparency looks like in practice. The same structure sits behind the research library: define the entity once, connect everything to it, let the accumulated graph do the persuading.

Service schema questions, answered directly

Does Google show a rich result for Service schema?

No. Google has no dedicated rich result for the Service type, and its structured data gallery does not list one. The value sits elsewhere: Service schema clarifies what your business does for the systems that build entity understanding, including the AI engines that paraphrase service pages into recommendations. Treat it as entity infrastructure, not as a snippet play.

Should Service schema go on the homepage or on individual service pages?

On the individual service pages. The homepage carries your Organization or LocalBusiness identity; each service page carries one Service object describing that specific offering, with a provider reference pointing back to the business entity by @id. One page, one service, one clear statement of what is sold there.

Can Service and LocalBusiness schema live on the same page?

Yes, and they should be connected rather than stacked. Put both in a single @graph and set the Service's provider property to the LocalBusiness @id. That tells parsers one business offers this service, instead of presenting two unrelated entities that happen to share a URL.

What does hasOfferCatalog actually do in Service schema?

It enumerates the specific jobs inside a broader service as a machine-readable list. A plumbing Service can carry an OfferCatalog naming drain clearing, water heater replacement, and repiping as itemized offers. Search engines get no rich result from it, but AI systems get an explicit inventory of what the business does, which reduces guesswork when they summarize you.

Do I have to include prices in Service schema?

No. Pricing is optional. If you publish prices on the page, mirror them in the markup with an Offer and PriceSpecification so the two never disagree. If your pricing is quote-based, leave price fields out entirely; an empty or placeholder price is worse than none because it feeds machines a number you never committed to.

If you would rather talk through which types your pages should carry than reverse-engineer it from source code, reach out and we will look at your service architecture together.

Find out what machines currently believe about your services

We check your service pages for schema coverage, validate what is already there, and show you how the major AI engines describe your offerings today. No charge, and a reply within one business day.