Medical Practice Schema Markup: JSON-LD for Every Physician Specialty
I've built physician directory sites for multi-location health systems, solo dermatology practices, and everything in between. And the single biggest missed opportunity I see across healthcare websites is structured data. Not the generic LocalBusiness kind -- I'm talking about the specific Physician and MedicalSpecialty schema types that tell Google exactly what a provider does, where they practice, and why patients should trust them.
Most medical practice websites ship with zero schema markup or, worse, incorrect markup generated by some WordPress plugin that treats a cardiology practice the same as a pizza shop. This guide gives you production-ready JSON-LD for real medical specialties -- cardiology, pediatrics, urology, orthopedics, and more -- along with the reasoning behind every property choice.
Table of Contents
- Why Medical Schema Markup Matters in 2026
- Schema.org Types for Medical Practices
- Understanding MedicalSpecialty Enumeration Values
- Base Physician JSON-LD Template
- Specialty-Specific JSON-LD Examples
- MedicalOrganization and MedicalClinic Schema
- Connecting Physicians to Organizations
- Properties That Build E-E-A-T
- Validation and Testing
- Common Mistakes to Avoid
- FAQ

Why Medical Schema Markup Matters in 2026
Google's AI Overviews now dominate healthcare queries. When a patient searches "cardiologist accepting new patients near me," Google doesn't just scan your page text anymore -- it reads your structured data to build entity relationships, populate knowledge panels, and feed AI-generated answers.
Here's what proper medical schema markup can trigger:
| Rich Result Type | Triggered By | Impact |
|---|---|---|
| Review snippets | AggregateRating on Physician |
Star ratings in SERPs |
| Knowledge panel | Physician + sameAs links |
Brand presence for named doctors |
| Local pack enhancement | MedicalClinic + openingHours |
Map listing details |
| FAQ rich results | FAQPage schema (healthcare sites still eligible) |
Expanded SERP real estate |
| AI Overview citations | Complete entity graph | Cited as source in AI answers |
A 2025 BrightLocal study found that healthcare listings with review snippets saw 37% higher click-through rates than those without. And Schema App's research confirmed that physician pages with complete structured data were 2.8x more likely to appear in AI Overview citations.
The bottom line: if your physician pages don't have proper schema markup, you're leaving visibility on the table.
Schema.org Types for Medical Practices
Before we write any code, let's understand the type hierarchy. Schema.org gives us several types that are relevant to healthcare, and picking the right one matters.
Thing
└── Organization
└── LocalBusiness
└── MedicalBusiness
├── MedicalClinic
├── Optician
├── Pharmacy
└── Physician
Physician is a subtype of MedicalBusiness, which is itself a subtype of LocalBusiness. This is important because it means a Physician entity inherits all the local business properties (address, hours, geo coordinates) plus medical-specific properties like medicalSpecialty and hospitalAffiliation.
For a solo practitioner's website, Physician is your primary type. For a multi-provider clinic, you'll want a MedicalClinic or MedicalOrganization as the parent entity with individual Physician entries nested or linked via member or referenced on separate pages.
When to Use What
| Your Setup | Primary Type | Secondary Types |
|---|---|---|
| Solo doctor, one location | Physician |
MedicalOrganization (optional) |
| Group practice, single location | MedicalClinic |
Physician per provider page |
| Health system, multiple locations | MedicalOrganization |
MedicalClinic per location, Physician per provider |
| Hospital | Hospital |
Physician, MedicalClinic (departments) |
Understanding MedicalSpecialty Enumeration Values
The medicalSpecialty property accepts values from Schema.org's MedicalSpecialty enumeration. These aren't free-text fields -- they're specific, predefined values. Here are the ones you'll use most often:
| Specialty | Schema.org Value | Notes |
|---|---|---|
| Cardiology | Cardiovascular |
Covers cardiology and cardiovascular surgery |
| Pediatrics | Pediatric |
General pediatrics |
| Urology | Urologic |
Includes pediatric urology |
| Dermatology | Dermatology |
Also DermatologyLaserSurgery variant |
| Orthopedics | Musculoskeletal |
Orthopedic surgery falls here |
| Neurology | Neurological |
Distinct from Psychiatric |
| Obstetrics & Gynecology | Obstetric and/or Gynecologic |
Can specify both |
| Emergency Medicine | Emergency |
ER physicians |
| Family Medicine | PrimaryCare |
General practice, family medicine |
| Oncology | Oncologic |
Cancer treatment |
| Pulmonology | Pulmonary |
Lung and respiratory |
| Gastroenterology | Gastroenterologic |
GI specialists |
| Endocrinology | Endocrine |
Diabetes, thyroid, etc. |
| Ophthalmology | Optometric |
Eye care |
| Psychiatry | Psychiatric |
Mental health physicians |
A full list is available at schema.org/MedicalSpecialty. Note that some specialties map to broader categories than you might expect -- there's no separate "Interventional Cardiology" value, for instance. You'd use Cardiovascular and describe the subspecialty in the physician's description or knowsAbout properties.

Base Physician JSON-LD Template
Here's a solid starting template for any physician page. I'll annotate the key decisions.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Physician",
"@id": "https://example.com/doctors/jane-smith#physician",
"name": "Dr. Jane Smith, MD, FACC",
"image": "https://example.com/images/dr-jane-smith.jpg",
"url": "https://example.com/doctors/jane-smith",
"telephone": "+1-555-867-5309",
"description": "Board-certified cardiologist specializing in interventional cardiology and structural heart disease with 15 years of clinical experience.",
"medicalSpecialty": "https://schema.org/Cardiovascular",
"isAcceptingNewPatients": true,
"address": {
"@type": "PostalAddress",
"streetAddress": "450 Heart Center Drive, Suite 200",
"addressLocality": "Austin",
"addressRegion": "TX",
"postalCode": "78701",
"addressCountry": "US"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": 30.2672,
"longitude": -97.7431
},
"openingHoursSpecification": [
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
"opens": "08:00",
"closes": "17:00"
}
],
"sameAs": [
"https://www.healthgrades.com/physician/dr-jane-smith",
"https://www.doximity.com/pub/jane-smith-md",
"https://www.linkedin.com/in/drjanesmith"
],
"alumniOf": {
"@type": "CollegeOrUniversity",
"name": "Johns Hopkins University School of Medicine"
},
"hasCredential": [
{
"@type": "EducationalOccupationalCredential",
"credentialCategory": "Board Certification",
"name": "American Board of Internal Medicine - Cardiovascular Disease"
}
],
"hospitalAffiliation": {
"@type": "Hospital",
"name": "St. David's Medical Center",
"url": "https://stdavids.com"
},
"worksFor": {
"@type": "MedicalOrganization",
"@id": "https://example.com/#organization",
"name": "Austin Heart Specialists"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.8",
"reviewCount": "142",
"bestRating": "5"
}
}
</script>
A few things I want to call out:
@id: This creates a unique identifier for this entity. Use the page URL plus a fragment. This becomes critical when you link physicians to organizations across pages.medicalSpecialty: I'm using the full URL form (https://schema.org/Cardiovascular) rather than just the string. Both work, but the URL form is more explicit.isAcceptingNewPatients: This is a newer property specific toPhysician. Use it. Patients search for this constantly.sameAs: Link to verified third-party profiles. This is how Google builds entity confidence and potentially triggers knowledge panels.aggregateRating: Only include this if you display reviews visibly on the page. Google's guidelines are strict on this -- hidden ratings violate their policies.
Specialty-Specific JSON-LD Examples
Let's build out examples for the specialties people actually search for.
Cardiology (Cardiovascular)
{
"@context": "https://schema.org",
"@type": "Physician",
"name": "Dr. Robert Chen, MD, FACC",
"medicalSpecialty": "https://schema.org/Cardiovascular",
"knowsAbout": [
"Interventional Cardiology",
"Cardiac Catheterization",
"Coronary Artery Disease",
"Heart Failure Management",
"Echocardiography"
],
"availableService": [
{
"@type": "MedicalProcedure",
"name": "Cardiac Catheterization",
"procedureType": "https://schema.org/PercutaneousProcedure"
},
{
"@type": "MedicalTest",
"name": "Stress Echocardiography"
},
{
"@type": "MedicalTest",
"name": "Electrocardiogram (ECG/EKG)"
}
]
}
The availableService property is unique to Physician and lets you specify exact procedures and tests. For cardiology, this distinction between MedicalProcedure and MedicalTest maps naturally to the interventional vs. diagnostic split.
Pediatrics
{
"@context": "https://schema.org",
"@type": "Physician",
"name": "Dr. Maria Gonzalez, MD, FAAP",
"medicalSpecialty": "https://schema.org/Pediatric",
"knowsAbout": [
"Well-Child Visits",
"Childhood Immunizations",
"Developmental Screening",
"Adolescent Medicine",
"Newborn Care"
],
"availableService": [
{
"@type": "MedicalTherapy",
"name": "Pediatric Vaccination Program"
},
{
"@type": "MedicalTest",
"name": "Developmental Milestone Assessment"
}
],
"hasCredential": [
{
"@type": "EducationalOccupationalCredential",
"credentialCategory": "Board Certification",
"name": "American Board of Pediatrics"
}
]
}
Pediatrics is interesting because the MedicalSpecialty value is just Pediatric -- there's no separate value for pediatric subspecialties like pediatric cardiology. For a pediatric cardiologist, you'd include both specialties:
"medicalSpecialty": [
"https://schema.org/Pediatric",
"https://schema.org/Cardiovascular"
]
Yes, medicalSpecialty accepts an array. Use it.
Urology
{
"@context": "https://schema.org",
"@type": "Physician",
"name": "Dr. Michael Torres, MD, FACS",
"medicalSpecialty": "https://schema.org/Urologic",
"knowsAbout": [
"Robotic-Assisted Surgery",
"Prostate Cancer Treatment",
"Kidney Stone Management",
"Urinary Incontinence",
"Male Infertility"
],
"availableService": [
{
"@type": "MedicalProcedure",
"name": "Robot-Assisted Laparoscopic Prostatectomy",
"procedureType": "https://schema.org/SurgicalProcedure"
},
{
"@type": "MedicalProcedure",
"name": "Extracorporeal Shock Wave Lithotripsy (ESWL)",
"procedureType": "https://schema.org/NoninvasiveProcedure"
},
{
"@type": "MedicalTest",
"name": "Cystoscopy"
}
]
}
Orthopedics
{
"@context": "https://schema.org",
"@type": "Physician",
"name": "Dr. Sarah Kim, MD",
"medicalSpecialty": "https://schema.org/Musculoskeletal",
"knowsAbout": [
"Total Joint Replacement",
"Sports Medicine",
"Arthroscopic Surgery",
"Fracture Care",
"Rotator Cuff Repair"
],
"availableService": [
{
"@type": "MedicalProcedure",
"name": "Total Knee Arthroplasty",
"procedureType": "https://schema.org/SurgicalProcedure"
},
{
"@type": "MedicalProcedure",
"name": "Arthroscopic ACL Reconstruction",
"procedureType": "https://schema.org/SurgicalProcedure"
}
]
}
Notice the medicalSpecialty value is Musculoskeletal, not "Orthopedic." This trips people up constantly.
MedicalOrganization and MedicalClinic Schema
Your organization-level schema is the foundation. Every physician entity should reference back to it. Here's a complete MedicalClinic example:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "MedicalClinic",
"@id": "https://example.com/#clinic",
"name": "Austin Heart Specialists",
"url": "https://example.com",
"logo": "https://example.com/images/logo.png",
"image": "https://example.com/images/clinic-exterior.jpg",
"telephone": "+1-555-867-5309",
"medicalSpecialty": "https://schema.org/Cardiovascular",
"address": {
"@type": "PostalAddress",
"streetAddress": "450 Heart Center Drive",
"addressLocality": "Austin",
"addressRegion": "TX",
"postalCode": "78701",
"addressCountry": "US"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": 30.2672,
"longitude": -97.7431
},
"openingHoursSpecification": [
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
"opens": "07:30",
"closes": "17:30"
}
],
"availableService": [
{
"@type": "MedicalTest",
"name": "Cardiac Stress Testing"
},
{
"@type": "MedicalProcedure",
"name": "Cardiac Catheterization"
}
],
"member": [
{
"@type": "Physician",
"@id": "https://example.com/doctors/jane-smith#physician"
},
{
"@type": "Physician",
"@id": "https://example.com/doctors/robert-chen#physician"
}
],
"sameAs": [
"https://www.facebook.com/austinheartspecialists",
"https://www.google.com/maps/place/?q=place_id:ChIJ_____"
]
}
</script>
The member array uses @id references to link to physician entities defined on their individual pages. This creates a connected graph that Google can traverse.
Connecting Physicians to Organizations
This is where most implementations fall apart. You need bidirectional references:
- Organization → Physician: Use
memberon the organization page - Physician → Organization: Use
worksForon the physician page
Both should reference the same @id. Think of it like a foreign key in a database. If the IDs don't match, Google can't connect them.
// On the physician page:
"worksFor": {
"@type": "MedicalClinic",
"@id": "https://example.com/#clinic",
"name": "Austin Heart Specialists"
}
// On the organization page:
"member": [
{
"@type": "Physician",
"@id": "https://example.com/doctors/jane-smith#physician"
}
]
For multi-location practices, each location should be a separate MedicalClinic entity with its own @id, and physicians who practice at multiple locations should use practicesAt (an array) instead of worksFor.
If you're building these sites on headless CMS platforms, this kind of relational data modeling maps perfectly to content references in systems like Sanity or Contentful. We handle this exact pattern in our headless CMS development work -- the CMS content model mirrors the schema.org entity graph.
Properties That Build E-E-A-T
Google's quality raters evaluate Experience, Expertise, Authoritativeness, and Trustworthiness for YMYL (Your Money or Your Life) content. Healthcare is the quintessential YMYL category. These schema properties directly support E-E-A-T signals:
| Property | Type | E-E-A-T Signal |
|---|---|---|
hasCredential |
EducationalOccupationalCredential |
Expertise |
alumniOf |
CollegeOrUniversity |
Expertise |
memberOf |
Organization |
Authoritativeness |
hospitalAffiliation |
Hospital |
Authoritativeness, Trust |
award |
Text |
Authoritativeness |
knowsAbout |
Text or Thing |
Expertise |
medicalSpecialty |
MedicalSpecialty |
Expertise |
sameAs |
URL |
Trust (cross-reference verification) |
aggregateRating |
AggregateRating |
Trust (social proof) |
Critical rule: you can only mark up content visible on the page. If you list board certifications in your schema but don't show them anywhere on the physician's profile page, you're violating Google's structured data guidelines. Add the content first, then mark it up.
Validation and Testing
Before you push anything to production:
- Google Rich Results Test (search.google.com/test/rich-results): Validates eligibility for rich results
- Schema Markup Validator (validator.schema.org): Checks syntax and conformance to Schema.org vocabulary
- Google Search Console: Monitor the "Enhancements" section after deployment for any errors
Here's my testing workflow:
# If you're generating schema programmatically (Next.js, Astro, etc.)
# Output the JSON to a file and validate locally first
node -e "const schema = require('./generate-schema.js'); console.log(JSON.stringify(schema, null, 2))" > schema-output.json
# Then paste into validator.schema.org or use their API
If you're building with Next.js or Astro, you can generate these JSON-LD blocks dynamically from your CMS data. Both frameworks handle <script type="application/ld+json"> injection cleanly -- Next.js via the <Script> component or next/head, Astro via its <head> slot.
Common Mistakes to Avoid
Using generic LocalBusiness instead of Physician. You lose all the medical-specific properties. A Physician is a LocalBusiness, so you get everything LocalBusiness offers plus more.
Free-text medicalSpecialty values. Writing "medicalSpecialty": "Cardiology" instead of using the proper enumeration value (Cardiovascular) means Google may not understand the specialty correctly.
Missing geo coordinates. Lat/long data helps Google associate your practice with location-based queries. Don't skip it.
Duplicate @id values across different entities. Every entity needs a unique @id. I've seen implementations where every physician shares the same @id suffix -- that completely breaks the entity graph.
Marking up content that isn't on the page. This bears repeating. Google's guidelines are explicit: everything in your structured data must correspond to visible content. Violations can result in manual actions.
Ignoring isAcceptingNewPatients. This is a high-intent signal. Patients filter by it. Include it and keep it updated.
FAQ
What's the difference between Physician and MedicalBusiness schema types?
Physician is a subtype of MedicalBusiness. Use Physician for individual provider pages and MedicalBusiness (or more specifically, MedicalClinic) for the practice itself. Physician inherits all properties from MedicalBusiness and adds physician-specific ones like hospitalAffiliation, availableService, and isAcceptingNewPatients.
Can I list multiple medical specialties for one physician?
Yes. The medicalSpecialty property accepts an array. A physician who is both a pediatric and cardiovascular specialist can list both: "medicalSpecialty": ["https://schema.org/Pediatric", "https://schema.org/Cardiovascular"]. This is common for dual-boarded physicians.
Does medical schema markup directly improve Google rankings?
Structured data is not a direct ranking factor. However, it enables rich results (star ratings, FAQ dropdowns, knowledge panels) that significantly improve click-through rates. It also feeds Google's entity understanding, which influences how your practice appears in AI Overviews and local pack results. The indirect SEO impact is substantial.
What schema type should I use for a hospital's physician directory?
Use Hospital as your top-level entity, MedicalClinic for individual departments (e.g., Department of Cardiology), and Physician for each provider page. Connect them with member, worksFor, and department properties. This creates a complete organizational graph.
Is JSON-LD the only format for medical schema markup?
No -- you can also use Microdata or RDFa embedded in your HTML. But Google explicitly recommends JSON-LD and it's the format they use in all their documentation examples. JSON-LD is also far easier to maintain because it's separate from your page layout. You can generate it programmatically from CMS data without touching templates.
How do I handle a physician who practices at multiple locations?
Use the practicesAt property with an array of MedicalClinic or Hospital entities, each with its own @id. Don't duplicate the physician entity across locations -- create one canonical Physician entity and link it to multiple practice locations. Each location's schema should also reference the physician via member.
Should I include patient reviews in my physician schema?
Only if the reviews are visibly displayed on the physician's page. Use the aggregateRating property on the Physician type and include ratingValue, reviewCount, and bestRating. Google takes this seriously -- marking up hidden or fake reviews can result in manual penalties. If you collect reviews through third-party platforms like Healthgrades, link to them via sameAs instead.
How often should I update medical practice schema markup?
Update your structured data whenever the underlying information changes: new office hours, a physician leaves or joins, a provider stops accepting new patients, or contact information changes. For practices using a headless CMS with dynamic schema generation, this happens automatically when content editors update the CMS. If you're hardcoding JSON-LD, set a quarterly review cadence at minimum. Stale structured data -- especially incorrect isAcceptingNewPatients or telephone values -- erodes trust with both Google and patients.