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 needUseFormat
EPG for one channelGET /v1/channels/{id}/epg.xmlXMLTV
EPG for every channel in one fileGET /v1/epg/master.xmlXMLTV
Programme list for your own appGET /v1/channels/{id}/schedule.jsonJSON
A list of your channels and their feed URLsGET /v1/channels.jsonJSON
A playout system polling for what's nextGET /v1/channels/{id}/playlist.jsonJSON
A running order with ad breaks for playout automationGET /v1/channels/{id}/transmission.csv, .xml or .jsonCSV, 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"
200 OK (trimmed)
<?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"
200 OK (trimmed)
{
  "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.

200 OK (trimmed)
{
  "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.

200 OK (trimmed)
{
  "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
QueryMeaning
from, toInclusive dates, YYYY-MM-DD, on the channel's own clock. Leave both out for the whole published version
profileA column profile. Built in: fast-transmission (default), running-order, fast-transmission-scte, epg-listing, amagi-playlist. An unknown slug is 404 NOT_FOUND
breaksderive (default: real pods with real durations), cues (zero-length markers only; the programme timeline is untouched) or none (programmes only)
typesComma-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-succeeded webhook or poll with If-None-Match.

Endpoints

OperationMethod + pathScope
XMLTV EPG for a channelGET /v1/channels/{id}/epg.xmlfeeds:read
XMLTV EPG for every channelGET /v1/epg/master.xmlfeeds:read
Published schedule as JSONGET /v1/channels/{id}/schedule.jsonfeeds:read
Index of channelsGET /v1/channels.jsonfeeds:read
Playout playlistGET /v1/channels/{id}/playlist.jsonfeeds:read
Transmission schedule (JSON)GET /v1/channels/{id}/transmission.jsonfeeds:read
Transmission schedule (XML)GET /v1/channels/{id}/transmission.xmlfeeds:read
Transmission schedule (CSV)GET /v1/channels/{id}/transmission.csvfeeds:read

On this page