twitch_logo

Tracking Viewer Ratings of Twitch Stream Minute-by-Minute

This blog post shows how to use the Twitch API to retrieve the current number of viewers of a channel. See this page for an application example.

Get your API key

Although you can use the API without key, using the client_id is recommended to make sure you are to rate limited by Twitch. You can get the client_id by going to https://secure.twitch.tv/settings/connections and registering a new application:

twitch1

The client ID will automatically be generated for you:

twitch11

Calling the API response

You call the API with requests to

https://api.twitch.tv/kraken/streams/<CHANNEL>?client_id=<YOUR_CLIENT_ID>

For example, to get the information for channel manvsgame and client_id abcdef1234567890 you would request:

https://api.twitch.tv/kraken/streams/manvsgame?client_id=abcdef1234567890

If the channel is offline, the response will look like that:

{u'_links': {u'channel': u'https://api.twitch.tv/kraken/channels/manvsgame',
             u'self': u'https://api.twitch.tv/kraken/streams/manvsgame'},
 u'stream': None}

If the channel is online, the response will look like that:

{u'_links': {u'channel': u'https://api.twitch.tv/kraken/channels/manvsgame',
             u'self': u'https://api.twitch.tv/kraken/streams/manvsgame'},
 u'stream': {u'_id': 7797659104L,
             u'_links': {u'self': u'https://api.twitch.tv/kraken/streams/manvsgame'},
             u'channel': {u'_id': 8330235,
                          u'_links': {u'chat': u'https://api.twitch.tv/kraken/chat/manvsgame',
                                      u'commercial': u'https://api.twitch.tv/kraken/channels/manvsgame/commercial',
                                      u'editors': u'https://api.twitch.tv/kraken/channels/manvsgame/editors',
                                      u'features': u'https://api.twitch.tv/kraken/channels/manvsgame/features',
                                      u'follows': u'https://api.twitch.tv/kraken/channels/manvsgame/follows',
                                      u'self': u'https://api.twitch.tv/kraken/channels/manvsgame',
                                      u'stream_key': u'https://api.twitch.tv/kraken/channels/manvsgame/stream_key',
                                      u'subscriptions': u'https://api.twitch.tv/kraken/channels/manvsgame/subscriptions',
                                      u'teams': u'https://api.twitch.tv/kraken/channels/manvsgame/teams',
                                      u'videos': u'https://api.twitch.tv/kraken/channels/manvsgame/videos'},
                          u'abuse_reported': None,
                          u'background': u'http://static-cdn.jtvnw.net/jtv_user_pictures/manvsgame-background_image-10842e1c129df87d.png',
                          u'banner': None,
                          u'created_at': u'2009-09-17T19:42:52Z',
                          u'delay': 0,
                          u'display_name': u'MANvsGAME',
                          u'game': u'I Wanna Be The Guy',
                          u'logo': u'http://static-cdn.jtvnw.net/jtv_user_pictures/manvsgame-profile_image-731e0a1382912c9c-300x300.png',
                          u'mature': True,
                          u'name': u'manvsgame',
                          u'profile_banner': u'http://static-cdn.jtvnw.net/jtv_user_pictures/manvsgame-profile_banner-926ae0b81e5737df-480.png',
                          u'profile_banner_background_color': None,
                          u'status': u'MAN vs I WANNA BE THE GUY - I Wanna Be The Month!',
                          u'updated_at': u'2013-12-10T08:06:53Z',
                          u'url': u'http://www.twitch.tv/manvsgame',
                          u'video_banner': u'http://static-cdn.jtvnw.net/jtv_user_pictures/manvsgame-channel_offline_image-c01cba2e538ae8f5-640x360.jpeg',
                          u'views': 26903371},
             u'game': u'I Wanna Be The Guy',
             u'preview': {u'large': u'http://static-cdn.jtvnw.net/previews-ttv/live_user_manvsgame-640x400.jpg',
                          u'medium': u'http://static-cdn.jtvnw.net/previews-ttv/live_user_manvsgame-320x200.jpg',
                          u'small': u'http://static-cdn.jtvnw.net/previews-ttv/live_user_manvsgame-80x50.jpg',
                          u'template': u'http://static-cdn.jtvnw.net/previews-ttv/live_user_manvsgame-{width}x{height}.jpg'},
             u'viewers': 9233}}

The number of viewers is in ‘stream’ -> ‘viewers’ (e.g. 9233).

Example code

The following Python continuously calls the API and stores the number of viewers to a log file (make sure to add your client_id).

import requests
import json
from datetime import datetime, timedelta
import time
import logging
import argparse

BASE_URL = 'https://api.twitch.tv/kraken/'
CLIENTID = '<add your API key here>'

def start_logging(logfile='log.txt'):
    log = logging.getLogger()
    log.setLevel(logging.DEBUG)

    ch  = logging.StreamHandler()
    fh = logging.FileHandler(logfile)
    log.addHandler(ch)
    log.addHandler(fh)        

    requests_log = logging.getLogger("requests")
    requests_log.setLevel(logging.WARNING)

def get_viewers(stream_name):
    url = '{0}streams/{1}?client_id={2}'.format(BASE_URL, stream_name, CLIENTID)
    r = requests.get(url)
    if r.status_code != 200:
        raise Exception("API returned {0}".format(r.status_code))
    infos = r.json()
    stream = infos['stream']
    results = {}
    if not stream:
        results = {'online':False,'title':None,'viewers':0}
    else:
        viewers = stream['viewers']
        title = stream['channel']['status']
        results = {'online':True,'title':title,'viewers':viewers}

    results['time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    results['stream'] = stream_name
    return results

def constant_polling(stream, onint=1,offint=5):
    logging.info("starting polling, inteval is " + 
        "{0}min (online) and {1}min (offline)".format(onint, offint))
    while True:
        online = False
        try:
            res = get_viewers(stream)            
            online = res['online']
            txt = json.dumps(res) 
            logging.info("Streamviewercount: {0}".format(txt))
        except Exception as e:
            logging.warn("Polling failed: {0}".format(e))            

        sleep_interval = onint if online else offint
        time.sleep(60*sleep_interval)

if __name__=="__main__":
    parser = argparse.ArgumentParser(description='log viewer rating of twitch' +
        ' stream')
    parser.add_argument('channel', help='channel name')
    parser.add_argument('--int_on', help='interval if online (min)', default=1)
    parser.add_argument('--int_off', help='interval if offline (min)', default=5)
    args = parser.parse_args()
    start_logging()
    constant_polling(args.channel, args.int_on, args.int_off)

[download GIST][5]. Sample log file output:

starting polling, inteval is 1min (online) and 5min (offline)
Streamviewercount: {"stream": "dendi", "title": "time to practice !!!  !!!!!! Dendimon", "time": "2013-12-10 17:38:10", "viewers": 24978, "online": true}
Streamviewercount: {"stream": "dendi", "title": "time to practice !!!  !!!!!! Dendimon", "time": "2013-12-10 17:39:12", "viewers": 23199, "online": true}
Streamviewercount: {"stream": "dendi", "title": "time to practice !!!  !!!!!! Dendimon", "time": "2013-12-10 17:40:15", "viewers": 21841, "online": true}
Streamviewercount: {"stream": "dendi", "title": "time to practice !!!  !!!!!! Dendimon", "time": "2013-12-10 17:41:16", "viewers": 21247, "online": true}
Streamviewercount: {"stream": "dendi", "title": null, "time": "2013-12-10 17:42:19", "viewers": 0, "online": false}

[5]: nums:false https://gist.github.com/baderj/7893756

Archived Comments

Note: I removed the Disqus integration in an effort to cut down on bloat. The following comments were retrieved with the export functionality of Disqus. If you have comments, please reach out to me by Twitter or email.

PE Feb 15, 2016 02:37:05 UTC

Hello, thanks for sharing this work. I am learning and have been trying to implement your example with no changes and consistently get the error: "Polling failed: 'stream'.

When I enter the base URL with a sample game (e.g., "DayZ") in my browser I do get the results. It looks like the preferred URL structure in the code would be something like: url = '{0}streams?game={1}&?client_id={2}'.format(BASE_URL, CLIENTID)'

What do you think might be happening? Thank you.

PE Feb 15, 2016 02:38:43 UTC

Correction to the url above (mistyped): url = '{0}streams?game={1}&?client_id={2}'.format(BASE_URL, stream_name, CLIENTID)'

Johannes Bader Feb 15, 2016 09:15:27 UTC

My example polls one channel for the viewers. You like to get the viewers for all streams of a game? I don't think there is an API for that. You need to get a list of channels first with /games/top, then go through the list an query the viewers for each channel.

Maybe there is a query that does what you are after, check https://github.com/justintv...

Matthew Ericson Feb 16, 2016 20:24:13 UTC

Thanks again, I'll look into that.