API guide

Overview

The API is open, read-only and needs no key. Every response is JSON encoded as UTF-8 and has an ok field.

Base URLhttps://api.embedse.com/v1
MethodsGET, HEAD
CORSAllowed from any origin
TimesUnix timestamps in seconds, UTC
FreshnessData refreshes every 30 seconds; responses are cached for up to 15 seconds

Match and stream IDs are short opaque strings such as k7Qx9PmA. Store them as strings and do not try to parse them.

Rate limits

Each IP address may send 60 requests per minute with a short burst allowance. Every response carries RateLimit-Policy: 60;w=60. Over the limit the API answers 429 with a Retry-After header. Poll the match list every 30–60 seconds; polling faster returns the same cached data.

Successful responses have an ETag header. Send it back in If-None-Match and the API answers 304 Not Modified with an empty body when nothing changed.

Call the API from your server and cache the result there. Your visitors should load your cached copy, not the API directly.

Errors

{ "ok": false, "error": "match_not_found" }
StatusErrorMeaning
400invalid_statusThe status filter is not one of the allowed values
400invalid_sourceThe source filter contains an unknown source ID
404match_not_found, stream_not_found, not_foundUnknown ID or path
405method_not_allowedOnly GET and HEAD are accepted
429rate_limitedToo many requests from your IP
503data_unavailableData is temporarily out of date; retry shortly

List matches

GET /v1/matches

Matches come in this order: live matches with an online server, live matches with other servers, live matches without servers, upcoming matches by start time, then ended matches with the most recent first. Ended matches stay in the list for 6 hours and have no servers.

QueryValues
statuslive, upcoming or ended
sportSport ID. Common values: soccer, basketball, american-football, hockey, baseball, combat, racing, tennis, golf
categoryCategory slug, for example nba or nhl
embeddable1 to return only matches that have at least one server
sourceOne source ID or a comma separated list, for example alpha,bravo. Returns only matches with a server from those sources; counts cover those servers only
curl "https://api.embedse.com/v1/matches?status=live&embeddable=1"
curl "https://api.embedse.com/v1/matches?status=live&source=bravo"
{
  "ok": true,
  "updated_at": 1794590000,
  "count": 1,
  "matches": [
    {
      "id": "k7Qx9PmA",
      "title": "Chicago Bulls vs Atlanta Hawks",
      "category": "NBA",
      "category_slug": "nba",
      "sport": "basketball",
      "league": "NBA",
      "home": "Chicago Bulls",
      "away": "Atlanta Hawks",
      "home_logo": "https://embedse.com/logos/3f9a1c0b7e5d2a4c6b81.png",
      "away_logo": "https://embedse.com/logos/9c2e7d4a1b0f3e6c5a92.png",
      "start": 1794600000,
      "end": 1794612000,
      "status": "live",
      "streams_count": 2,
      "online_streams": 1,
      "embed_url": "https://embedse.com/m/k7Qx9PmA"
    }
  ]
}

Get a match

GET /v1/matches/{id}

Returns one match object with its streams array included. Add ?source=alpha to keep only servers from that source.

Match streams

GET /v1/matches/{id}/streams

Accepts the same source filter.

{
  "ok": true,
  "updated_at": 1794590000,
  "match_id": "k7Qx9PmA",
  "count": 1,
  "streams": [
    {
      "id": "Zr4mT1bQ",
      "server": 1,
      "name": "Bravo",
      "source": "bravo",
      "language": "en",
      "status": "online",
      "embed_url": "https://embedse.com/m/k7Qx9PmA/1"
    }
  ]
}

Stream status

GET /v1/streams/{id}/status

{
  "ok": true,
  "updated_at": 1794590000,
  "stream": {
    "id": "Zr4mT1bQ",
    "server": 1,
    "name": "Bravo",
    "source": "bravo",
    "language": "en",
    "status": "online",
    "embed_url": "https://embedse.com/m/k7Qx9PmA/1"
  },
  "match_id": "k7Qx9PmA",
  "match_status": "live"
}

Sources

GET /v1/sources

Every server belongs to a source with a fixed ID: alpha, bravo, charlie, delta, echo, foxtrot, golf, hotel. IDs never change, so you can store them and use them in the source filter. Use the name of a server as its display label. This endpoint lists every source with its current counts.

{
  "ok": true,
  "updated_at": 1794590000,
  "count": 7,
  "sources": [
    {
      "id": "alpha",
      "name": "Alpha",
      "matches": 12,
      "live_matches": 9,
      "streams": 20,
      "online_streams": 18
    }
  ]
}

Categories

GET /v1/categories

Lists every category that has matches right now, with the most live matches first. Use slug in the category filter. sport is null for categories that mix several sports.

{
  "ok": true,
  "updated_at": 1794590000,
  "count": 1,
  "categories": [
    {
      "slug": "nba",
      "name": "NBA",
      "sport": "basketball",
      "matches": 11,
      "live_matches": 4,
      "embeddable_matches": 6
    }
  ]
}

Embedding

Every match has one embed link. Each server also has its own link, which is the match link plus the server number.

Wrap the iframe in a 16:9 box so it scales with the page width:

<div style="position:relative;width:100%;aspect-ratio:16/9;background:#000">
  <iframe src="https://embedse.com/m/k7Qx9PmA"
    style="position:absolute;inset:0;width:100%;height:100%;border:0"
    allow="autoplay; fullscreen; encrypted-media; picture-in-picture"
    allowfullscreen referrerpolicy="origin"></iframe>
</div>
LinkBehavior
/m/{match_id}Automatic. The player opens the first online server. If it stops working, the player switches to the next server on its own. Viewers can pick a server from the Sources menu in the top left corner of the player. The menu shows only when there is more than one server.
/m/{match_id}/{server}Single server. The player plays only that server and shows no menu. If the server goes down, the player retries the same server and never switches to another one. Use this link when you show your own server list above the player.

The match embed_url is the automatic link. Each stream's embed_url is a single server link.

Server numbers can change when servers are added or removed. Read them again from the API instead of storing them for a long time. A single server link whose server no longer exists shows a "no servers available" message.

Filtering by source

Add ?source=bravo (or a comma separated list) to an automatic link to limit the player and its menu to servers from those sources. Links returned by a request with the source filter already include it.

https://embedse.com/m/k7Qx9PmA?source=alpha
https://embedse.com/m/k7Qx9PmA?source=alpha,bravo

Do not add the sandbox attribute. Sandboxed frames are detected and will not play.

Player events

The player sends messages to the parent page with postMessage. Every message has type: "embedse", event, match (the match ID) and status (the match status).

EventWhenExtra fields
readyThe player has loadedservers: number of servers
serverThe player starts a serverserver, name, manual (true when the viewer picked it)
playingVideo starts playing on a serverserver, name
unavailableNothing can play right nowreason: not_started, no_servers or all_failed
endedThe match has ended
window.addEventListener('message', function (e) {
  if (e.origin !== 'https://embedse.com' || !e.data || e.data.type !== 'embedse') return;
  if (e.data.event === 'playing') console.log('Playing', e.data.name);
  if (e.data.event === 'ended') console.log('Match ended');
});

Quick start

List live matches that have servers and put the first one on the page:

const res = await fetch('https://api.embedse.com/v1/matches?status=live&embeddable=1');
const data = await res.json();
if (data.ok && data.matches.length) {
  const m = data.matches[0];
  const frame = document.createElement('iframe');
  frame.src = m.embed_url;
  frame.allow = 'autoplay; fullscreen; encrypted-media; picture-in-picture';
  frame.allowFullscreen = true;
  frame.style.cssText = 'width:100%;aspect-ratio:16/9;border:0';
  document.getElementById('player').appendChild(frame);
}

Ads

The API is free and has no ads. The JSON responses never contain ads.

The embed player runs one ad network script to keep the service free. When a viewer clicks the player, at most one ad opens every 10 seconds. The player shows no banners, overlays or pre-roll video ads, and it never blocks or pauses the stream.

Ad-free embeds are available. See Contact.

Contact

Use the contact form for:

  • Ad-free embeds for your website
  • Custom specs for your project
  • Partnership, bug reports and other questions

auth.streamea.st/contact

Objects

Match

FieldTypeNotes
idstringOpaque match ID
titlestring
category, category_slugstring
sportstring | nullSee the sport filter for values
leaguestring | nullCompetition name, for example Premier League; null when unknown
home, awaystring | nullNull for events without two sides
home_logo, away_logostring | nullTeam logo URL; null when there is no logo
start, endintegerScheduled start and expected end. For ended matches end is when the match ended
statusstringlive, upcoming, ended
streams_count, online_streamsinteger
embed_urlstring | nullNull when the match has no servers

Stream

FieldTypeNotes
idstringOpaque stream ID
serverintegerPosition in the match server list, starting at 1
namestringDisplay label
sourcestring | nullSource ID; null when the server has no source
languagestring | nullCommentary language code such as en; null when unknown
statusstringonline, idle (not started yet), offline
embed_urlstring