API reference
Live feeds
Everything Clipped reads off a streamer, as it reads it, over a WebSocket. Three per streamer, so a program takes only what it uses: analysis (the signals Clipped scores the stream on, second by second, and the clips it finds), transcript (what the streamer says) and chat (every message).
A feed follows a streamer, not one broadcast. Open it whenever you like and keep it open: while they are offline it sends waiting; when a recording starts it sends started, then a backlog of the recent past marked "backlog": true, then everything as it happens; when the recording stops it sends ended and waits for the next. Clipped records the streamers you follow while they are live, so follow one first (POST /api/v1/streamers) — a feed for anyone else is refused.
A feed only reads along. It never starts a recording and never keeps one running, so a program left connected costs nothing while the streamer is offline.
Send the key as Authorization: Bearer when opening the socket. A browser can't set headers on a WebSocket, so there ?key= works too — only put a key in a page you trust with it. Opening a feed counts as one call against your rate limit, however long it stays open; an account can have 60 open at once. A refused feed is answered before the upgrade, with the usual status and error body. A revoked key, a suspended account or an unfollowed streamer closes an open one within five minutes, with close code 1008. A client that stops reading is disconnected rather than buffered for. Reconnect and you get a fresh backlog.
Every message is one JSON object: feed, channel, session (the recording's id, or null while waiting), at (when it was sent, ms), type, and for most types data. Times inside data are wall-clock milliseconds, except a second's t, which is epoch seconds.
Endpoints
/api/v1/streamsEach streamer you follow, whether a recording of them is running, and their three feed addresses.
/api/v1/streams/{channel}/analysisThe signals, second by second, and the clips found.
?audio=false leaves out the 10-a-second audio meters.
/api/v1/streams/{channel}/transcriptWhat the streamer says, a segment at a time, with each word's timing.
/api/v1/streams/{channel}/chatEvery chat message, and each one's score once it has one.
What is sent
Every feed
waiting—No recording of this streamer is running for you: they are offline, or not recording yet.
started—A recording started, or was already running when you connected. Its backlog follows.
ended—That recording stopped. waiting follows, or started straight away if the streamer is already on their next broadcast.
analysis
snapshot{ recording, seconds, moments, words }The backlog: the recording, its recent seconds, every clip found so far, and the word counts.
recordingrecordingThe stream's title, game, viewers, whether it is live, and which of chat, audio, transcription and scoring are working. Sent when any of that changes.
secondssecond[]One per second: messages, chatters, emotes, sentiment (sentSum / sentN), messages per emotion, loudness and peak (dBFS), pitch, brightness, words spoken, the streamer's excitement, and hype, the 0–100 score clips are found on, with the chat and loudness z-scores behind it. A second can be sent again as late scores land: keep the last one for each t.
audio{ t, db, peak, pitch, centroid, bands }The audio meters, about ten times a second.
words{ window, recent, session, streamer }What chat keeps saying — words, emotes, repeated phrases, and what is trending — over the last minute and the whole recording, and what the streamer keeps saying.
sentiment{ id, t, sentiment, emotion, confidence }[]Chat messages scored: sentiment −1 to 1, and an emotion (hype, funny, shock, love, sad, anger, neutral).
momentmomentA clip found, or one that changed: reviewed, titled, rescored. Its id is the one /api/v1/clips/{id} takes. Includes its score, the triggers that fired, its title and summary, the words and emotes around it, its transcript, a link into the VOD, and its thumbnail: the image's address, or null until it is made, when the moment is sent again with it.
transcript
transcriptsegment[]{ id, start, end, text, language, words: [{ text, start, end }], excitement, tone }. The backlog is the last 150 segments.
chat
chatmessage[]{ id, t, user, name, color, badges, text, emotes, kind, bits, platform }, where kind is chat, sub, raid, bits or announcement and emotes gives each emote's place in text and its image. The backlog is the last 200 messages.
sentiment{ id, t, sentiment, emotion, confidence }[]Scores for messages already sent, by their id.
Examples
import WebSocket from "ws"
const feed = new WebSocket("wss://app.clipped.so/api/v1/streams/jynxzi/analysis?audio=false", {
headers: { Authorization: "Bearer clip_sk_…" },
})
feed.on("unexpected-response", (req, res) => console.error("refused:", res.statusCode))
feed.on("message", (raw) => {
const m = JSON.parse(raw)
if (m.type === "seconds" && !m.backlog) for (const s of m.data) console.log(s.t, "hype", s.hype, "chat", s.msgs)
if (m.type === "moment") console.log("clip:", m.data.id, m.data.score, m.data.title)
})
feed.on("close", () => setTimeout(reconnect, 5000)) // and open it again
const chat = new WebSocket("wss://app.clipped.so/api/v1/streams/jynxzi/chat?key=clip_sk_…")
chat.onmessage = (e) => {
const m = JSON.parse(e.data)
if (m.type === "chat") for (const msg of m.data) console.log(msg.name + ":", msg.text)
}
{ "feed": "transcript", "channel": "jynxzi", "session": null, "at": 1791918000000, "type": "waiting" }
{ "feed": "transcript", "channel": "jynxzi", "session": "jynxzi-41234567", "at": 1791921600000, "type": "started" }
{ "feed": "transcript", "channel": "jynxzi", "session": "jynxzi-41234567", "at": 1791921600000, "type": "transcript", "backlog": true, "data": [] }
{
"feed": "transcript", "channel": "jynxzi", "session": "jynxzi-41234567", "at": 1791921612400, "type": "transcript",
"data": [{
"id": "s1791921605120",
"start": 1791921605120, "end": 1791921609880,
"text": "No way. No way he just did that.",
"language": "en",
"words": [{ "text": "No", "start": 1791921605120, "end": 1791921605300 }, …]
}]
}