EPG & exports
Read published channel schedules as XMLTV, JSON, a pollable playlist or a transmission file for playout.
Once a channel's schedule is published, Ingenee serves it in the formats distributors and
playout systems expect. Every endpoint here needs only the feeds:read scope.
Only published schedules are served
These feeds read the published version, never a newer draft. A channel that hasn't been published (or whose latest build hasn't passed the gate) returns an empty feed rather than its draft line-up. Publishing again is what updates them. Archived channels aren't served.
Pick a feed
| You need | Use | Format |
|---|---|---|
| EPG for one channel | GET /v1/channels/{id}/epg.xml | XMLTV |
| EPG for every channel in one file | GET /v1/epg/master.xml | XMLTV |
| Programme list for your own app | GET /v1/channels/{id}/schedule.json | JSON |
| A list of your channels and their feed URLs | GET /v1/channels.json | JSON |
| A playout system polling for what's next | GET /v1/channels/{id}/playlist.json | JSON |
| A running order with ad breaks for playout automation | GET /v1/channels/{id}/transmission.csv, .xml or .json | CSV, XML, JSON |
Caching
The XMLTV, schedule, master EPG, channels and playlist feeds send ETag (a weak validator), Last-Modified, Cache-Control
and Access-Control-Allow-Origin: *, and honor If-None-Match with 304 and HEAD. Poll with the ETag you last received:
curl -i https://dev.app.ingenee.ai/api/v1/channels/f27a91c3-4b6d-4e08-a5d2-9c1e3b7f0a64/epg.xml \
-H "Authorization: Bearer $INGENEE_API_KEY" -H 'If-None-Match: W/"1c4e9a07b2d5"'max-age is 300 seconds for these feeds and 60 for the playlist. A feed that doesn't exist (wrong id, another organization's
channel) returns 404 ENDPOINT_NOT_FOUND. The transmission files are downloads, not cached feeds, and have no ETag.
XMLTV for a channel
curl https://dev.app.ingenee.ai/api/v1/channels/f27a91c3-4b6d-4e08-a5d2-9c1e3b7f0a64/epg.xml \
-H "Authorization: Bearer $INGENEE_API_KEY"<?xml version="1.0" encoding="UTF-8"?>
<tv generator-info-name="Ingenee">
<channel id="ingenee-classic-cinema"><display-name>Ingenee Classic Cinema</display-name></channel>
<programme start="20261102010000 +0000" stop="20261102024800 +0000" channel="ingenee-classic-cinema">
<title>The Long Night</title>
<desc>A night shift turns into a manhunt.</desc>
<rating><value>TV-14</value></rating>
<icon src="https://image.tmdb.org/t/p/w500/…"/>
</programme>
</tv>The channel id is the channel's slug. Times are UTC. A programme carries sub-title and episode-num for episodes, a
desc cut to 500 characters, and an icon only when there is a public image URL. If the channel has saved
slates and fallbacks, ingenee-slate elements follow the channel element.
GET /v1/epg/master.xml is the same, with every non-archived channel's channel elements first and then all programmes.
It carries title, rating and icon but not descriptions or episode details.
Schedule JSON
curl https://dev.app.ingenee.ai/api/v1/channels/f27a91c3-4b6d-4e08-a5d2-9c1e3b7f0a64/schedule.json \
-H "Authorization: Bearer $INGENEE_API_KEY"{
"channel": { "id": "f27a91c3-4b6d-4e08-a5d2-9c1e3b7f0a64", "slug": "ingenee-classic-cinema", "name": "Ingenee Classic Cinema", "region": "US", "language": "en" },
"programmes": [
{ "start": "2026-11-02T01:00:00.000Z", "stop": "2026-11-02T02:48:00.000Z", "title": "The Long Night", "rating": "TV-14",
"image": "https://image.tmdb.org/t/p/w500/…" },
{ "start": "2026-11-02T02:48:00.000Z", "stop": "2026-11-02T03:30:00.000Z", "title": "Harbor Lights", "rating": "TV-PG",
"season": 2, "episode": 4, "episodeTitle": "Low Tide" }
]
}image is present only when there is a public image. season, episode and episodeTitle appear only for episodes. A
slates array is added when the channel has saved slates.
Channel index
GET /v1/channels.json lists every non-archived channel with its status (idle, building, live, paused) and
horizonDays. It is the place to discover channel ids.
{
"version": 1,
"count": 1,
"channels": [
{ "id": "f27a91c3-4b6d-4e08-a5d2-9c1e3b7f0a64", "slug": "ingenee-classic-cinema", "name": "Ingenee Classic Cinema",
"region": "US", "archetype": "movie", "status": "live", "horizonDays": 14,
"feeds": { "xmltv": "https://cdn.ingenee.ai/epg/ingenee-classic-cinema.xml", "scheduleJson": "https://cdn.ingenee.ai/schedule/ingenee-classic-cinema.json" } }
]
}The feeds URLs are absolute URLs on Ingenee's delivery host. To read with your API key, use the /v1 paths in this guide.
Playlist for a playout system
GET /v1/channels/{id}/playlist.json is the stable, pollable form of the running order. Poll it; it is cached for 60 seconds.
{
"channel": { "id": "f27a91c3-…", "slug": "ingenee-classic-cinema", "name": "Ingenee Classic Cinema", "timezone": "America/New_York" },
"generatedAt": "2026-10-30T07:12:01.004Z",
"window": { "from": "2026-11-02T01:00:00.000Z", "to": "2026-11-16T05:00:00.000Z" },
"eventCount": 2,
"events": [
{ "start": "2026-11-02T01:00:00.000Z", "end": "2026-11-02T01:45:00.000Z", "durationSec": 2700, "type": "PROGRAM",
"title": "The Long Night", "mediaId": "…", "version": "broadcast edit", "assetUri": "…", "scte": null },
{ "start": "2026-11-02T01:45:00.000Z", "end": "2026-11-02T01:47:00.000Z", "durationSec": 120, "type": "AD_BREAK",
"title": null, "mediaId": null, "version": null, "assetUri": null,
"scte": { "scte_event_type": "SPLICE_INSERT", "scte_event_id": "…" } }
],
"emptyReason": null,
"warnings": []
}type is PROGRAM, AD_BREAK, PROMO, BUMPER, FILLER or LIVE. version says which cut airs. Missing values are
null, never invented. An empty events list comes with an emptyReason, so you can tell "nothing scheduled" from "something broke".
warnings are non-fatal build notes such as a runtime that had to be trusted.
Transmission files
The transmission export is the exact running order to the second, including ad breaks with their signalling metadata. It is a download of a date range, in the format you choose by the path.
curl "https://dev.app.ingenee.ai/api/v1/channels/f27a91c3-4b6d-4e08-a5d2-9c1e3b7f0a64/transmission.csv?from=2026-11-02&to=2026-11-03&breaks=derive" \
-H "Authorization: Bearer $INGENEE_API_KEY" -o transmission.csv| Query | Meaning |
|---|---|
from, to | Inclusive dates, YYYY-MM-DD, on the channel's own clock. Leave both out for the whole published version |
profile | A column profile. Built in: fast-transmission (default), running-order, fast-transmission-scte, epg-listing, amagi-playlist. An unknown slug is 404 NOT_FOUND |
breaks | derive (default: real pods with real durations), cues (zero-length markers only; the programme timeline is untouched) or none (programmes only) |
types | Comma-separated event types to keep: PROGRAM, AD_BREAK, PROMO, BUMPER, FILLER, LIVE |
When you pass profile, that profile's saved format wins over the path extension. The response is text/csv,
application/xml or application/json with a Content-Disposition: attachment filename. The ad plan and SCTE signalling
columns come from the channel's ad configuration: Ingenee describes the break and the cue; your
playout or SSAI layer inserts them.
Not yet available
- A per-channel push to a distributor. These feeds are pulled.
- Listing or creating custom export profiles through the API. Profiles authored in the console can be used by
slug. - Webhook events when a feed changes. Use the
publish-succeededwebhook or poll withIf-None-Match.
Endpoints
| Operation | Method + path | Scope |
|---|---|---|
| XMLTV EPG for a channel | GET /v1/channels/{id}/epg.xml | feeds:read |
| XMLTV EPG for every channel | GET /v1/epg/master.xml | feeds:read |
| Published schedule as JSON | GET /v1/channels/{id}/schedule.json | feeds:read |
| Index of channels | GET /v1/channels.json | feeds:read |
| Playout playlist | GET /v1/channels/{id}/playlist.json | feeds:read |
| Transmission schedule (JSON) | GET /v1/channels/{id}/transmission.json | feeds:read |
| Transmission schedule (XML) | GET /v1/channels/{id}/transmission.xml | feeds:read |
| Transmission schedule (CSV) | GET /v1/channels/{id}/transmission.csv | feeds:read |