The Complete XMLTV (EPG) Format Reference for IPTV: Channels, Programmes & Every Element
A complete reference to the XMLTV EPG format for IPTV: the channel and programme elements, every attribute, episode numbering, ratings, and linking the guide to your M3U playlist.
XMLTV is the open, DTD-governed XML format used as the de facto EPG (program guide) for IPTV. A file declares `<channel>` elements, then `<programme>` elements whose broadcast time and channel are attributes — times written as `YYYYMMDDHHMMSS ±HHMM`, assumed UTC if the offset is omitted. Each programme can carry titles, descriptions, credits, episode numbers (`xmltv_ns` or `onscreen`), ratings, images and more, all in a fixed child order set by the DTD. You attach a guide to an M3U playlist with `url-tvg` (alias `x-tvg-url`) and by matching each channel's `tvg-id` to a `<channel id>`; UniPlayer also falls back to name matching. Serve it gzipped, validate against the DTD, and always use full timestamps with an explicit timezone.
If the M3U playlist says which channels exist and where to stream them, XMLTV says what’s on. It is the open XML format behind virtually every IPTV program guide: a plain-text listing of channels and the programmes broadcast on them. There is a real specification here — a published DTD maintained by the XMLTV Project — which makes XMLTV far more precise than the loosely-conventional M3U format. This reference walks through every element and attribute, the exact date format, episode numbering, ratings and images, and how a guide attaches to your playlist.
UniPlayer loads XMLTV guides and maps them onto the channels in your own playlist, so the rules below are the same ones that determine whether your guide shows up correctly.
The big idea: a guide written for the viewer
XMLTV was created in 1999 by Ed Avis and is maintained by the XMLTV Project. Its defining design choice is that it is written from the viewer’s point of view, not the broadcaster’s. Rather than nesting programmes inside channels inside days, an XMLTV document is essentially a flat list: first the channels, then all the programmes mixed together, with each programme carrying its broadcast time and channel as attributes. Programmes for the same channel need not even be adjacent. This is why the format scales cleanly to thousands of channels and why guides “just merge”.
Document skeleton
Every XMLTV file is a well-formed XML document with this shape:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tv SYSTEM "xmltv.dtd">
<tv generator-info-name="MyGrabber 1.0" source-info-name="Example Source">
<!-- channels first -->
<channel id="bbcone.uk">
<display-name>BBC One</display-name>
<icon src="https://example.com/logos/bbc1.png"/>
</channel>
<!-- then programmes -->
<programme start="20260618200000 +0000" stop="20260618210000 +0000" channel="bbcone.uk">
<title lang="en">The Nine O'Clock News</title>
<desc lang="en">The day's headlines.</desc>
</programme>
</tv>
- The root element is always
<tv>, and its content model is strictly channels then programmes. - An XML prolog declaring the encoding is strongly recommended; UTF‑8 is the modern default and the only safe choice for non‑Latin channel and programme names.
- Files are commonly served compressed. Plain
.xmland gzip (.xml.gz) are near-universal; some sources also ship.zip,.taror.xzarchives, though.xz(LZMA2) compresses harder but isn’t decoded by every player. Keep guides compressed — XMLTV is verbose and shrinks dramatically.
Date & time format (read this twice)
Every timestamp in XMLTV uses one format, loosely based on ISO 8601:
YYYYMMDDHHMMSS ±HHMM
for example 20260618200000 +0000. Key rules and the pitfalls that follow from them:
- Partial substrings are legal.
202606(year + month) or20260618(date only) are valid where full precision is unknown. - The timezone is a trailing offset after a space —
+0000,+0300,-0500. Named zones likeBSTappear in old data but are discouraged. - No offset means UTC. If you omit the zone, consumers assume UTC — which silently shifts your guide if the times were actually local.
- Best practice: always write the full 14-digit timestamp with an explicit offset. This removes all ambiguity and is what robust players expect. If your guide and channel are misaligned by a fixed number of hours, fix it on the playlist side with
tvg-shiftrather than rewriting timestamps.
The <tv> root element
<tv> carries optional provenance metadata. None of it affects playback, but it’s good hygiene and useful for debugging which source produced a guide.
| Attribute | Meaning |
|---|---|
date | When the listings were originally produced (in XMLTV date format). |
source-info-name / source-info-url | Human-readable name and URL of the data source. |
source-data-url | URL of the actual data that was processed. |
generator-info-name / generator-info-url | The program that generated this file, and its homepage. |
The <channel> element
Channels are declared once and referenced by id from every programme.
<channel id="bbctwo.uk">
<display-name lang="en">BBC Two HD</display-name>
<display-name lang="en">BBC Two</display-name>
<display-name>102</display-name>
<icon src="https://example.com/logos/bbc2.png" width="240" height="240"/>
<url system="official">https://www.bbc.co.uk/bbctwo</url>
</channel>
| Part | Rules |
|---|---|
id (attribute, required) | A unique identifier. The spec suggests an RFC 2838 DNS-like form (e.g. bbcone.uk). This is the value your playlist’s tvg-id must match. |
<display-name> (one or more) | The human-facing name(s). You may give several — for different languages (lang) or several names for the same language. Earlier names are considered more canonical. A channel number alone is acceptable. |
<icon> | The channel logo: src (required), optional width/height. |
<url> | An informational link (official site, fan page). Optional system identifies the kind/source. |
Channel ordering in the file is irrelevant — channels are looked up by id, not position (though sorting by id makes diffs cleaner).
The <programme> element
This is where the schedule lives. Each programme is one broadcast slot.
<programme start="20260618200000 +0000" stop="20260618210000 +0000" channel="bbctwo.uk">
...
</programme>
Programme attributes
| Attribute | Meaning |
|---|---|
start (required) | Broadcast start, in XMLTV date format. |
stop | Broadcast end. Optional in the spec, but strongly recommended — without it, grid UIs can’t size the slot. |
channel (required) | Must equal a <channel>’s id. |
clumpidx | When two programmes share one slot (“clump”), e.g. 0/2 and 1/2. Defaults to 0/1. |
pdc-start / vps-start | Broadcaster timing-control signals (PDC/VPS) for accurate recording. |
showview / videoplus | Legacy ShowView/VideoPlus recording codes. |
Programme child elements — and their order
The DTD fixes the order of children. If you emit them out of order, strict validators will reject the file. The full sequence is:
title+, sub-title*, desc*, credits?, date?, category*, keyword*,
language?, orig-language?, length?, icon*, url*, country*,
episode-num*, video?, audio?, previously-shown?, premiere?,
last-chance?, new?, subtitles*, rating*, star-rating*, review*, image*
Below, grouped by purpose.
Descriptive content
| Element | Notes |
|---|---|
<title> (one or more) | The programme title (e.g. “The Simpsons”). Repeat with different lang for translations. |
<sub-title> | Episode title / “sub-title”. |
<desc> | A free-text description (a paragraph). Multiple lang variants allowed. |
<category> | Genre (e.g. “News”, “Drama”). Multiple allowed; players typically use the first. lang supported. |
<keyword> | Free-form keywords. lang supported. |
<date> | The year the programme/film was finished (often the copyright year), e.g. 2026 or 20260711. |
<language> / <orig-language> | Spoken language and original language. Use a two-letter code or a name. |
<length> | Real running time excluding ads. Requires `units=“seconds |
<icon> | Programme image/thumbnail (src, optional width/height). |
<url> | Informational link about the programme; optional system. |
<country> | Country of production. lang supported. |
Credits
<credits> wraps cast and crew in this fixed order: director, actor, writer, adapter, producer, composer, editor, presenter, commentator, guest. Each may appear multiple times.
<credits>
<director>Jane Doe</director>
<actor role="Detective Smith">John Roe</actor>
<actor role="Narrator" guest="yes">Sam Lee</actor>
<presenter>Graham Norton</presenter>
</credits>
<actor> supports a role attribute and guest="yes|no". Any credit may itself contain <image> (a person or character photo) and <url> (e.g. a profile page).
Episode numbering
<episode-num> carries the season/episode, with a system attribute (default onscreen). The two predefined systems plus the most common third-party one:
xmltv_ns— the structured, machine-readable scheme. Three dot-separated parts: season . episode . part, each zero-indexed, and each optionally written asX/Yto express “X out of Y total”. Omit a part you don’t know (but keep the dots). Examples:1 . 0 . 0/1→ series 2, episode 1, single-part. (Remember: zero-indexed, so “1” = the second series.)0 . 12/13 . 0/3→ series 1, episode 13 of 13, part 1 of 3.0 . .→ known to be series 1, episode and part unknown.
onscreen— copy whatever appears on screen, e.g.S01E02orEpisode #FFEE.dd_progid— a common non-standard system carrying a Schedules Direct / Gracenote program id (e.g.EP000000060087). Not part of the spec but widely seen in North American guides.
<episode-num system="xmltv_ns">0 . 11 . 0/1</episode-num>
<episode-num system="onscreen">S01E12</episode-num>
Technical broadcast details
<video>holdspresent(yes/no),colour(yes/no),aspect(e.g.16:9),quality(e.g.HDTV,1080p).<audio>holdspresent(yes/no) andstereo, whose legal values aremono,stereo,dolby,dolby digital,bilingual,surround. (bilingualhere means left/right channels carry different mono languages.)<subtitles>hastype="teletext|onscreen|deaf-signed"and an optional<language>child. Repeatable.
<video><colour>yes</colour><aspect>16:9</aspect><quality>HDTV</quality></video>
<audio><stereo>surround</stereo></audio>
<subtitles type="teletext"><language>en</language></subtitles>
Lifecycle flags
<previously-shown>(empty element) marks a repeat. Optionalstart(when it last aired) andchannel(where). Absence does not guarantee the programme is brand new.<premiere>and<last-chance>are paragraph elements (with optionallang); their exact meaning varies by broadcaster, so they exist mainly to reproduce what a printed listing would say. Either can be empty (<premiere/>).<new>(empty element) flags a first-run showing.
Ratings, reviews and images
<rating>is a content/age rating: a required<value>plus optional<icon>s, with asystemattribute (e.g.MPAA,BBFC, a country code).<star-rating>is a quality score as<value>writtenN/M(e.g.3/5), optionalsystemand<icon>.<review>carries a critic review;type="text|url"is required, plus optionalsource,reviewer,lang.<image>(a newer addition) is a richer image reference than<icon>:type="poster|backdrop|still|person|character",size="1|2|3"(small/medium/large by largest dimension),orient="P|L"(portrait/landscape), andsystemto name the source (imdb,tmdb, …).
<rating system="MPAA"><value>PG-13</value></rating>
<star-rating><value>4/5</value></star-rating>
<image type="poster" size="3" orient="P" system="tmdb">https://image.tmdb.org/.../poster.jpg</image>
The IPTV catch-up extension: catchup-id
There is one widely-used attribute that is not part of the XMLTV specification but matters greatly for IPTV: catchup-id on <programme>. Some providers require a programme-specific id to build the archive URL, and players read it into the {catchup-id} placeholder used in M3U catchup-source templates.
<programme start="20260618200000 +0000" channel="footv" catchup-id="episode-44871">
<title>The Match</title>
</programme>
This is the bridge to catch-up playback covered in the M3U format reference — when a channel’s catch-up mode is vod, the {catchup-id} from the matched programme is substituted into the playback URL.
Multi-language guides
XMLTV supports localisation almost everywhere via the lang attribute. You can provide several <title>, <sub-title>, <desc> and <category> elements, each tagged with a language, and a player will pick the user’s preferred one. <orig-language> records the programme’s original language separately from <language> (the version being broadcast).
<title lang="en">The Bear</title>
<title lang="ru">Медведь</title>
<desc lang="en">A chef returns home to run a sandwich shop.</desc>
<desc lang="ru">Шеф-повар возвращается домой управлять кафе.</desc>
Linking XMLTV to your M3U playlist
A guide is useless until it’s joined to channels. The join happens on the playlist side:
- In the
#EXTM3Uheader, point at the guide withurl-tvg(or its synonymx-tvg-url). - On each channel’s
#EXTINFline, settvg-idequal to a<channel id="…">in the guide.
Players then resolve each channel against the XMLTV in a fixed order — match tvg-id to the channel id first, then fall back to comparing the channel’s tvg-name and on-screen name against each <display-name>. The single most reliable practice: set tvg-id and make it identical to the XMLTV id. Name-based matching is fragile across “HD” suffixes, case and spacing. (See the M3U format reference for the full matching algorithm and the tvg-shift time-offset attribute.)
How UniPlayer handles this. UniPlayer matches by
tvg-idand falls back to channel name, so a single, universal XMLTV collection can be attached to several different playlists and still light up the right channels — even when those playlists use inconsistent ids. (Today UniPlayer links one EPG source per playlist; broader multi-EPG support is on the roadmap.)
Delivery, refresh and validation
- Serve it compressed. Gzip (
.xml.gz) is universal and dramatically smaller..zipand.tarare also common;.xzcompresses harder still but isn’t decoded by every player, so weigh size against compatibility. - Refresh sensibly. Most guides update every few hours; signal cadence on the playlist side (
refresh/m3uautoload) rather than expecting the player to guess. - Validate against the DTD. Because XMLTV has a real DTD, you can validate structure and element order before shipping — this catches the out-of-order-children mistakes strict players reject.
How UniPlayer handles this. UniPlayer reads XMLTV guides as plain
.xmlor as.gz,.zipand.tararchives. It also de-duplicates work: if the same EPG source is referenced by several playlists, UniPlayer fetches it once rather than downloading the same guide repeatedly — keeping memory and bandwidth low even with large, shared guides.
Validation checklist & common mistakes
- Declare UTF‑8 in the prolog and save the file as UTF‑8 (no stray BOM).
- Keep child elements in DTD order —
titlebeforedescbeforecredits, etc. Out-of-order children fail strict validation. - Always include
stopso grid UIs can size slots. - Use full timestamps with an explicit offset. Omitting the zone silently means UTC.
- Make channel
ids stable and unique, and match them exactly with playlisttvg-ids. - Don’t bloat the guide. Ship only the days you need, and gzip; multi-megabyte uncompressed guides slow down every client.
<category>/<title>first-wins. Players usually take the first of repeated elements, so order language/genre variants deliberately.
A complete, annotated example
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tv SYSTEM "xmltv.dtd">
<tv source-info-name="Example EPG" generator-info-name="MyGrabber 1.0">
<channel id="bbcone.uk">
<display-name lang="en">BBC One HD</display-name>
<display-name lang="en">BBC One</display-name>
<icon src="https://example.com/logos/bbc1.png"/>
</channel>
<programme start="20260618200000 +0000" stop="20260618210000 +0000" channel="bbcone.uk" catchup-id="ep-44871">
<title lang="en">Sherlock</title>
<sub-title lang="en">A Study in Pink</sub-title>
<desc lang="en">A modern update finds the famous sleuth solving crimes in London.</desc>
<credits>
<director>Paul McGuigan</director>
<actor role="Sherlock Holmes">Benedict Cumberbatch</actor>
<actor role="Dr John Watson">Martin Freeman</actor>
</credits>
<date>2010</date>
<category lang="en">Drama</category>
<category lang="en">Crime</category>
<language>en</language>
<length units="minutes">88</length>
<country>GB</country>
<episode-num system="xmltv_ns">0 . 0 . 0/1</episode-num>
<episode-num system="onscreen">S01E01</episode-num>
<video><colour>yes</colour><aspect>16:9</aspect><quality>HDTV</quality></video>
<audio><stereo>stereo</stereo></audio>
<subtitles type="teletext"><language>en</language></subtitles>
<previously-shown start="20100725" />
<rating system="BBFC"><value>12</value></rating>
<star-rating><value>5/5</value></star-rating>
<image type="poster" size="3" orient="P" system="tmdb">https://image.tmdb.org/.../sherlock.jpg</image>
</programme>
</tv>
A note on extensions and compatibility
XMLTV has a genuine specification, but the ecosystem still adds non-standard attributes where the spec falls short — catchup-id for IPTV catch-up, dd_progid for North American program ids, and Android TV’s display-number/repeat-programs. Consumers ignore attributes and elements they don’t understand, so these extensions are safe to include. When you author a guide, validate against the DTD, prefer xmltv_ns episode numbers and stable channel ids, and test in the real player before publishing.
UniPlayer reads standard XMLTV guides and links them to the channels in your own playlist by
tvg-id. It doesn’t provide listings or content of its own — you bring the playlist and the guide, the same way you’d open your own files in VLC.
(01)What is XMLTV?
XMLTV is an open XML format for describing television schedules. Created in 1999 and maintained by the XMLTV Project, it has become the de facto standard EPG (Electronic Program Guide) format for IPTV players, OTT middleware and guide grabbers. A file lists channels and then programmes, with broadcast time and channel stored as attributes of each programme.
(02)What date and time format does XMLTV use?
Timestamps look like YYYYMMDDHHMMSS followed by a space and a timezone offset, e.g. 20260618200000 +0000. Shorter substrings are allowed (e.g. just YYYYMM), and if you omit the offset the time is assumed to be UTC. Best practice is to always write the full timestamp with an explicit offset.
(03)How do I link an XMLTV guide to my M3U playlist?
Put the guide's URL in the url-tvg (or x-tvg-url) attribute of the #EXTM3U header, then make each channel's tvg-id exactly equal the id of a <channel> element in the guide. Players match by tvg-id first, then fall back to matching names.
(04)What's the difference between the xmltv_ns and onscreen episode-num systems?
xmltv_ns is a structured, machine-readable numbering: three dot-separated parts (season . episode . part), all zero-indexed, each optionally written as X/Y to show totals. onscreen is the human-readable string exactly as shown on screen, like S01E02. Use xmltv_ns when you can; onscreen otherwise.