Structured Data for Local Businesses: A Complete Guide to Schema Markup

Architectural blueprint showing website data structure with brass drafting tools

Google already knows your business exists. The question is whether it understands what you do, where you do it, and why someone should pick you over the 40 other companies in your category.

That understanding doesn't come from your About page. It comes from structured data — machine-readable code that tells search engines (and AI assistants) exactly what your business is, without guessing. And for local businesses in Calgary, getting this right is the difference between showing up in a rich result with your hours, reviews, and services laid out clearly, or getting buried behind a competitor who did.

We've audited hundreds of local business websites over 25 years. The pattern is almost always the same: decent content, reasonable SEO, and zero structured data beyond whatever their theme auto-generated (which is usually broken). It takes a few hours to fix properly. The payoff lasts for years.

What Structured Data Actually Does (and Doesn't Do)

Structured data won't magically push you to position one. Search engines don't use it as a direct ranking factor — Google has said this repeatedly. What it does is make your existing content eligible for rich results: knowledge panels, local packs, FAQ accordions, event listings, star ratings. Those enhanced results get dramatically higher click-through rates. A standard blue link converts at roughly 2-3%. A rich result with ratings, price ranges, and business hours? We've seen CTRs north of 8%.

It also feeds the AI layer. When ChatGPT, Perplexity, or Google's AI Overviews answer a question about businesses in Calgary, they pull from structured data to build those answers. If you haven't told them what you are in a format they can parse, they'll rely on whatever fragments they can scrape. That's not a strategy — it's a coin flip. We wrote about this in detail in our AI search optimization guide, and structured data is the foundation of everything in that piece.

The format we're working with is JSON-LD (JavaScript Object Notation for Linked Data). It sits in a <script> tag in your page's <head>, invisible to visitors but readable by every major search engine and AI crawler. Google recommends it over alternatives like Microdata or RDFa. So do we.

LocalBusiness: Your Foundation Schema

This is the one schema every local business must have. It tells search engines who you are, where you are, when you're open, and how to reach you. Get this wrong and nothing else matters.

Here's a real-world example for a Calgary-based business:

{
  "@context": "https://schema.org",
  "@type": "LocalBusiness",
  "@id": "https://example.com/#business",
  "name": "Canmore Creek Dental",
  "description": "Family dental clinic in southwest Calgary offering general dentistry, cosmetic procedures, and emergency dental care.",
  "url": "https://example.com",
  "telephone": "+1-403-555-0142",
  "email": "[email protected]",
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "4520 Crowchild Trail SW, Unit 210",
    "addressLocality": "Calgary",
    "addressRegion": "AB",
    "postalCode": "T3A 2E4",
    "addressCountry": "CA"
  },
  "geo": {
    "@type": "GeoCoordinates",
    "latitude": 51.0447,
    "longitude": -114.0719
  },
  "openingHoursSpecification": [
    {
      "@type": "OpeningHoursSpecification",
      "dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday"],
      "opens": "08:00",
      "closes": "17:00"
    },
    {
      "@type": "OpeningHoursSpecification",
      "dayOfWeek": "Friday",
      "opens": "08:00",
      "closes": "14:00"
    }
  ],
  "priceRange": "$$",
  "image": "https://example.com/images/clinic-exterior.jpg",
  "sameAs": [
    "https://www.facebook.com/canmorecreekdental",
    "https://www.instagram.com/canmorecreekdental"
  ],
  "areaServed": {
    "@type": "City",
    "name": "Calgary",
    "sameAs": "https://en.wikipedia.org/wiki/Calgary"
  }
}

A few things to notice. The @id field gives this entity a unique identifier that other schemas on your site can reference — we'll use this later. The geo coordinates matter because they help Google verify your location against your Google Business Profile—and if your profile itself needs work, our GBP optimisation guide for Calgary businesses covers that in depth. Don't guess these; look them up.

The areaServed property is one most businesses skip entirely. If you serve multiple communities — say Calgary, Airdrie, Cochrane, and Okotoks — list them all. AI assistants use this when someone asks "find a dentist near Okotoks."

One mistake we see constantly: using the generic LocalBusiness type when a more specific one exists. Schema.org has subtypes for almost every business category. A dentist should use Dentist. A restaurant uses Restaurant. A law firm uses LegalService. An accounting firm uses AccountingService. The more specific type gives search engines more context and makes you eligible for category-specific rich result features.

FAQ Schema: Your Best Quick Win

FAQ schema has the highest effort-to-reward ratio of any structured data type. It takes 20 minutes to add and can immediately generate accordion-style rich results in Google, occupying significantly more visual space on the search results page.

More importantly for 2026, FAQ schema is how AI assistants get their answers about your business. When someone asks ChatGPT "does Canmore Creek Dental do emergency appointments?" and you have an FAQ schema with that exact question answered, you've made it trivially easy for the model to respond accurately.

{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "Do you accept new patients?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Yes, Canmore Creek Dental is currently accepting new patients of all ages. You can book your first appointment by calling 403-555-0142 or using our online booking form."
      }
    },
    {
      "@type": "Question",
      "name": "Do you offer emergency dental appointments in Calgary?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "We reserve same-day appointment slots for dental emergencies Monday through Thursday. Call our office before 3 PM and we'll do our best to see you that day."
      }
    },
    {
      "@type": "Question",
      "name": "What insurance plans do you accept?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "We accept all major Alberta dental insurance plans and submit claims electronically on your behalf. We also offer direct billing for most providers including Blue Cross, Sun Life, Manulife, and Great-West Life."
      }
    }
  ]
}

Write your FAQ answers as if a person is reading them out loud. Because increasingly, something is. Short, direct sentences. Include your city name naturally where it fits. Don't stuff keywords — AI models are trained to detect that, and it degrades the quality of the answer they'll generate from your content.

If you only implement two schemas from this guide, make them LocalBusiness and FAQ. Together, they cover roughly 80% of the information search engines and AI assistants need to accurately represent your business.

Pick five to eight questions your front desk gets asked most often. Those are your FAQ entries. Don't invent questions nobody actually asks — it reads as fake to both humans and machines.

Service Schema: Telling Google What You Sell

Most local businesses offer more than one service, but their website treats everything as an undifferentiated blob of text. Service schema fixes that by giving each offering its own structured definition.

This matters because search queries are getting more specific. People don't search "dentist Calgary" anymore. They search "teeth whitening Calgary southwest" or "Invisalign provider near Marda Loop." If your structured data explicitly defines those as services you offer, with associated descriptions and areas served, you're the answer.

{
  "@context": "https://schema.org",
  "@type": "LocalBusiness",
  "@id": "https://example.com/#business",
  "name": "Canmore Creek Dental",
  "hasOfferCatalog": {
    "@type": "OfferCatalog",
    "name": "Dental Services",
    "itemListElement": [
      {
        "@type": "Offer",
        "itemOffered": {
          "@type": "Service",
          "name": "General Dentistry",
          "description": "Routine dental exams, professional cleanings, fillings, and preventive care for patients of all ages in Calgary.",
          "areaServed": {
            "@type": "City",
            "name": "Calgary"
          },
          "provider": {
            "@id": "https://example.com/#business"
          }
        }
      },
      {
        "@type": "Offer",
        "itemOffered": {
          "@type": "Service",
          "name": "Cosmetic Dentistry",
          "description": "Teeth whitening, porcelain veneers, and dental bonding to improve the appearance of your smile.",
          "areaServed": {
            "@type": "City",
            "name": "Calgary"
          },
          "provider": {
            "@id": "https://example.com/#business"
          }
        }
      },
      {
        "@type": "Offer",
        "itemOffered": {
          "@type": "Service",
          "name": "Emergency Dental Care",
          "description": "Same-day emergency dental appointments for toothaches, broken teeth, and other urgent dental issues in Calgary.",
          "areaServed": {
            "@type": "City",
            "name": "Calgary"
          },
          "provider": {
            "@id": "https://example.com/#business"
          }
        }
      }
    ]
  }
}

Notice the provider field uses the @id reference from the LocalBusiness schema. This connects the service back to the business entity, creating a linked graph of information rather than isolated fragments. Search engines follow these connections.

For businesses with pricing they want to display, you can add priceSpecification to each Offer with a price, priceCurrency, and unitText. But only do this if your pricing is genuinely fixed. Displaying a price range that's outdated or misleading will hurt more than it helps.

We list all seven of our own service areas using this exact pattern in our schema. Each service links back to our business entity, includes Calgary as the area served, and has a description written for both human readers and AI parsers.

Event and Review: Two Schemas Worth Adding

If your business hosts events — classes, workshops, grand openings, seasonal promotions — Event schema gets them into Google's event results with dates, times, and venue details. This is particularly useful for restaurants, fitness studios, and professional services firms that run seminars.

{
  "@context": "https://schema.org",
  "@type": "Event",
  "name": "Free Dental Health Seminar: Kids and Sugar",
  "startDate": "2026-04-15T18:00:00-06:00",
  "endDate": "2026-04-15T19:30:00-06:00",
  "eventAttendanceMode": "https://schema.org/OfflineEventAttendanceMode",
  "eventStatus": "https://schema.org/EventScheduled",
  "location": {
    "@type": "Place",
    "name": "Canmore Creek Dental",
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "4520 Crowchild Trail SW, Unit 210",
      "addressLocality": "Calgary",
      "addressRegion": "AB"
    }
  },
  "organizer": {
    "@id": "https://example.com/#business"
  },
  "offers": {
    "@type": "Offer",
    "price": "0",
    "priceCurrency": "CAD",
    "availability": "https://schema.org/InStock"
  }
}

The startDate uses ISO 8601 format with the timezone offset for Mountain Time (-06:00 in MDT, -07:00 in MST). Get this wrong and your event shows up at the wrong time in Google. We've seen it happen.

For reviews, the rules tightened significantly in 2024. Google now requires that Review schema on a LocalBusiness page only represents first-party reviews — reviews you've collected yourself on your own site with a legitimate review mechanism. You can't just copy your Google reviews into schema markup on your website. If you don't collect reviews directly on your site, skip this schema type and let Google pull review data from your Google Business Profile instead. Adding self-authored reviews disguised as customer reviews is a fast path to a manual action penalty.

What you can do is use aggregateRating if you have a genuine review collection system:

{
  "@type": "LocalBusiness",
  "@id": "https://example.com/#business",
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.8",
    "reviewCount": "127",
    "bestRating": "5"
  }
}

But only if those 127 reviews actually exist on your website. Google checks.

Putting It All Together: Validation and Common Mistakes

Implementation checklist for Calgary local businesses:

  • Use the most specific @type for your business (not generic LocalBusiness if a subtype exists)
  • Include geo coordinates that match your Google Business Profile exactly
  • List every community you serve in areaServed
  • Add FAQ schema with five to eight real questions your customers actually ask
  • Define each service individually with hasOfferCatalog
  • Connect schemas with @id references so entities link together
  • Write all descriptions in natural language, as if spoken aloud
  • Validate everything in Google's Rich Results Test before going live
  • Re-validate after any website redesign or CMS migration — themes and plugins break schema constantly

The validation step is where most businesses stop short. You can write perfect JSON-LD and still have it fail because of a missing comma, a misspelled property, or a type that Google doesn't support for rich results. Run your pages through Google's Rich Results Test and the Schema.org Validator. Fix every error. Warnings are worth reviewing but won't prevent your markup from working.

Common mistakes we find during audits:

Your theme probably auto-generates some schema. It's probably wrong. We've seen WordPress themes output WebSite schema with the business name as the author, Organization schema with placeholder content from the theme demo, and LocalBusiness schema that lists the theme developer's address. Check what's already on your site before adding anything new — duplicating schema types causes conflicts. Use Google's Rich Results Test on your existing pages first.

Missing or incorrect @context. Every JSON-LD block needs "@context": "https://schema.org" at the top. We've debugged schemas where the developer used "http://" instead of "https://" and Google silently ignored the entire block.

Mismatched NAP (Name, Address, Phone). Your schema, your Google Business Profile, your website header, and every directory listing must show the same business name, address, and phone number character for character. "4520 Crowchild Trail SW" is not the same as "4520 Crowchild Tr. S.W." to a machine.

Outdated hours. Schema with incorrect opening hours actively harms you — Google may display those hours in search results, and a customer who shows up to a closed business won't come back. Update your openingHoursSpecification whenever your hours change, including holiday schedules.

If you're building a new site or planning a redesign, structured data should be part of the build from day one, not bolted on after launch. It's the same principle we apply to accessibility — retrofitting costs more and produces worse results than building it in from the start. If your current site needs structured data added or audited, that's something we do regularly.

The businesses that will dominate local search over the next few years aren't the ones spending the most on ads. They're the ones making it easiest for machines to understand exactly who they are and what they offer — and structured data is how you do that.

Sources

Get In Touch

Let's talk about your project.

Whether you have a clear scope or just a rough idea, we'd love to hear from you. Tell us what you need and we'll get back within one to two business days.

Not sure where to start? Take our 2-minute assessment and get a personalized recommendation before reaching out.

Rather see our thinking first? Include your current website in the form and we’ll review it before we reply — your response comes back with the three highest-impact things we’d fix, not a sales pitch. If you’d like to walk through them together after, we’re happy to.

Calgary, Alberta, Canada

Response within 1–2 business days

Tell us about your project

The more detail you share, the better we can understand how to help.