Code examples
Working curl, JavaScript and Python reads of the public sample.
These public examples read fixture 0042500401, feed pulse. They require no key. Run them as written against the public API.
curl
curl --fail-with-body --silent --show-error 'https://api.live.storyos.ai/v1/fixtures/0042500401/feeds/pulse'JavaScript
Run in Node.js 22+ or a browser developer console. It makes two bounded requests; it does not choose a polling interval.
// Node.js 22+, or a browser's developer console. Public sample; no key.
const url = 'https://api.live.storyos.ai/v1/fixtures/0042500401/feeds/pulse';
let etag;
let snapshot;
async function pollOnce() {
const response = await fetch(url, {
headers: etag ? { 'If-None-Match': etag } : {},
cache: 'no-store',
});
if (response.status === 304) return false; // Keep the current snapshot.
if (!response.ok) {
throw new Error('HTTP ' + response.status + ': ' + await response.text());
}
const next = await response.json();
snapshot = next; // Replace the whole snapshot, even if items is empty.
etag = response.headers.get('ETag');
return true;
}
(async () => {
console.log('Updated:', await pollOnce());
console.log(snapshot.fixtureId, snapshot.items.length, 'items');
console.log('Updated:', await pollOnce()); // false when unchanged
})().catch(console.error);Python
Python 3, using the standard library. urllib treats 304 as an HTTPError, so the example handles that before rethrowing other failures.
# Python 3. Standard library only. Public sample; no key.
import json
from urllib.error import HTTPError
from urllib.request import Request, urlopen
url = 'https://api.live.storyos.ai/v1/fixtures/0042500401/feeds/pulse'
etag = None
snapshot = None
def poll_once():
global etag, snapshot
headers = {'User-Agent': 'Storyteller-Content-API-Example/1.0', 'Accept': 'application/json'}
if etag:
headers['If-None-Match'] = etag
try:
with urlopen(Request(url, headers=headers), timeout=15) as response:
next_snapshot = json.load(response)
next_etag = response.headers.get('ETag')
except HTTPError as error:
if error.code == 304:
return False # Keep the current snapshot.
raise # Keep state and let the caller handle the error.
snapshot = next_snapshot # Includes replacement with an empty items list.
etag = next_etag
return True
print('Updated:', poll_once())
print(snapshot['fixtureId'], len(snapshot['items']), 'items')
print('Updated:', poll_once()) # False when unchangedRead a live game
Set the live game's NBA game ID. No key is needed.
# A live game's feed: set FIXTURE_ID to its NBA game ID. No key.
: "${FIXTURE_ID:?Set a live NBA game ID}"
curl --fail-with-body --silent --show-error \
"https://api.live.storyos.ai/v1/fixtures/$FIXTURE_ID/feeds/pulse"Keep request scopes separate
The examples keep state for one fixed public URL. Production clients need independent snapshot/ETag state for each exact fixture/feed/query combination. Reset that state when switching replay bounds. A failed request preserves prior state; decide how your application shows stale data and schedules bounded retries.