SEO
Event schema generator (JSON-LD)
By Charles Summers · Updated · Free, no signup
Short answer
Event needs three things: a name, a startDate in ISO 8601 with a UTC offset, and a location that is either a Place with a postal address or a VirtualLocation with a url. The two properties that became load-bearing in 2020 and are still routinely missing are eventAttendanceMode, which distinguishes in-person from online and hybrid, and eventStatus, which lets you say cancelled, postponed or rescheduled without deleting the page. Event is also the only type here that expires by itself: a start date in the past drops the item, so this markup has a shelf life the rest do not.
Use the schema markup generator
Why events need a different approach
Event markup is a modelling problem dressed as an SEO task. Almost every failure traces back to describing a moment in time and a place incorrectly, not to missing a property. A start time with no UTC offset is ambiguous the moment somebody in another timezone reads it. A webinar modelled as a physical venue is a category error that no amount of completeness elsewhere fixes. A cancelled event whose page was deleted leaves ticket holders arriving at a 404 and a search result still advertising it.
The 2020 pandemic reshaped this type more than any other, because an entire industry had to move online, then cancel, then reschedule, then run hybrid, sometimes in the same week. Two properties carry that history. eventAttendanceMode says whether attendance is offline, online or mixed, and eventStatus says whether the event is going ahead, cancelled, postponed, rescheduled or moved online. Both are technically optional and both are the difference between markup that stays true and markup that lies to people holding tickets.
It runs entirely in your browser. Nothing you type is sent to a server, no account is required, and there is no usage limit, because there is no cost per run to control.
The offset is not optional in practice, whatever the spec allows
startDate accepts an ISO 8601 value, and a bare date such as 2026-11-19 is technically well-formed. For an all-day festival that is defensible. For anything with a start time it is a problem waiting to happen, because a time with no offset has to be interpreted, and the interpreting system is not standing in your venue. Write 2026-11-19T19:00:00+00:00 and the ambiguity disappears for everyone reading it, wherever they are.
Daylight saving is where hand-written offsets go wrong, and the error is systematic rather than random. A UK venue is +00:00 in November and +01:00 in June. A team that hardcodes one offset in a template gets every event in the other half of the year wrong by an hour, and an hour is exactly the size of error that nobody notices until an audience arrives late. Generate the offset from a timezone-aware date library using the venue's timezone identifier, never from a constant.
endDate deserves more respect than it gets. It is what tells a consuming system how long to treat the event as current, and without it a two-hour talk and a three-day conference look identical. For multi-day events it is the difference between the listing disappearing after day one and staying live throughout. Include it whenever the end is known, and use the same offset discipline as the start.
The last date property people forget is on the offer rather than the event. validFrom marks when tickets go on sale, which is a genuinely useful fact for an event announced months ahead with a sales window opening later. Without it, a page can advertise availability during a period when nothing can actually be bought, and that mismatch between what the markup asserts and what the page does is the same class of problem as a wrong price.
Attendance mode and location have to describe the same event
The location property is where the biggest single category of broken event markup lives. It takes one of two shapes and the shape has to match how the event is actually attended. In-person means a Place with a name and a full PostalAddress. Online means a VirtualLocation with a url, and no address, because there is no address. Hybrid means both, expressed as an array containing a Place and a VirtualLocation.
eventAttendanceMode has to agree with that choice, and this is the pairing that is most often half-implemented. Setting OnlineEventAttendanceMode while leaving a Place with a street address underneath it describes an event that is simultaneously online and in a building, which is not a state anything can resolve. The address is usually a leftover from the template default, and it survives because nothing errors, it is simply wrong.
A few specific cases are worth naming because they recur.
- Online events with no url. A VirtualLocation without a url is the online equivalent of a venue with no address. If the joining link is private, use the registration page URL rather than omitting the property.
- Hybrid events sold as one ticket. Both locations in the array, MixedEventAttendanceMode, and offers that make it clear what each ticket type actually buys.
- Tours and multi-venue runs. Each date is a separate Event with its own URL, not one Event whose location is a list of cities.
- Free events. Still an offer, with price set to 0 and a priceCurrency alongside it. Omitting offers entirely is what makes a free event look like an event with unknown pricing.
- Sold out. availability becomes SoldOut. The event has not been cancelled, and using eventStatus for a sold-out show confuses two different facts.
The lifecycle: cancelled, postponed, rescheduled, and expiry
Every other type on this tool describes something static. An Event has a lifecycle, and eventStatus exists to express it. EventScheduled is the default and means going ahead. EventCancelled means it is not happening. EventPostponed means it is not happening on the original date and no new date is set yet. EventRescheduled means a new date is set, and the honest form keeps previousStartDate so the change is legible. EventMovedOnline pairs with a switch of attendance mode to online.
The instinct when an event is cancelled is to delete the page, and that instinct is wrong for a fortnight or so. A deleted page cannot say cancelled. It becomes a 404 that ticket holders land on from their confirmation email, while the search result may still be advertising the event because nothing has told the index otherwise. Keeping the page, setting eventStatus to EventCancelled, and saying so in visible copy at the top is the version that serves the people who bought tickets. Retire the URL later, once the traffic has stopped.
Postponement has its own trap. Google's documented approach is to keep the original startDate until a new date is confirmed rather than blanking it or guessing, because a postponed event with no date at all is unrepresentable and a fabricated date is worse than a stale one. When the new date is confirmed, move to EventRescheduled, set the new startDate, and record the old one in previousStartDate.
Then there is expiry, which is unique to this type. Once startDate has passed, the item is over and drops out of the enhancement reporting, which is correct behaviour rather than an error. What it means practically is that Event markup is the only kind on this tool with a maintenance clock attached. If your events are generated from a database this is free. If somebody pastes JSON-LD into a page by hand, you accumulate a slow archive of past events being asserted as though they were upcoming, and nothing in Search Console will nag you about it.
Recurring events follow the same logic. A weekly class is not one Event with a date range spanning six months, it is a series of occurrences, and the pattern that works is a separate Event item for each occurrence with its own URL. A single Event stretching across months is describing something nobody can attend as a single thing.
Numbers worth knowing
| Metric | Typical | What it means |
|---|---|---|
| Required properties | name, startDate, location | The shortest required list of any type on this tool, which flatters how easy it is. Location has to be a Place with an address or a VirtualLocation with a url, and getting that wrong is the most common failure. |
| startDate format | ISO 8601 with a UTC offset | 2026-11-19T19:00:00+00:00. Generate the offset from the venue timezone with a date library, because a hardcoded offset is wrong for half the year in any country observing daylight saving. |
| Item lifespan | ends when startDate passes | The only type here with a built-in expiry. Past events drop out of the enhancement report by design, which also means hand-pasted markup quietly accumulates into an archive nobody is warned about. |
| Cancellation handling | keep the page, set EventCancelled | Deleting the page produces a 404 for ticket holders arriving from confirmation emails while the result may still advertise the event. Retire the URL after the traffic subsides, not on the day. |
Mistakes that quietly cost you results
- Modelling a webinar as a Place with a street address
- An online event takes a VirtualLocation with a url, and eventAttendanceMode becomes OnlineEventAttendanceMode. The leftover address is usually a template default that nothing errors on, which is exactly why it survives for years.
- Hardcoding the UTC offset in the event template
- Any country observing daylight saving changes offset twice a year, so a constant is wrong for roughly half your calendar and wrong by exactly the amount that makes people late. Derive it from the venue timezone at render time.
- Representing a weekly class as one Event with a six-month range
- Nobody attends a six-month event. Emit one Event per occurrence, each with its own URL and its own start and end, so that each is individually findable and expires on its own schedule.
- Using eventStatus to signal that tickets have sold out
- Sold out is an availability value on the offer, and the event is still scheduled and still happening. eventStatus is for whether the event itself is going ahead, and conflating the two tells attendees their event is cancelled when it is not.
What does the output look like?
This is the exact output the tool produces from the example inputs. It is generated by the same code that runs when you click the button, so what you see here is what you get.
Frequently asked questions
What should I do with the page after an event has finished?
Keep it, and edit it rather than deleting it. A past event page carrying a recap, photos and a link to the next occurrence is genuinely useful, whereas a 404 discards accumulated links and any search interest in the event name afterwards. Update the copy so it clearly reads as past rather than upcoming, and if the event recurs annually, decide once whether you use a permanent URL updated each year or a dated URL per edition, then apply it consistently.
How do I mark up an event I am listing but not organising?
Set organizer to the actual organising body and keep your own role out of the Event object, since claiming to organise something you are listing misdescribes it. Listing pages carrying dozens of events are best expressed as separate Event items rather than one enormous nested structure. Be honest about ticketing too: if the offer url points at a third-party seller rather than at you, that is where it should point.
Do I need offers if the event is free to attend?
Include it, with price set to 0 and a priceCurrency alongside. It reads oddly to write a currency for a zero price, but the pair is what makes the offer complete, and stating zero is a positive assertion that the event is free. Omitting offers entirely reads as unknown pricing rather than free, which is a meaningfully different thing to anyone deciding whether to click.
What is previousStartDate for and when should I set it?
It records the date an event was originally due to happen, and it belongs with EventRescheduled when a new date has been confirmed. It exists so the change is legible rather than silent, which matters to anyone who saw the earlier listing and may still have the original date in a calendar. Set it at the moment you change startDate, in the same edit, because it is the sort of property that never gets added retrospectively.
Should each date in a festival or conference get its own Event item?
It depends on how it is sold and attended. A three-day conference bought as one ticket is one Event with a startDate and an endDate spanning the three days. A festival where each day is separately ticketed, or a run of individual talks people pick between, is better modelled as separate Events with a shared parent, since each is independently attendable and independently findable. Follow the ticket, not the calendar.
Related free tools
- Schema Markup Generator SEO
- Schema Markup Generator for article pages SEO
- Schema Markup Generator for product pages SEO
- Schema Markup Generator for faq pages SEO
- Schema Markup Generator for local business SEO
- Schema Markup Generator for software apps SEO
- Schema Markup Generator for breadcrumbs SEO
- Readability Scorer SEO
- SEO Meta Tag Generator for ecommerce product pages SEO
- SEO Meta Tag Generator for local business SEO
- AI B2B Headline Copywriter for logistics Copywriting
- AI B2B Headline Copywriter for professional services Copywriting
Some links on this site are affiliate links, which means Hacking Demand may earn a commission if you buy through them at no extra cost to you. This does not influence which tools are listed. The tools on this page are free and have no affiliate relationship of any kind.