Preloader
Others
  • Estimated reading time: 6 Minutes

How IPTV Players Work: Xtream Codes API, M3U Playlists & XMLTV EPG Explained

How IPTV Players Work: Xtream Codes API, M3U Playlists & XMLTV EPG Explained

If you have ever opened an IPTV player app, typed in a server URL, username and password, and watched thousands of channels and a full TV guide appear a few seconds later, you have used one of the most widely adopted, yet least documented, interfaces in consumer streaming. Behind almost every iptv subscription sit the same three building blocks: a playlist format, a JSON API and an XML programme guide. This article explains how they fit together, with working examples you can use to build or debug a simple player.

The three formats every IPTV player speaks

Format What it describes Typical use
M3U / M3U8 playlist A list of channels and their stream URLs Simple players, quick imports
Xtream Codes API JSON endpoints for accounts, categories, live, VOD and series Full-featured apps with libraries and catch-up
XMLTV The electronic programme guide (EPG) The "now and next" grid in the app

The video itself is usually delivered as HLS (HTTP Live Streaming) or as an MPEG-TS stream over HTTP. The formats above simply tell the player what exists and where to find it.

M3U playlists: the simplest format

An extended M3U file is plain text. Each channel is an #EXTINF line with metadata, followed by the stream URL:

#EXTM3U
#EXTINF:-1 tvg-id="news.example" tvg-name="Example News" tvg-logo="https://example.com/logos/news.png" group-title="News",Example News
https://example.com/live/demo/demo/1001.m3u8
#EXTINF:-1 tvg-id="sport1.example" tvg-name="Example Sport 1" group-title="Sport",Example Sport 1
https://example.com/live/demo/demo/1002.m3u8

The attributes matter. group-title creates the categories you see in the app, tvg-logo supplies the channel icon, and tvg-id links the channel to its programme guide data. A minimal parser in JavaScript looks like this:

function parseM3U(text) {
  const lines = text.split(/\r?\n/);
  const channels = [];
  for (let i = 0; i < lines.length; i++) {
    if (!lines[i].startsWith('#EXTINF')) continue;
    const attrs = Object.fromEntries(
      [...lines[i].matchAll(/([\w-]+)="([^"]*)"/g)].map((m) => [m[1], m[2]])
    );
    const name = lines[i].split(',').pop().trim();
    const url = (lines[i + 1] || '').trim();
    channels.push({ name, url, group: attrs['group-title'], epgId: attrs['tvg-id'], logo: attrs['tvg-logo'] });
  }
  return channels;
}

M3U is easy to generate and import, but it has limits: it is one large file, it has no structure for films and series, and every change means downloading the whole list again.

The Xtream Codes API: structured data over JSON

Most modern IPTV apps prefer the Xtream Codes style API, because it returns structured JSON that can be loaded in pieces. A player signs in with three values: the server URL, a username and a password. The main endpoint is player_api.php:

Account and server information
GET https://example.com/player_api.php?username=demo&password=demo

Live TV categories and channels
GET https://example.com/player_api.php?username=demo&password=demo&action=get_live_categories
GET https://example.com/player_api.php?username=demo&password=demo&action=get_live_streams

Video on demand and series
GET https://example.com/player_api.php?username=demo&password=demo&action=get_vod_streams
GET https://example.com/player_api.php?username=demo&password=demo&action=get_series

Short programme guide for one channel
GET https://example.com/player_api.php?username=demo&password=demo&action=get_short_epg&stream_id=1001

The first call returns a user_info object, with fields such as the account status, expiry date and the number of allowed connections, and a server_info object with the URL, port and time zone. Players use these to show account details and to build stream URLs, which usually follow this pattern:

  • https://example.com/live/{username}/{password}/{stream_id}.m3u8
  • https://example.com/movie/{username}/{password}/{stream_id}.{container_extension}
  • https://example.com/series/{username}/{password}/{episode_id}.{container_extension}

A small Node.js script that lists live categories might look like this:

const base = 'https://example.com/player_api.php';
const auth = 'username=demo&password=demo';

async function listCategories() {
  const res = await fetch(`${base}?${auth}&action=get_live_categories`);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const categories = await res.json();
  for (const c of categories) console.log(c.category_id, c.category_name);
}

listCategories().catch(console.error);

Because the data arrives in sections, a player can show live categories instantly and load the much larger film and series libraries in the background. That is why Xtream-based apps feel faster than M3U imports on big catalogues.

XMLTV: where the TV guide comes from

The programme guide is usually an XMLTV document, often available from an endpoint such as xmltv.php. It contains <channel> elements and <programme> elements with start and stop times:

<tv>
  <channel id="news.example">
    <display-name>Example News</display-name>
  </channel>
  <programme start="20261024180000 +0100" stop="20261024190000 +0100" channel="news.example">
    <title>Evening Bulletin</title>
  </programme>
</tv>

The channel attribute must match the playlist's tvg-id. Most "empty TV guide" bugs come down to three causes: mismatched IDs, time zone offsets being ignored, or an XML file so large that a low-powered device times out while parsing it. Parse it in a streaming fashion, cache the result and only keep the next day or two in memory.

Playing the stream in a browser with hls.js

Safari plays HLS natively, while most other browsers need a library. hls.js is the common choice:

<video id="player" controls muted playsinline></video>
<script src="https://cdn.jsdelivr.net/npm/hls.js@1"></script>
<script>
  const video = document.getElementById('player');
  const src = 'https://example.com/live/demo/demo/1001.m3u8';
  if (Hls.isSupported()) {
    const hls = new Hls({ lowLatencyMode: true });
    hls.loadSource(src);
    hls.attachMedia(video);
    hls.on(Hls.Events.ERROR, (event, data) => console.warn(data.type, data.details));
  } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
    video.src = src;
  }
</script>

Two practical notes. Browsers block mixed content, so an HTTPS page cannot load an HTTP stream. And the stream server must send CORS headers, or the browser will refuse the playlist even when the URL works in a native app.

Debugging streams with ffprobe

When a channel will not play, check the stream itself before blaming the player. ffprobe, part of FFmpeg, shows the codecs, resolution and bitrate:

ffprobe -hide_banner -show_streams -select_streams v:0 \
  "https://example.com/live/demo/demo/1001.m3u8"

Look at codec_name (H.264 or HEVC), width and height. A device that cannot decode HEVC will show a black screen with sound, which is one of the most common support tickets in IPTV.

What real-world services add on top

The formats are only the interface. What separates a good service from a poor one is the infrastructure behind it: enough server capacity for peak events such as a big football match, load balancing across regions, accurate EPG data, and support for apps on Fire TV, Android TV, smart TVs and phones. UK services such as Telemo also bundle features like a built-in VPN, because some internet providers throttle video traffic at busy times. For developers, the lesson is that player quality depends as much on handling slow APIs, partial data and network errors gracefully as on decoding video.

A note on legality

M3U, Xtream-style APIs, XMLTV and HLS are neutral technologies used by broadcasters, hotels, universities and commercial TV services. Whether a particular stream is legal depends on whether the content is properly licensed, not on the format. If you build or test a player, use your own content or streams you have the rights to.

Key takeaways

  • M3U is a simple list of channels and URLs; Xtream Codes is a structured JSON API; XMLTV supplies the guide.
  • Load Xtream data in sections so the app feels fast, and cache everything you can.
  • Match tvg-id to the XMLTV channel ID, and respect time zone offsets.
  • In the browser, use hls.js, serve over HTTPS and make sure the stream sends CORS headers.
  • Use ffprobe to check codecs before debugging the player.
Related articles
The Growing Influence of AI and Automation on Financial Software
25 Oct, 2026
  • Estimated reading time: 5 Minutes
Preparing Code Datasets for AI: Issue-to-Resolution Stories Matter
25 Oct, 2026
  • Estimated reading time: 4 Minutes
Common SaaS Development Mistakes and How to Avoid Them
25 Oct, 2026
  • Estimated reading time: 8 Minutes
The Role of Automation in Simplifying Everyday IT Operations
24 Oct, 2026
  • Estimated reading time: 5 Minutes
Best AI Logo Generators in 2026 for Beginners & Small Businesses
24 Oct, 2026
  • Estimated reading time: 9 Minutes
Weekly trending
The Growing Influence of AI and Automation on Financial Software
25 Oct, 2026
  • Estimated reading time: 5 Minutes
Preparing Code Datasets for AI: Issue-to-Resolution Stories Matter
25 Oct, 2026
  • Estimated reading time: 4 Minutes
Common SaaS Development Mistakes and How to Avoid Them
25 Oct, 2026
  • Estimated reading time: 8 Minutes
The Role of Automation in Simplifying Everyday IT Operations
24 Oct, 2026
  • Estimated reading time: 5 Minutes
Our Sponsors

Our blog is proudly supported by industry-leading sponsors.