Showroom Chat Stream
Get JKT48 live chat data from Showroom streams using JKT48Connect API
Introduction
The JKT48Connect Showroom Chat Stream API provides access to real-time chat messages from JKT48 member live streams on Showroom. Use this endpoint to fetch live chat data using room_id obtained from the showroom live data API.
Real-time Chat
Access live chat messages from ongoing Showroom streams.
User Interactions
Track viewer engagement and member responses on Showroom.
Live Updates
Stream chat data in real-time for interactive applications.
Quick Start
Get Showroom Live Data
First, fetch showroom live data to get room_id parameter.
const liveData = await getShowroomLive();
const { room_id } = liveData.live_streams[0]; // Get from live stream
Fetch Chat Stream
curl "https://v2.jkt48connect.my.id/api/jkt48/chat-stream-sr?room_id=123456&apikey=YOUR_API_KEY"
Process Chat Messages
Handle real-time chat data and user interactions.
Endpoint Details
Base URL: https://v2.jkt48connect.my.id
Endpoint: /api/jkt48/chat-stream-sr
Method: GET
Authentication: API Key required
Query Parameters:
room_id
(required): Showroom room ID from live dataapikey
(required): Your API authentication keylimit
(optional): Number of messages to fetch (default: 50)since
(optional): Timestamp to fetch messages since
Example:
GET /api/jkt48/chat-stream-sr?room_id=123456&apikey=YOUR_API_KEY HTTP/1.1
Host: v2.jkt48connect.my.id
Returns live chat messages and metadata:
{
"stream_info": {
"room_id": "123456",
"room_name": "Freya JKT48",
"title": "Weekend Chat with Freya",
"status": "live",
"viewers": 2150,
"duration": "01:45:30",
"member": {
"name": "Freya Jayawardana",
"nickname": "Freya",
"team": "KIII"
}
},
"chat_messages": [
{
"id": "sr_msg_001",
"user": {
"user_id": "12345",
"username": "showroom_fan",
"display_name": "JKT48 Fan",
"avatar": "https://image.showroom-live.com/avatar/12345.jpg",
"level": 15,
"is_premium": false
},
"message": "Halo Freya! Semangat live nya!",
"timestamp": "2025-06-29T14:30:15.000Z",
"type": "chat",
"gifts": null,
"stars": 0
},
{
"id": "sr_msg_002",
"user": {
"user_id": "67890",
"username": "premium_fan",
"display_name": "Premium Fan",
"avatar": "https://image.showroom-live.com/avatar/67890.jpg",
"level": 25,
"is_premium": true
},
"message": "🌟✨",
"timestamp": "2025-06-29T14:31:00.000Z",
"type": "gift",
"gifts": {
"gift_id": "star",
"gift_name": "Star",
"quantity": 5,
"total_stars": 50
},
"stars": 50
}
],
"stats": {
"total_messages": 1547,
"unique_users": 587,
"messages_per_minute": 18.2,
"total_stars": 15420,
"gift_count": 67,
"top_gifts": ["star", "heart", "clap"]
}
}
Implementation Examples
const API_KEY = 'YOUR_API_KEY';
const BASE_URL = 'https://v2.jkt48connect.my.id';
async function getShowroomChatStream(roomId, options = {}) {
const params = new URLSearchParams({
room_id: roomId,
apikey: API_KEY,
...options
});
try {
const response = await fetch(
`${BASE_URL}/api/jkt48/chat-stream-sr?${params}`
);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error('Failed to fetch showroom chat stream:', error);
throw error;
}
}
// Get recent chat messages
async function getRecentShowroomMessages(roomId, limit = 20) {
return await getShowroomChatStream(roomId, { limit });
}
// Poll for new messages
async function pollShowroomChatMessages(roomId, callback) {
let lastTimestamp = new Date().toISOString();
const poll = async () => {
try {
const data = await getShowroomChatStream(roomId, {
since: lastTimestamp,
limit: 10
});
if (data.chat_messages.length > 0) {
callback(data.chat_messages);
lastTimestamp = data.chat_messages[0].timestamp;
}
} catch (error) {
console.error('Polling error:', error);
}
};
// Poll every 2 seconds
setInterval(poll, 2000);
poll(); // Initial call
}
// Format chat message
function formatShowroomChatMessage(message) {
const time = new Date(message.timestamp).toLocaleTimeString('id-ID');
const level = `Lv.${message.user.level}`;
const premium = message.user.is_premium ? '💎' : '';
return {
id: message.id,
time: time,
user: `${premium}${level} ${message.user.display_name}`,
text: message.message,
stars: message.stars,
isGift: message.type === 'gift',
gifts: message.gifts
};
}
// Display live chat
async function displayShowroomLiveChat(roomId) {
try {
const data = await getShowroomChatStream(roomId);
console.log(`=== SHOWROOM LIVE CHAT: ${data.stream_info.title} ===`);
console.log(`Member: ${data.stream_info.member.name} (${data.stream_info.member.team})`);
console.log(`Viewers: ${data.stream_info.viewers} | Duration: ${data.stream_info.duration}`);
console.log(`Messages/min: ${data.stats.messages_per_minute} | Total Stars: ${data.stats.total_stars}`);
console.log('Top gifts:', data.stats.top_gifts.join(' '));
console.log('\n--- RECENT MESSAGES ---');
data.chat_messages.forEach(msg => {
const formatted = formatShowroomChatMessage(msg);
if (formatted.isGift) {
console.log(`🎁 [${formatted.time}] ${formatted.user} sent ${formatted.gifts.quantity}x ${formatted.gifts.gift_name} (${formatted.stars} stars)`);
} else {
console.log(`💬 [${formatted.time}] ${formatted.user}: ${formatted.text}`);
}
});
} catch (error) {
console.error('Error displaying showroom chat:', error);
}
}
// Chat analytics
function analyzeShowroomChatData(chatData) {
const messages = chatData.chat_messages;
const giftMessages = messages.filter(m => m.type === 'gift');
const chatMessages = messages.filter(m => m.type === 'chat');
return {
totalMessages: messages.length,
chatMessages: chatMessages.length,
giftMessages: giftMessages.length,
uniqueUsers: new Set(messages.map(m => m.user.user_id)).size,
totalStars: messages.reduce((sum, m) => sum + m.stars, 0),
averageUserLevel: messages.reduce((sum, m) => sum + m.user.level, 0) / messages.length,
premiumUsers: messages.filter(m => m.user.is_premium).length,
topGivers: getTopGivers(giftMessages)
};
}
// Real-time showroom chat monitor
class ShowroomChatMonitor {
constructor(roomId) {
this.roomId = roomId;
this.isMonitoring = false;
}
start(onMessage) {
if (this.isMonitoring) return;
this.isMonitoring = true;
pollShowroomChatMessages(this.roomId, (messages) => {
messages.forEach(msg => {
const formatted = formatShowroomChatMessage(msg);
onMessage(formatted);
});
});
}
stop() {
this.isMonitoring = false;
}
}
// Gift tracking
function trackGifts(chatData) {
const giftMessages = chatData.chat_messages.filter(m => m.type === 'gift');
const giftStats = {};
giftMessages.forEach(msg => {
const giftName = msg.gifts.gift_name;
if (!giftStats[giftName]) {
giftStats[giftName] = { count: 0, total_stars: 0 };
}
giftStats[giftName].count += msg.gifts.quantity;
giftStats[giftName].total_stars += msg.stars;
});
return Object.entries(giftStats)
.sort(([, a], [, b]) => b.total_stars - a.total_stars)
.map(([name, stats]) => ({ name, ...stats }));
}
import requests
import time
from datetime import datetime
from collections import Counter, defaultdict
API_KEY = 'YOUR_API_KEY'
BASE_URL = 'https://v2.jkt48connect.my.id'
def get_showroom_chat_stream(room_id, **options):
"""Fetch showroom chat stream data"""
params = {'room_id': room_id, 'apikey': API_KEY, **options}
url = f"{BASE_URL}/api/jkt48/chat-stream-sr"
try:
response = requests.get(url, params=params)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"Error fetching showroom chat stream: {e}")
raise
def get_recent_showroom_messages(room_id, limit=20):
"""Get recent showroom chat messages"""
return get_showroom_chat_stream(room_id, limit=limit)
def format_showroom_chat_message(message):
"""Format showroom chat message for display"""
time_str = datetime.fromisoformat(message['timestamp'].replace('Z', '+00:00')).strftime('%H:%M:%S')
level = f"Lv.{message['user']['level']}"
premium = '💎' if message['user']['is_premium'] else ''
return {
'id': message['id'],
'time': time_str,
'user': f"{premium}{level} {message['user']['display_name']}",
'text': message['message'],
'stars': message['stars'],
'is_gift': message['type'] == 'gift',
'gifts': message.get('gifts')
}
def display_showroom_live_chat(room_id):
"""Display showroom live chat messages"""
try:
data = get_showroom_chat_stream(room_id)
print(f"=== SHOWROOM LIVE CHAT: {data['stream_info']['title']} ===")
print(f"Member: {data['stream_info']['member']['name']} ({data['stream_info']['member']['team']})")
print(f"Viewers: {data['stream_info']['viewers']} | Duration: {data['stream_info']['duration']}")
print(f"Messages/min: {data['stats']['messages_per_minute']} | Total Stars: {data['stats']['total_stars']}")
print(f"Top gifts: {' '.join(data['stats']['top_gifts'])}")
print("\n--- RECENT MESSAGES ---")
for msg in data['chat_messages']:
formatted = format_showroom_chat_message(msg)
if formatted['is_gift']:
gifts = formatted['gifts']
print(f"🎁 [{formatted['time']}] {formatted['user']} sent {gifts['quantity']}x {gifts['gift_name']} ({formatted['stars']} stars)")
else:
print(f"💬 [{formatted['time']}] {formatted['user']}: {formatted['text']}")
except Exception as e:
print(f"Error displaying showroom chat: {e}")
def monitor_showroom_chat(room_id, duration=60):
"""Monitor showroom chat for specified duration"""
start_time = time.time()
last_timestamp = datetime.now().isoformat()
print(f"Monitoring showroom chat for {duration} seconds...")
while time.time() - start_time < duration:
try:
data = get_showroom_chat_stream(room_id, since=last_timestamp, limit=5)
if data['chat_messages']:
for msg in data['chat_messages']:
formatted = format_showroom_chat_message(msg)
if formatted['is_gift']:
gifts = formatted['gifts']
print(f"🔴🎁 [{formatted['time']}] {formatted['user']} sent {gifts['quantity']}x {gifts['gift_name']}")
else:
print(f"🔴💬 [{formatted['time']}] {formatted['user']}: {formatted['text']}")
last_timestamp = data['chat_messages'][0]['timestamp']
time.sleep(3) # Wait 3 seconds
except Exception as e:
print(f"Monitoring error: {e}")
time.sleep(5)
def analyze_showroom_chat_data(chat_data):
"""Analyze showroom chat statistics"""
messages = chat_data['chat_messages']
gift_messages = [m for m in messages if m['type'] == 'gift']
chat_messages = [m for m in messages if m['type'] == 'chat']
user_ids = [msg['user']['user_id'] for msg in messages]
user_levels = [msg['user']['level'] for msg in messages]
return {
'total_messages': len(messages),
'chat_messages': len(chat_messages),
'gift_messages': len(gift_messages),
'unique_users': len(set(user_ids)),
'total_stars': sum(msg['stars'] for msg in messages),
'average_user_level': sum(user_levels) / len(user_levels) if user_levels else 0,
'premium_users': len([m for m in messages if m['user']['is_premium']]),
'top_gifters': get_top_gifters(gift_messages)
}
def track_showroom_gifts(chat_data):
"""Track gift statistics"""
gift_messages = [m for m in chat_data['chat_messages'] if m['type'] == 'gift']
gift_stats = defaultdict(lambda: {'count': 0, 'total_stars': 0})
for msg in gift_messages:
gift_name = msg['gifts']['gift_name']
gift_stats[gift_name]['count'] += msg['gifts']['quantity']
gift_stats[gift_name]['total_stars'] += msg['stars']
return sorted(
[{'name': name, **stats} for name, stats in gift_stats.items()],
key=lambda x: x['total_stars'],
reverse=True
)
# Usage example
if __name__ == "__main__":
# Example: Display chat for a showroom live stream
display_showroom_live_chat("123456")
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
"time"
)
const (
APIKey = "YOUR_API_KEY"
BaseURL = "https://v2.jkt48connect.my.id"
)
type ShowroomChatStreamData struct {
StreamInfo ShowroomStreamInfo `json:"stream_info"`
ChatMessages []ShowroomChatMessage `json:"chat_messages"`
Stats ShowroomChatStats `json:"stats"`
}
type ShowroomStreamInfo struct {
RoomID string `json:"room_id"`
RoomName string `json:"room_name"`
Title string `json:"title"`
Status string `json:"status"`
Viewers int `json:"viewers"`
Duration string `json:"duration"`
Member struct {
Name string `json:"name"`
Nickname string `json:"nickname"`
Team string `json:"team"`
} `json:"member"`
}
type ShowroomChatMessage struct {
ID string `json:"id"`
User ShowroomChatUser `json:"user"`
Message string `json:"message"`
Timestamp string `json:"timestamp"`
Type string `json:"type"`
Gifts *ShowroomGift `json:"gifts"`
Stars int `json:"stars"`
}
type ShowroomChatUser struct {
UserID string `json:"user_id"`
Username string `json:"username"`
DisplayName string `json:"display_name"`
Avatar string `json:"avatar"`
Level int `json:"level"`
IsPremium bool `json:"is_premium"`
}
type ShowroomGift struct {
GiftID string `json:"gift_id"`
GiftName string `json:"gift_name"`
Quantity int `json:"quantity"`
TotalStars int `json:"total_stars"`
}
type ShowroomChatStats struct {
TotalMessages int `json:"total_messages"`
UniqueUsers int `json:"unique_users"`
MessagesPerMinute float64 `json:"messages_per_minute"`
TotalStars int `json:"total_stars"`
GiftCount int `json:"gift_count"`
TopGifts []string `json:"top_gifts"`
}
func getShowroomChatStream(roomID string, options map[string]string) (*ShowroomChatStreamData, error) {
params := url.Values{}
params.Add("room_id", roomID)
params.Add("apikey", APIKey)
for key, value := range options {
params.Add(key, value)
}
url := fmt.Sprintf("%s/api/jkt48/chat-stream-sr?%s", BaseURL, params.Encode())
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("API request failed with status: %d", resp.StatusCode)
}
var data ShowroomChatStreamData
err = json.NewDecoder(resp.Body).Decode(&data)
return &data, err
}
func formatShowroomChatMessage(message ShowroomChatMessage) map[string]interface{} {
timestamp, _ := time.Parse(time.RFC3339, message.Timestamp)
timeStr := timestamp.Format("15:04:05")
level := fmt.Sprintf("Lv.%d", message.User.Level)
premium := ""
if message.User.IsPremium {
premium = "💎"
}
return map[string]interface{}{
"id": message.ID,
"time": timeStr,
"user": fmt.Sprintf("%s%s %s", premium, level, message.User.DisplayName),
"text": message.Message,
"stars": message.Stars,
"is_gift": message.Type == "gift",
"gifts": message.Gifts,
}
}
func displayShowroomLiveChat(roomID string) {
data, err := getShowroomChatStream(roomID, nil)
if err != nil {
fmt.Printf("Error fetching showroom chat stream: %v\n", err)
return
}
fmt.Printf("=== SHOWROOM LIVE CHAT: %s ===\n", data.StreamInfo.Title)
fmt.Printf("Member: %s (%s)\n", data.StreamInfo.Member.Name, data.StreamInfo.Member.Team)
fmt.Printf("Viewers: %d | Duration: %s\n", data.StreamInfo.Viewers, data.StreamInfo.Duration)
fmt.Printf("Messages/min: %.1f | Total Stars: %d\n", data.Stats.MessagesPerMinute, data.Stats.TotalStars)
fmt.Printf("Top gifts: %v\n", data.Stats.TopGifts)
fmt.Println("\n--- RECENT MESSAGES ---")
for _, msg := range data.ChatMessages {
formatted := formatShowroomChatMessage(msg)
if formatted["is_gift"].(bool) {
gifts := msg.Gifts
fmt.Printf("🎁 [%s] %s sent %dx %s (%d stars)\n",
formatted["time"],
formatted["user"],
gifts.Quantity,
gifts.GiftName,
formatted["stars"])
} else {
fmt.Printf("💬 [%s] %s: %s\n",
formatted["time"],
formatted["user"],
formatted["text"])
}
}
}
func monitorShowroomChat(roomID string, duration time.Duration) {
fmt.Printf("Monitoring showroom chat for %v...\n", duration)
start := time.Now()
lastTimestamp := time.Now().Format(time.RFC3339)
for time.Since(start) < duration {
options := map[string]string{
"since": lastTimestamp,
"limit": "5",
}
data, err := getShowroomChatStream(roomID, options)
if err != nil {
fmt.Printf("Monitoring error: %v\n", err)
time.Sleep(5 * time.Second)
continue
}
if len(data.ChatMessages) > 0 {
for _, msg := range data.ChatMessages {
formatted := formatShowroomChatMessage(msg)
if formatted["is_gift"].(bool) {
gifts := msg.Gifts
fmt.Printf("🔴🎁 [%s] %s sent %dx %s\n",
formatted["time"],
formatted["user"],
gifts.Quantity,
gifts.GiftName)
} else {
fmt.Printf("🔴💬 [%s] %s: %s\n",
formatted["time"],
formatted["user"],
formatted["text"])
}
}
lastTimestamp = data.ChatMessages[0].Timestamp
}
time.Sleep(3 * time.Second)
}
}
func main() {
// Example: Display chat for a showroom live stream
displayShowroomLiveChat("123456")
}
Real-time Integration
// Simple polling for new showroom messages
async function startShowroomChatPolling(roomId) {
let lastCheck = new Date().toISOString();
setInterval(async () => {
try {
const data = await getShowroomChatStream(roomId, {
since: lastCheck,
limit: 10
});
if (data.chat_messages.length > 0) {
console.log(`📨 ${data.chat_messages.length} new messages`);
data.chat_messages.forEach(msg => {
if (msg.type === 'gift') {
console.log(`🎁 ${msg.user.display_name} sent ${msg.gifts.gift_name} (${msg.stars} stars)`);
} else {
console.log(`💬 ${msg.user.display_name}: ${msg.message}`);
}
});
lastCheck = data.chat_messages[0].timestamp;
}
} catch (error) {
console.error('Showroom polling failed:', error);
}
}, 3000);
}
// Advanced showroom chat monitoring
class ShowroomLiveChatMonitor {
constructor(roomId) {
this.roomId = roomId;
this.subscribers = [];
}
subscribe(callback) {
this.subscribers.push(callback);
}
async start() {
const data = await getShowroomChatStream(this.roomId);
// Notify subscribers of initial data
this.subscribers.forEach(callback => {
callback('init', data);
});
// Start polling for updates
this.poll();
}
async poll() {
// Implementation for continuous polling
}
}
// Showroom chat analytics and insights
function generateShowroomChatAnalytics(chatData) {
const messages = chatData.chat_messages;
const giftMessages = messages.filter(m => m.type === 'gift');
const chatMessages = messages.filter(m => m.type === 'chat');
return {
engagement: {
messagesPerMinute: chatData.stats.messages_per_minute,
giftRatio: giftMessages.length / messages.length,
averageStarsPerGift: giftMessages.reduce((sum, m) => sum + m.stars, 0) / giftMessages.length
},
users: {
total: chatData.stats.unique_users,
premiumUsers: messages.filter(m => m.user.is_premium).length,
averageLevel: messages.reduce((sum, m) => sum + m.user.level, 0) / messages.length
},
gifts: {
totalStars: chatData.stats.total_stars,
giftCount: chatData.stats.gift_count,
topGifts: chatData.stats.top_gifts,
topGivers: getTopShowroomGivers(giftMessages)
}
};
}
Error Handling
async function safeShowroomChatStream(roomId, options = {}) {
try {
const data = await getShowroomChatStream(roomId, options);
// Validate response
if (!data.chat_messages || !Array.isArray(data.chat_messages)) {
throw new Error('Invalid showroom chat data structure');
}
return data;
} catch (error) {
console.error(`Showroom chat stream error for room ${roomId}:`, error);
return {
stream_info: { room_id: roomId, status: 'error' },
chat_messages: [],
stats: { total_messages: 0, unique_users: 0, total_stars: 0 },
error: error.message
};
}
}
Integration Example
Combine showroom live data and chat stream for complete experience:
// Get showroom live streams and their chat
async function getShowroomStreamsWithChat() {
const liveData = await getShowroomLive();
const streamsWithChat = await Promise.all(
liveData.live_streams.map(async (stream) => {
const chatData = await getShowroomChatStream(stream.room_id, { limit: 10 });
return {
...stream,
recentChat: chatData.chat_messages,
chatStats: chatData.stats
};
})
);
return streamsWithChat;
}
// Monitor all active showroom streams
async function monitorAllShowroomChats() {
const liveData = await getShowroomLive();
liveData.live_streams.forEach(stream => {
console.log(`Starting monitor for ${stream.member.name} (Room: ${stream.room_id})`);
startShowroomChatPolling(stream.room_id);
});
}
Get your API key from JKT48Connect and start building live showroom chat applications!
How is this guide?
Last updated on