Technical

Schema markup for medical clinics, done correctly

Which Schema.org types to use, how to connect them with @graph, and the ten fields that actually move citation frequency.

By Kailesk · · 14 min read

Schema.org is the shared vocabulary that search engines, AI models, and structured data consumers use to understand what a page is about. For a medical clinic, structured data is the difference between being parsed as a generic website and being understood as a weight loss clinic in Austin with two providers offering semaglutide and tirzepatide at a specific price range.

This guide documents the Schema.org markup we ship on every clinic site we rebuild at KailxLabs. It is opinionated about which types to use, how to connect them, and which fields carry the most weight in AI citation.

The ten fields that matter most

Schema.org has several hundred types and thousands of properties. A clinic site does not need most of them. The ten fields below do most of the work.

  1. @type on the clinic declared as both MedicalClinic and LocalBusiness.
  2. name matching the clinic's canonical business name everywhere on the site.
  3. address as a complete PostalAddress object.
  4. telephone in E.164 format, identical to Google Business Profile.
  5. medicalSpecialty naming the specific specialty (WeightLoss, Dermatology, CosmeticDentistry, etc.).
  6. priceRange using the standard $, $$, $$$ notation.
  7. geo as GeoCoordinates with latitude and longitude.
  8. openingHoursSpecification populated, matching the Google Business Profile.
  9. aggregateRating with a count and a value if reviews exist on the domain.
  10. sameAs linking to the clinic's Google Business Profile, Healthgrades, Zocdoc, and primary social profiles.

The canonical @graph for a clinic homepage

Every page ships a single JSON LD block with a connected @graph. Below is the template we start from on every clinic engagement.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": ["MedicalClinic", "LocalBusiness"],
      "@id": "https://yourclinic.com/#organization",
      "name": "Your Clinic Name",
      "url": "https://yourclinic.com",
      "telephone": "+15125551234",
      "priceRange": "$$",
      "medicalSpecialty": ["WeightLoss", "InternalMedicine"],
      "address": {
        "@type": "PostalAddress",
        "streetAddress": "1234 S Congress Ave",
        "addressLocality": "Austin",
        "addressRegion": "TX",
        "postalCode": "78704",
        "addressCountry": "US"
      },
      "geo": {
        "@type": "GeoCoordinates",
        "latitude": 30.2672,
        "longitude": -97.7431
      },
      "openingHoursSpecification": [
        {
          "@type": "OpeningHoursSpecification",
          "dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
          "opens": "09:00",
          "closes": "17:00"
        }
      ],
      "sameAs": [
        "https://g.page/yourclinic",
        "https://www.healthgrades.com/..."
      ]
    },
    {
      "@type": "WebSite",
      "@id": "https://yourclinic.com/#website",
      "url": "https://yourclinic.com",
      "name": "Your Clinic Name",
      "publisher": { "@id": "https://yourclinic.com/#organization" }
    }
  ]
}
</script>

Treatment pages. Drug and MedicalProcedure

A semaglutide treatment page adds a Drug entity. A mole removal page adds a MedicalProcedure. Both reference the parent clinic through @id.

{
  "@type": "Drug",
  "@id": "https://yourclinic.com/semaglutide/#drug",
  "name": "Semaglutide",
  "activeIngredient": "Semaglutide",
  "administrationRoute": "Subcutaneous injection",
  "isAvailableGenerically": false,
  "prescriptionStatus": "PrescriptionOnly",
  "manufacturer": {
    "@type": "Organization",
    "name": "Novo Nordisk"
  },
  "provider": { "@id": "https://yourclinic.com/#organization" }
}

For a procedure page, use MedicalProcedure instead.

{
  "@type": "MedicalProcedure",
  "@id": "https://yourclinic.com/veneers/#procedure",
  "name": "Porcelain Veneers",
  "procedureType": "Cosmetic",
  "bodyLocation": "Teeth",
  "preparation": "Dental consultation and impression",
  "howPerformed": "Custom porcelain shells bonded to the front surface of teeth",
  "followup": "One week post placement check",
  "provider": { "@id": "https://yourclinic.com/#organization" }
}

Provider pages. Person and MedicalProfessional

Each provider gets a dedicated page with a combined Person and MedicalProfessional entity.

{
  "@type": ["Person", "Physician"],
  "@id": "https://yourclinic.com/providers/jane-doe/#person",
  "name": "Jane Doe, MD",
  "jobTitle": "Founding Physician",
  "medicalSpecialty": ["InternalMedicine", "WeightLoss"],
  "worksFor": { "@id": "https://yourclinic.com/#organization" },
  "hasCredential": [
    {
      "@type": "EducationalOccupationalCredential",
      "credentialCategory": "degree",
      "educationalLevel": "MD",
      "recognizedBy": {
        "@type": "Organization",
        "name": "Baylor College of Medicine"
      }
    }
  ]
}

FAQ pages. FAQPage with mainEntity

Any page with a frequently asked questions section must declare a FAQPage entity with each question as a Question and each answer as an Answer.

{
  "@type": "FAQPage",
  "@id": "https://yourclinic.com/#faq",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "What does a semaglutide program cost at this clinic?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "The semaglutide program costs $299 per month and includes weekly provider visits, injections, and two follow up calls."
      }
    }
  ]
}

Reviews. AggregateRating plus individual Review

A clinic with reviews on its own domain ships AggregateRating as a property of the clinic organization, plus individual Review entities where the review content is displayed.

"aggregateRating": {
  "@type": "AggregateRating",
  "ratingValue": "4.9",
  "reviewCount": "87",
  "bestRating": "5",
  "worstRating": "1"
}

Common mistakes that damage citation

Multiple disconnected JSON LD blocks

Several WordPress SEO plugins output separate JSON LD blocks for the site, the organization, the page, and individual elements. Each block is valid in isolation but unconnected from the others. An engine reading the page must reconstruct the relationships. Often it gives up.

The fix is to use a single @graph with @id references connecting every entity. One JSON LD block per page.

Name mismatches across the site

The clinic name in the schema on page A must match the name in the schema on page B. Different names across pages create duplicate entities in the engine's understanding. The engine treats them as separate clinics and dilutes authority across both.

Wrong address type

Using a string for address instead of a PostalAddress object is common and damaging. An engine expecting structured fields gets a line of text it must parse. Always use the PostalAddress object.

Empty or placeholder values

Schema fields populated with Your Clinic Name or city or 000 000 0000 are production bugs. Every field should contain real values or be removed entirely. Placeholder data signals abandonment and damages trust.

Medical specialty as a string rather than enumerated value

Schema.org defines MedicalSpecialty as an enumerated type. Valid values include WeightLoss, Dermatology, CosmeticSurgery, and several dozen others. Using Medical Weight Loss as a string when WeightLoss is the enumerated value reduces the engine's confidence in the declaration.

Testing and validation

Three tools are sufficient to validate schema on every page.

  • Schema.org Validator. validator.schema.org. Catches type errors and malformed JSON.
  • Google Rich Results Test. search.google.com/test/rich-results. Shows which Google rich result features qualify.
  • Manual JSON LD inspection. View page source, search for application/ld+json, read the block. Confirm the entities are connected and the values are real.

How often to update schema

Schema should update whenever the underlying facts change. New provider joins, the Person block ships. Pricing changes, the treatment pages update. Hours change, openingHoursSpecification updates. Annual audit at minimum to verify no stale data persists.

A stale schema declaration (incorrect pricing, outdated hours, departed provider still listed) is worse than missing schema. The engine trusts structured data more than prose. Incorrect structured data is quoted as fact.

Why the work is worth it

Schema markup is the cheapest, fastest, most reliable lever in AI visibility. It costs nothing to ship once the site is built correctly. It compounds across every AI engine simultaneously. It survives redesigns as long as the content structure stays consistent.

A clinic that ships a proper @graph on every page gives every AI engine a precompiled understanding of the clinic. The engines that reward legibility (every modern engine) reward that clinic with more consistent citation. The work is small. The return is disproportionate.

Run the free 48 hour audit to see which schema gaps are costing your clinic citations today.