Skip to content
HotelSEO Lab
← The Lab
Hotel SEO Foundations

The Definitive Hotel JSON-LD Schema Library

Every JSON-LD schema type an independent hotel actually needs — Hotel, LodgingBusiness, HotelRoom, FAQPage, Review, LocalBusiness, and BreadcrumbList — with plain-English explanations and copy-ready code blocks.

HotelSEO LabJuly 1, 2026 12 min read

Most independent hotels treat structured data like a plugin checkbox. Flip it on, forget it, hope Google does something nice. Then they wonder why the OTA listing for their own property outranks them, and why ChatGPT describes their “rooftop bar” that closed in 2019.

Schema markup will not fix bad content or thin authority. What it does is remove ambiguity. It tells a crawler — and increasingly, an AI assistant — exactly what your page is, in a format built for machines instead of marketing copy. That precision is the difference between being quoted correctly and being paraphrased into oblivion. If you have ever wondered why your hotel is invisible to ChatGPT, missing or sloppy structured data is often part of the answer.

This is a working reference. Every schema type below includes what it does, when to use it, and a copy-ready JSON-LD block you can adapt. Swap the placeholder values in [square brackets] for your real data, validate, ship.

The ground rules before you touch a single line

A few things that will save you from the most common self-inflicted wounds:

Inside the fenced code blocks below, curly braces are correct and required — that is JSON. In your visible page copy and headings, never write them. This article follows the same rule.

Animated infographic: hotel json ld schema library

Download this study as a one-page PDF

1. Hotel (and its parent, LodgingBusiness)

This is the anchor. Hotel is a subtype of LodgingBusiness, which is itself a subtype of LocalBusiness. Use the most specific type that fits: Hotel for a hotel, BedAndBreakfast, Motel, Resort, or Hostel where accurate. If you run extended-stay units, LodgingBusiness plus clear room typing often reads better — we go deeper on that in the aparthotel and extended-stay marketing guide.

Put this on your homepage. It is the entity everything else references.

{
  "@context": "https://schema.org",
  "@type": "Hotel",
  "@id": "https://www.example-hotel.com/#hotel",
  "name": "[The Harborview Hotel]",
  "url": "https://www.example-hotel.com/",
  "description": "[A 42-room independent boutique hotel on the waterfront in [your city], with a seasonal restaurant and free parking.]",
  "image": [
    "https://www.example-hotel.com/img/exterior.jpg",
    "https://www.example-hotel.com/img/lobby.jpg"
  ],
  "telephone": "+1-207-555-0142",
  "email": "[email protected]",
  "priceRange": "$$$",
  "currenciesAccepted": "USD",
  "paymentAccepted": "Cash, Credit Card",
  "checkinTime": "15:00",
  "checkoutTime": "11:00",
  "petsAllowed": true,
  "numberOfRooms": 42,
  "starRating": {
    "@type": "Rating",
    "ratingValue": "4"
  },
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "[12 Harbor Way]",
    "addressLocality": "[Portland]",
    "addressRegion": "[ME]",
    "postalCode": "[04101]",
    "addressCountry": "US"
  },
  "geo": {
    "@type": "GeoCoordinates",
    "latitude": "43.6591",
    "longitude": "-70.2568"
  },
  "amenityFeature": [
    {
      "@type": "LocationFeatureSpecification",
      "name": "Free WiFi",
      "value": true
    },
    {
      "@type": "LocationFeatureSpecification",
      "name": "Free parking",
      "value": true
    },
    {
      "@type": "LocationFeatureSpecification",
      "name": "Restaurant",
      "value": true
    }
  ],
  "sameAs": [
    "https://www.facebook.com/examplehotel",
    "https://www.instagram.com/examplehotel"
  ]
}

The starRating here is a self-declared or official classification, not a guest review score — do not confuse the two. The amenityFeature array is doing quiet, heavy lifting: it is exactly the structured signal an AI assistant reaches for when a traveler asks “which hotels in [your city] allow pets and have free parking.” That answerability is the whole game we cover across AI visibility, AEO and GEO.

2. HotelRoom (and Offer for pricing)

Each distinct room type deserves its own page and its own HotelRoom markup, tied back to the parent hotel through containedInPlace. Wrap price and availability in an Offer. Only include an Offer price if a comparable price is genuinely visible on the page.

{
  "@context": "https://schema.org",
  "@type": "HotelRoom",
  "name": "[Harbor King Room]",
  "description": "[A 320 sq ft room with a king bed, harbor-facing window, and walk-in shower.]",
  "bed": {
    "@type": "BedDetails",
    "typeOfBed": "King",
    "numberOfBeds": 1
  },
  "occupancy": {
    "@type": "QuantitativeValue",
    "maxValue": 2,
    "unitText": "person"
  },
  "amenityFeature": [
    {
      "@type": "LocationFeatureSpecification",
      "name": "Air conditioning",
      "value": true
    },
    {
      "@type": "LocationFeatureSpecification",
      "name": "Coffee maker",
      "value": true
    }
  ],
  "containedInPlace": {
    "@type": "Hotel",
    "@id": "https://www.example-hotel.com/#hotel"
  },
  "offers": {
    "@type": "Offer",
    "price": "249.00",
    "priceCurrency": "USD",
    "availability": "https://schema.org/InStock",
    "url": "https://www.example-hotel.com/rooms/harbor-king",
    "priceSpecification": {
      "@type": "PriceSpecification",
      "price": "249.00",
      "priceCurrency": "USD",
      "validThrough": "2026-12-31"
    }
  }
}

Notice the containedInPlace points to the same @id you defined on the homepage. That is the reference-do-not-repeat rule in action. When your own room pages carry clean price and availability markup, you make it far easier for search and AI to surface your direct rate instead of defaulting to the OTA — which is precisely the leverage behind the book-direct math on OTA commission.

3. FAQPage

FAQ schema marks up genuine questions and answers that appear on the page. It is one of the most useful structured data types for AEO because it hands an assistant pre-packaged question-answer pairs. The rule is strict: the questions and answers must be visible on the page, must be real FAQs, and must not be promotional or used to game rich results.

{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "What time is check-in and check-out?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Check-in is from 3:00 PM and check-out is by 11:00 AM. Early check-in and late check-out are subject to availability."
      }
    },
    {
      "@type": "Question",
      "name": "Is parking free at the hotel?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Yes. On-site self-parking is complimentary for all overnight guests, with no reservation required."
      }
    },
    {
      "@type": "Question",
      "name": "Do you charge a resort fee?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "No. Our published rate is the rate you pay, plus applicable state and local taxes. We do not add a resort or destination fee."
      }
    }
  ]
}

That resort-fee answer is not a throwaway. Fee transparency is a live legal and trust issue, and being explicit about it is both good practice and good content — see our breakdown of the junk-fee law and resort fees. If you want to preempt the tax question travelers actually ask, the hotel occupancy tax by state explainer is the honest answer to link.

4. Review and AggregateRating

This is where hotels get themselves in trouble. Review markup must reflect real, verifiable reviews that a guest can see on the page. Never fabricate ratings, never aggregate scores you cannot show, and never mark up reviews sourced only from a third-party platform as if they were on your own page. Done honestly, it can qualify you for a star rating in results.

{
  "@context": "https://schema.org",
  "@type": "Hotel",
  "@id": "https://www.example-hotel.com/#hotel",
  "name": "[The Harborview Hotel]",
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.6",
    "reviewCount": "318",
    "bestRating": "5",
    "worstRating": "1"
  },
  "review": [
    {
      "@type": "Review",
      "author": {
        "@type": "Person",
        "name": "[Dana R.]"
      },
      "datePublished": "2026-05-14",
      "reviewRating": {
        "@type": "Rating",
        "ratingValue": "5",
        "bestRating": "5"
      },
      "reviewBody": "[Spotless room, harbor view was worth every dollar, and the front desk arranged a late checkout without a fuss.]"
    }
  ]
}

Attach aggregateRating to your existing Hotel entity by reusing the same @id rather than declaring a second, conflicting hotel object. If your reviews live mostly on external platforms, the honest move is to feature genuine on-page testimonials and let the third-party profiles do their own work — a workflow we handle inside content and reputation.

5. LocalBusiness fields (the local layer)

Because Hotel inherits from LocalBusiness, you do not need a separate LocalBusiness block competing with your hotel entity. Instead, make sure the local-critical properties live on the hotel: consistent NAP (name, address, phone), openingHoursSpecification, and geo. Consistency between this markup and your Google Business Profile is what earns trust.

{
  "@context": "https://schema.org",
  "@type": "Hotel",
  "@id": "https://www.example-hotel.com/#hotel",
  "name": "[The Harborview Hotel]",
  "openingHoursSpecification": [
    {
      "@type": "OpeningHoursSpecification",
      "dayOfWeek": [
        "Monday", "Tuesday", "Wednesday", "Thursday",
        "Friday", "Saturday", "Sunday"
      ],
      "opens": "00:00",
      "closes": "23:59"
    }
  ],
  "department": {
    "@type": "Restaurant",
    "name": "[The Harborview Kitchen]",
    "servesCuisine": "New England seafood",
    "openingHoursSpecification": {
      "@type": "OpeningHoursSpecification",
      "dayOfWeek": ["Wednesday", "Thursday", "Friday", "Saturday"],
      "opens": "17:00",
      "closes": "22:00"
    }
  }
}

A front desk is typically staffed around the clock, hence the full-day hours, but your restaurant or bar is a department with its own hours — model it that way so an assistant does not tell a guest the kitchen is open at 2 AM. The NAP in your markup must match your profile exactly; mismatches are a quiet ranking tax. The full workflow lives in the Google Business Profile playbook and our local SEO and GBP service.

6. BreadcrumbList

Breadcrumbs tell search engines your site hierarchy and can produce the breadcrumb trail shown in results instead of a raw URL. Cheap to add, genuinely useful on room and destination pages.

{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position": 1,
      "name": "Home",
      "item": "https://www.example-hotel.com/"
    },
    {
      "@type": "ListItem",
      "position": 2,
      "name": "Rooms",
      "item": "https://www.example-hotel.com/rooms"
    },
    {
      "@type": "ListItem",
      "position": 3,
      "name": "Harbor King Room",
      "item": "https://www.example-hotel.com/rooms/harbor-king"
    }
  ]
}

The name values should match the anchor text a visitor actually sees, and the final crumb should be the current page. This same hierarchy logic is what makes destination and area pages rank — the deep version is in the OTA destination landers mega-guide.

Putting it together without breaking anything

You do not deploy all seven of these on every page. Map them to page types:

Page typePrimary schemaSupporting schema
HomepageHotel / LodgingBusinessAggregateRating, LocalBusiness fields
Room pageHotelRoom + OfferBreadcrumbList, containedInPlace ref
FAQ / policies pageFAQPageHotel ref
Reviews pageReview + AggregateRatingHotel ref
Area / destination pageWebPageBreadcrumbList, Hotel ref

Three habits keep this healthy over time. First, treat the @id as your single source of truth and reference it everywhere rather than duplicating the hotel object. Second, keep the markup in sync with the visible page — when the restaurant hours change or a room type is retired, the JSON-LD changes the same day. Stale schema is worse than no schema because it teaches AI to describe a hotel that no longer exists, the exact failure mode we describe in the sleeping-giant AI data piece. Third, validate on every deploy.

Clean structured data will not, on its own, lift you past a competitor with stronger content and links. But it is the difference between a crawler understanding your property in one pass and guessing at it — and between an AI assistant quoting your real check-in time or inventing one. In a search landscape where OTAs quietly outrank hotels for their own names, removing every scrap of ambiguity about who you are and what you offer is not optional. It is table stakes. This is the foundation layer of hotel SEO, and it feeds directly into book-direct conversion.

If you would rather not hand-audit seven schema types across every template, that is exactly the kind of unglamorous, high-leverage plumbing we do. Book a call and we will pressure-test your structured data against how real crawlers and assistants read it.

FAQ

Quick answers

Do I need Hotel or LodgingBusiness schema?

Hotel is a subtype of LodgingBusiness, so for a hotel, aparthotel, inn, or B and B you should use the most specific type that fits — usually Hotel. LodgingBusiness is the fallback when nothing more specific applies. Using the narrower type gives search engines and AI assistants more precise context, and you inherit every property the parent type supports.

Will schema markup guarantee rich results or a higher ranking?

No. Schema makes your content machine-readable and eligible for certain rich results, and it helps AI assistants cite you accurately, but it is not a ranking lever on its own and there is no guaranteed placement. Google decides when to show rich results, and it can ignore markup it distrusts. Treat schema as clean plumbing, not a magic ranking switch.

Can I mark up review scores I invented or aggregated loosely?

Never. AggregateRating and Review markup must reflect genuine, verifiable reviews that are actually visible on the page. Fabricated or self-serving review markup violates Google's policies and can trigger a manual action. Only mark up reviews a real guest can see on that URL.

Where should the JSON-LD go on the page?

Place it in a script tag with type application/ld+json, ideally in the head or anywhere in the body — position does not matter to parsers. Keep one primary entity per page and reference shared entities by id so you are not repeating the whole hotel object on every URL.

Keep reading

More from the Lab

Free intro call

Let's go find out why the OTAs are outranking you for your own name.

20 free minutes. We'll look at your hotel live, show you where you're invisible — on Google and in the AI answers — and tell you straight whether we can help.

No lock-in · No 12-month handcuffs · You talk to the strategist