Build custom overlays, integrations, and applications using the Ninja Chatter API.
The easiest way to receive chat messages for overlays is using the read-only stream endpoints. No authentication required!
const ws = new WebSocket('wss://api.ninjachatter.com/stream/YOUR_ROOM_ID');
ws.onmessage = (e) => {
const msg = JSON.parse(e.data);
console.log(`${msg.user.name}: ${msg.payload.text}`);
};
const events = new EventSource('https://api.ninjachatter.com/stream/YOUR_ROOM_ID/sse');
events.onmessage = (e) => {
const msg = JSON.parse(e.data);
console.log(`${msg.user.name}: ${msg.payload.text}`);
};
These endpoints provide read-only access to room chat messages. Perfect for building overlays, bots, and integrations.
| Endpoint | Protocol | Format | Description |
|---|---|---|---|
/stream/:room |
WS | Clean JSON | WebSocket stream with our Envelope format |
/stream/:room/sse |
GET | Clean JSON | Server-Sent Events stream |
/stream/:room/ssn |
WS | SSN Format | WebSocket stream in SSN-compatible format |
If you have existing SocialStream.ninja overlays, you can point them at our API with minimal changes.
Add the server parameter to your dock.html URL:
https://socialstream.ninja/dock.html?session=YOUR_ROOM_ID&server=wss://api.ninjachatter.com/stream/YOUR_ROOM_ID/ssn
YOUR_ROOM_ID with your actual room ID from the Dashboard.
The /stream/:room/ssn endpoint translates messages to SSN's format:
{
"id": "abc123def456",
"chatname": "Steve",
"chatmessage": "Hello world!",
"chatimg": "https://cdn.example.com/avatar.png",
"type": "twitch",
"hasDonation": null,
"membership": null,
"moderator": false,
"bot": false,
"userid": "user123",
"textonly": true
}
Used by /stream/:room and /stream/:room/sse:
{
"id": "abc123def456",
"type": "chat",
"room": "my-room-id",
"user": {
"id": "user123",
"name": "Steve",
"provider": "twitch",
"role": "viewer",
"avatar": "https://cdn.example.com/avatar.png"
},
"payload": {
"text": "Hello world!",
"source": "twitch",
"chatmessage": "<span>Hello world!</span>",
"textonly": false
},
"ts": 1702000000,
"meta": {}
}
| Field | Type | Description |
|---|---|---|
id | string | Unique message ID |
type | string | Always "chat" for stream endpoints |
room | string | Room ID |
user.id | string | User's unique ID |
user.name | string | Display name |
user.provider | string | Auth provider: discord, twitch, youtube, patreon, guest, webhook |
user.role | string | User role: viewer, publisher, mod, bot |
user.avatar | string? | Avatar URL (optional) |
payload.text | string | Plain text message content |
payload.source | string? | Original source platform (for SSN messages) |
payload.chatmessage | string? | HTML-formatted message (for SSN messages) |
payload.textonly | boolean? | If false, chatmessage contains HTML |
ts | number | Unix timestamp (seconds) |
meta | object | Additional metadata |
The SSE endpoint is a simpler alternative to WebSocket that works in more environments.
EventSource APIconst source = new EventSource('https://api.ninjachatter.com/stream/YOUR_ROOM_ID/sse');
source.onmessage = (event) => {
const msg = JSON.parse(event.data);
displayMessage(msg.user.name, msg.payload.text, msg.user.avatar);
};
source.onerror = (err) => {
console.log('Connection error, will auto-reconnect...');
};
For full chat participation (sending messages, reactions, etc.), use the authenticated WebSocket endpoint.
WS /ws/:room?token=JWT_TOKEN
Use the Ingress API to mint tokens, or use OAuth login flows.
// Full-featured client with auto-reconnect
const client = ChatEmbed.createChatClient({
room: 'YOUR_ROOM_ID',
apiBase: 'https://api.ninjachatter.com',
wsBase: 'wss://api.ninjachatter.com',
onMessage: (msg) => {
console.log(`${msg.user.name}: ${msg.payload.text}`);
},
onStateChange: (state) => {
console.log('Connection state:', state);
}
});
// Mint a guest token and connect
const token = await ChatEmbed.mintGuestToken({
room: 'YOUR_ROOM_ID',
apiBase: 'https://api.ninjachatter.com',
username: 'Guest User'
});
client.connect(token);
Copy this HTML into an OBS Browser Source:
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0;
font-family: sans-serif;
background: transparent;
}
#chat {
display: flex;
flex-direction: column;
gap: 8px;
padding: 16px;
}
.msg {
background: rgba(0,0,0,0.7);
color: white;
padding: 8px 12px;
border-radius: 8px;
animation: fadeIn 0.3s ease;
}
.msg img {
width: 24px;
height: 24px;
border-radius: 50%;
vertical-align: middle;
margin-right: 8px;
}
.msg .name { font-weight: bold; color: #38bdf8; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } }
</style>
</head>
<body>
<div id="chat"></div>
<script>
const ROOM_ID = 'YOUR_ROOM_ID'; // Change this!
const MAX_MESSAGES = 10;
const ws = new WebSocket(`wss://api.ninjachatter.com/stream/${ROOM_ID}`);
const chat = document.getElementById('chat');
ws.onmessage = (e) => {
const msg = JSON.parse(e.data);
const div = document.createElement('div');
div.className = 'msg';
div.innerHTML = `
${msg.user.avatar ? `<img src="${msg.user.avatar}" alt="">` : ''}
<span class="name">${msg.user.name}:</span>
<span>${msg.payload.text}</span>
`;
chat.appendChild(div);
// Remove old messages
while (chat.children.length > MAX_MESSAGES) {
chat.firstChild.remove();
}
};
ws.onclose = () => setTimeout(() => location.reload(), 5000);
</script>
</body>
</html>
import { useState, useEffect } from 'react';
function useChatStream(roomId) {
const [messages, setMessages] = useState([]);
const [connected, setConnected] = useState(false);
useEffect(() => {
const ws = new WebSocket(`wss://api.ninjachatter.com/stream/${roomId}`);
ws.onopen = () => setConnected(true);
ws.onclose = () => setConnected(false);
ws.onmessage = (e) => {
const msg = JSON.parse(e.data);
setMessages(prev => [...prev.slice(-99), msg]);
};
return () => ws.close();
}, [roomId]);
return { messages, connected };
}
// Usage
function ChatOverlay({ roomId }) {
const { messages, connected } = useChatStream(roomId);
return (
<div>
{messages.map(msg => (
<div key={msg.id}>
<strong>{msg.user.name}:</strong> {msg.payload.text}
</div>
))}
</div>
);
}
import asyncio
import json
import websockets
async def listen_chat(room_id):
uri = f"wss://api.ninjachatter.com/stream/{room_id}"
async with websockets.connect(uri) as ws:
print(f"Connected to room: {room_id}")
async for message in ws:
msg = json.loads(message)
print(f"{msg['user']['name']}: {msg['payload']['text']}")
# Run the listener
asyncio.run(listen_chat("YOUR_ROOM_ID"))
Questions? Feedback? GitHub ยท Social Stream Ninja