Feed API
The Feed API gives you the posts LoopFeed’s AI curation has matched to a match, team, player or competition: highlights, clips, previews, interviews, news and more, from every source LoopFeed curates. You don’t need to set up topics or sources.
GET https://q.loop-feed.com/v1/feeds/football/team/9→ the latest posts about ArsenalQuick start
Section titled “Quick start”-
Get your API key from LoopFeed. It looks like
lfk_followed by 43 characters. It is shown only once, so store it in your server’s secrets. -
Request a feed from your server:
Terminal window curl -H "Authorization: Bearer lfk_your_key" \"https://q.loop-feed.com/v1/feeds/football/team/9?limit=5" -
Show it in a browser or app: your server mints a short-lived feed token, and the page uses the token instead of the key.
GET /v1/feeds/{sport}/{kind}/{id}Sport ({sport}) | Kinds ({kind}) |
|---|---|
football: association football (soccer) | match, team, player, competition |
rugby: rugby league | match, team, competition |
rugby-union: rugby union and sevens | match, team, competition |
{id} is the numeric ID of the match, team, player or competition in LoopFeed’s sports data. See
Finding IDs.
- A competition feed covers every season of that competition. Its ID stays the same from one season to the next.
- Player feeds are football only.
- IDs are unique only within a sport. Football team
18and rugby league team18are different teams, so always include the sport.
Query parameters
Section titled “Query parameters”| Parameter | Default | Description |
|---|---|---|
limit | 20 | Posts per page, up to 100. |
cursor | The next value from the previous page. See Paging. | |
contentType | Only these content types, comma-separated, at most 5. For example highlights,clip. | |
service | Only one platform: youtube, twitter, instagram or rss. | |
token | A feed token, for browsers and apps instead of the API key. |
Response
Section titled “Response”{ "content": [ { "id": "cmh0x1y2z0001abcdpost00001", "externalId": "m0G_UKNoSqY", "service": "youtube", "type": "video", "title": "MADUEKE THE MATCHWINNER! | EXTENDED HIGHLIGHTS | Nottingham Forest vs Arsenal (0-1)", "text": "Noni Madueke's late goal was enough to secure a valuable 1-0 win away to Nottingham Forest...", "url": "https://www.youtube.com/watch?v=m0G_UKNoSqY", "date": "2026-10-19T23:00:00Z", "created": "2026-10-19T23:04:12.521Z", "author": { "name": "Arsenal", "username": "Arsenal", "url": "https://youtube.com/channel/UCpryVRk_VDudG8SHXgWcG0w", "verified": true }, "media": [ { "type": "embed", "embed": "<iframe src='https://www.youtube.com/embed/m0G_UKNoSqY'></iframe>" }, { "type": "image", "url": "https://i.ytimg.com/vi/m0G_UKNoSqY/maxresdefault.jpg", "width": 1280, "height": 720 } ], "metadata": { "contentType": "highlights", "matches": [ { "sport": "football", "matchId": "518066", "homeTeamId": "11", "homeTeamName": "Nottingham Forest", "awayTeamId": "9", "awayTeamName": "Arsenal", "leagueId": "10001", "leagueName": "England - Premier League", "startDateTime": "2026-10-18T15:30:00.000Z" } ], "entities": [ { "sport": "football", "kind": "competition", "id": "10001", "name": "Premier League" }, { "sport": "football", "kind": "team", "id": "9", "name": "Arsenal" }, { "sport": "football", "kind": "team", "id": "11", "name": "Nottingham Forest" }, { "sport": "football", "kind": "player", "id": "4410", "name": "Noni Madueke" } ], "custom": {} }, "pinned": false } ], "next": "MjAyNi0xMC0xOVQyMzowMDowMFp8Y21oMHgxeTJ6MDAwMWFiY2Rwb3N0MDAwMDE"}The example is shortened. Each post has the same shape as items from the
topic content API, and its curation is in metadata:
| Field | Description |
|---|---|
metadata.contentType | The post’s content type, for example highlights. |
metadata.matches | The match the post is about, if any: its ID, teams, competition and kick-off time. |
metadata.entities | The teams, players and competitions the post is about. Each has a sport, kind, id and name. |
- Posts are newest first, by the time they were published.
- If the same video was posted by several sources, it appears once.
- The match
statusandscoreinmetadata.matchesare a snapshot from when the post was curated. Use your live sports data for current scores.
Paging
Section titled “Paging”next is the cursor of the following page, or null on the last page. Pass it back unchanged as cursor, with the
same filters:
curl -H "Authorization: Bearer lfk_your_key" \ "https://q.loop-feed.com/v1/feeds/football/team/9?limit=20&cursor=MjAyNi0xMC0xOVQyMzowMDowMFp8Y21oMHgxeTJ6MDAwMWFiY2Rwb3N0MDAwMDE"To check for new posts, request the first page again (no cursor).
Authentication
Section titled “Authentication”There are two ways to authenticate. Use the one that matches where the request comes from.
| From | Send | Opens |
|---|---|---|
| Your servers | The API key, in a header | Every feed of the sports your subscription covers |
| Browsers and apps | A feed token, as ?token= | One feed, until the token expires |
Servers: API key
Section titled “Servers: API key”Send the key in either header:
Authorization: Bearer lfk_your_keyX-API-Key: lfk_your_keyIf you gave LoopFeed your servers’ IP addresses, the key only works from those addresses. Servers that have IPv6 usually connect over it, so give us their IPv6 addresses (or range) as well as their IPv4 ones. A refused request says which address it came from.
Browsers and apps: feed tokens
Section titled “Browsers and apps: feed tokens”A feed token opens one feed for a limited time, from one hour by default up to one day. If a token leaks, only that one feed is exposed, and only until the token expires. Your server mints tokens with the API key:
POST /v1/feeds/tokensAuthorization: Bearer lfk_your_keyContent-Type: application/json
{ "scheme": "football", "kind": "team", "id": "9", "ttlSeconds": 3600 }| Field | Description |
|---|---|
scheme | The sport: football, rugby or rugby-union. |
kind | match, team, player or competition. |
id | The numeric ID. |
ttlSeconds | Optional. How long the token works: 60 to 86400 seconds, default 3600. |
The response (201 Created) has the token, its expiry and the path it opens:
{ "token": "Y21zdWIxMjN8Zm9vdGJhbGx8dGVhbXwxM3wxNzYwOTg2MDAw.q2V0...", "expiresAt": "2026-10-20T19:00:00Z", "url": "/v1/feeds/football/team/9?token=Y21zdWIxMjN8Zm9vdGJhbGx8dGVhbXwxM3wxNzYwOTg2MDAw.q2V0..."}The page then requests https://q.loop-feed.com + url, adding any other parameters with &:
// GET /feed-token?team=9 on your site: returns a token for one team's feedapp.get('/feed-token', async (req, res) => { const response = await fetch('https://q.loop-feed.com/v1/feeds/tokens', { method: 'POST', headers: { Authorization: `Bearer ${process.env.LOOPFEED_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ scheme: 'football', kind: 'team', id: String(req.query.team) }), }); res.status(response.status).json(await response.json());});const { url } = await (await fetch('/feed-token?team=9')).json();const feed = await (await fetch(`https://q.loop-feed.com${url}&limit=10&contentType=highlights`)).json();
for (const post of feed.content) { console.log(post.metadata.contentType, post.title, post.url);}Mint a new token when the old one expires, or shortly before. Tokens stop working within a minute if LoopFeed pauses your subscription or rotates your key.
Finding IDs
Section titled “Finding IDs”Feeds use the IDs of LoopFeed’s sports data provider, the same as the rest of LoopFeed. For example, Arsenal is
football team 9, and the Premier League is football competition 10001.
- From a post: every curated post lists its teams, players and competitions in
metadata.entities, and its match inmetadata.matches, with their IDs. - From your topics: the Content API lists the teams you set on
each topic, and the
leagueIdof each attached match. - Ask us: your account manager can send you the IDs for the competitions you cover.
Caching and limits
Section titled “Caching and limits”- Caching: responses can be cached for 60 seconds (
Cache-Control: private, max-age=60). Each response has anETag. Send it back asIf-None-Match, and you get304 Not Modifiedwith no body when nothing changed. - Rate limit: your subscription allows a set number of requests a minute. Over that, you get
429with aRetry-Afterheader (seconds). Cache feeds on your server rather than requesting them for every visitor.
Errors
Section titled “Errors”Errors return JSON with a message:
{ "error": "This feed token is for another feed" }| Status | Meaning |
|---|---|
400 | A parameter is invalid: a non-numeric ID, an unknown contentType or service, a bad cursor, or ttlSeconds out of range. |
401 | No API key or token, or the key or token is unknown or expired, or the subscription is paused. |
403 | The subscription doesn’t cover this sport, the request came from an IP address the key doesn’t allow (the message names the address), the API key was sent from a browser, or the token is for a different feed. |
404 | Unknown sport or kind in the path. |
429 | Rate limit exceeded. Wait for Retry-After seconds. |
5xx | A temporary problem on our side. Retry with backoff. |
Feeds and topics
Section titled “Feeds and topics”| Feed API | Topic content API | |
|---|---|---|
| What you get | Posts about one match, team, player or competition, from every source LoopFeed curates | Your topic’s posts, from the sources and filters you chose |
| Moderation | LoopFeed’s AI, checked by LoopFeed reviewers | You: hide, pin and edit in your queue |
| Access | Subscription and API key | Public, by topic ID |