Course Schema Markup: Course Info and Course Lists

Course schema is structured data that describes an educational course to search engines: what it is called, what it covers, who provides it and, optionally, how it is delivered and what it costs. On a course page you use the Course type from schema.org, written as JSON-LD. On a page that lists several courses, such as a catalog or category page, you use an ItemList that points at the individual course pages.
There is one thing to settle before you spend an afternoon on it. Google's Course info rich result was included in a group of search features that Google announced it was phasing out in 2025, and the markup is still valid schema.org vocabulary even where Google no longer draws a special result from it. So this guide covers both halves honestly: what the markup contains and how to write it correctly, and how to decide whether it is worth maintaining on your site today. Check Google's current Course documentation before you commit, because eligibility in this area has shifted and will keep shifting.
What Course markup actually describes
Schema.org defines Course as a sequence of one or more educational events and, usually, an associated syllabus. That definition matters because it separates the course from any single running of it. A course called Introduction to Data Analysis is one thing. The cohort that starts in March, taught online by a named instructor, is another. Schema.org models that difference with a second type, CourseInstance, which hangs off the course through the hasCourseInstance property.
Keeping the two apart saves you from a common mistake: stuffing a start date into the course itself. A course is evergreen and a run of it is dated. If your catalog page for a course has three upcoming sessions, you have one Course and three CourseInstance entries, not three courses with near-identical names.
Course is also not the same thing as a product, an event or a learning video. A course you sell can carry an Offer, and a live session can be described by its instance, but the page should be about one course. A page that bundles twelve unrelated courses is a list page, and it should be marked up as a list.
The required and recommended properties for a single course
For the individual course page, Google's documentation has asked for three things: the course name, a description, and the provider. The provider is an Organization, and the name inside it is what is expected. Everything beyond those three has been treated as recommended rather than required, which means a page with only the basics can be valid while a page with more detail gives a richer and more accurate description.
In practice the useful properties fall into a few groups.
- Identity: name, description, and a url for the course page. Keep the name identical to the visible heading, and write the description as a real summary of what the learner will be able to do, not as a slogan. Google has documented a length limit on the description, so keep it to a short paragraph.
- Provider: an Organization with a name and, where you have one, a sameAs link to the provider's official site or profile. For a university department, name the institution the learner would actually recognise.
- Cost: an Offer, or several, carrying a price and priceCurrency. A free course should say so with a price of 0 rather than omitting the offer, because an absent offer tells a parser nothing and a zero price tells it the course is free.
- Delivery: hasCourseInstance containing CourseInstance objects, each with a courseMode such as online or onsite, and optionally a courseSchedule, a courseWorkload, and an instructor.
- Qualification and depth: courseCode for your catalog identifier, coursePrerequisites, numberOfCredits, and educationalCredentialAwarded where a certificate or degree actually results.
- Quality signals: aggregateRating, but only when the ratings are genuine, collected from learners, and visible on the page itself.
A worked example: one course, two sessions
Suppose a training company runs a course called Technical SEO Fundamentals. It costs 4,500 Mexican pesos, runs for six weeks, and has one online cohort and one in-person cohort in Mexico City. The JSON-LD in the page head or body would be structured as follows, described here in words so you can translate it into your template.
The top level is a Course with the name, the description, and a provider that is an Organization called by the company's name. Next to the provider sits an offers block with an Offer whose price is 4500 and whose priceCurrency is MXN. Then comes hasCourseInstance, an array of two CourseInstance objects. The first has courseMode online and a courseWorkload of about five hours per week, written as an ISO 8601 duration. The second has courseMode onsite, a location naming the Mexico City venue, and its own start date.
Notice what is and is not repeated. Both sessions share one price, so it is stated once on the course. If the online cohort were cheaper than the in-person one, each CourseInstance could carry its own offers and the course-level Offer would be dropped or reduced to a range. The rule to apply is the same as with any structured data: whatever you declare must match what a human sees on the page, in the same currency, with the same number.
Generate this markup from the same database fields that render the visible page. Hand-written JSON-LD pasted into hundreds of templates is the single most common cause of markup that quietly disagrees with the page a year later, when the price changes in one place and not the other.
Marking up a course list page
A category page that lists many courses is a different job. Google's guidance for this case has been to use an ItemList, with each entry a ListItem that has a position and a url pointing to that course's own page. The summary page itself does not repeat the full Course detail for every item. Each destination page carries its own complete Course markup, and the list simply points at them.
Three rules keep a list valid. First, every URL in the list must be a real, crawlable page on the same site, and each must contain its own Course markup. Second, the positions should be consecutive numbers starting at 1, in the order shown to the user. Third, the list should contain the courses actually visible on that page, not a hidden superset. If you paginate a category into pages of twenty, each page lists its own twenty and no more.
Google has also stated for list-style carousel features that the list page and the detail pages must be consistent with each other, and that the markup has to be on pages available to everyone rather than behind a login. That last point catches learning platforms whose catalogs are visible only after sign-up. If a crawler cannot see the page, the markup on it cannot do anything for you, for exactly the reasons covered in guides to paywalled content: the crawler needs a version of the page it can fetch.
When you are actually eligible, and what to expect
Eligibility has two layers. The technical layer is that your markup must be valid, must follow Google's structured data policies, and must describe content that is visible on the page. The second layer is discretionary: even perfect markup does not guarantee a special result, and Google has said so for structured data generally.
The bigger question is the state of the feature itself. In 2025 Google announced that it was simplifying the search results page and would stop showing several rich result types, and Course info was named in that group. Reports for those types were planned to disappear from Search Console as part of the same change. Treat the current Google Search Central documentation as the authority on what is shown today, because this is the kind of detail that changes without the vocabulary changing.
What does not change is that the markup remains valid schema.org, other consumers can read it, and it is a compact, machine-readable statement of what a course is. Teams that already generate it from structured fields pay very little to keep it. Teams that have not built it should weigh the cost honestly: if the plan depends on a course carousel appearing in Google, check that the feature exists before you build for it.
It is also worth separating a rich result from ranking. Structured data is not a ranking factor in the sense of pushing a page up for a query. It helps a search engine understand the page and, when a feature exists, makes you eligible for it. Your course pages still need to earn their position with content, internal links and a clear match to what learners search for.
Mistakes that make course markup invalid or misleading
Most problems with education markup come from a handful of repeatable errors, and each one is easy to check for.
- Marking up a course you do not offer. A description of a course that is not available, or one that has been discontinued but still carries an active offer, describes content the user cannot obtain. Remove the markup, or mark the instance as no longer available, when the course ends.
- Using Course for things that are not courses. A blog article about learning, a webinar recording, or a single lesson in a larger program is not a course. A course is a structured program of instruction. Misusing the type to chase a feature is the kind of thing that draws a manual action for structured data spam.
- Missing or unnamed provider. A provider given only as a URL string, or as a person where the body is an institution, leaves the required field empty in a parser's eyes. Use an Organization with a name.
- Price mismatch. The Offer says 0 and the page says 4,500 pesos after login. Declared price, currency and availability must agree with what a visitor sees without signing in.
- Invented ratings. An aggregateRating with a review count that does not correspond to visible, genuine reviews is a fast way to lose trust and attract a penalty. If you have no reviews, leave the property out.
- Duplicated markup on list pages. Copying full Course markup for forty courses into one category page makes that page claim to be forty things. Keep details on the detail pages and use an ItemList for the category.
Testing, deploying, and keeping it accurate
Validate in two stages. The Rich Results Test checks whether Google's parser finds eligible markup for a feature, and the Schema Markup Validator checks that the vocabulary is valid schema.org regardless of any Google feature. Given the changes described above, passing the second is the more durable goal, and passing the first tells you only what Google currently supports. If you want a longer discussion of how those tools differ, the broader guide on schema validation covers the workflow in detail.
Use a sample of pages rather than a single one. Test a free course, a paid course, a course with several instances, and a course with no instance at all, since those are the shapes most likely to produce a missing-field surprise. Then fetch one of each from the live site with the URL Inspection tool to confirm the markup survives in the rendered page, particularly if your catalog is built with JavaScript.
Finally, decide who owns the data. Course markup is only as accurate as the records behind it. Put the price, dates, and availability fields in the system that the editors already update, render both the page and the JSON-LD from those fields, and add a check to your release process that fails when a course marked available has a past end date. That one check prevents most of the stale markup that accumulates on education sites.
Frequently asked questions
Is course schema still worth adding?
The markup remains valid schema.org and costs little if you generate it from structured fields. Google announced in 2025 that it was phasing out the Course info result, so do not build a plan around a special display. Check Google's current documentation for what is shown today.
Which properties does Google require for a Course?
Google's documentation has asked for the course name, a description, and a provider that is an Organization with a name. Offers, course instances, instructors, and ratings have been treated as recommended. Always confirm against the current documentation.
How do I mark up a page that lists many courses?
Use an ItemList, where each ListItem has a position and a url to a course page. Each of those pages should carry its own full Course markup. The list page should show only the courses that are visible on it.
What is the difference between Course and CourseInstance?
Course is the program itself, which stays the same over time. CourseInstance is one scheduled running of it, with its own mode of delivery, dates, and instructor. Put dates and delivery details on the instance, not on the course.