Get_followers api scope problem

My python app uses twitchio package.

at some point I do:
followers = await self._http.get_channel_followers(token = ACCESS_TOKEN,
broadcaster_id=broadcaster_id,
user_id=int(user_id))

I get an error:
Error checking follower status: 401: You’re not authorized to use this route.: missing scope moderator:read:followers

But when I do:
curl -H “Authorization: Bearer ${TOKEN}” https://id.twitch.tv/oauth2/validate

And I see that the token has moderator:read:followers.

I’m the creator if that channel.
And the token in question is generated using:
https://twitchtokengenerator.com/

Other APIs are working ok…

Any advice, please

First off, you shouldn’t use 3rd party token generators. The Twitch CLI can generate tokens if needed and doesn’t have the same risks associated with tokens generated by a 3rd party site.

Secondly,

user_id=int(user_id))

User ID’s are strings, not integers.

Also, check the docs for the endpoint https://dev.twitch.tv/docs/api/reference/#get-channel-followers as it has a table for what can cause 401 errors, so make sure you’re not doing any of those things.

Thanks a LOT for your help.

I’m passing user_id as int because TwitchHTTP class has method:
async def get_channel_followers(

self,
token: str,
broadcaster_id: str,
user_id: Optional[int] = None,
) The doc says that token used for this API should be User Access Token, but my token is Application token. Do you think this could be the problem?

If so as far as i understand User Access Token should be approved by the concerned user, but my app which simply verifies that message sender is a follower of the channel - i don’t know in advance which user will post a message, so i can’t have user access tokens per user. Any advice? Thanks again Vadim

You can’t use an App token to see if one users follows a channel. Follow count is public, but data on which specific users follow a channel is private and so requires a User Token with the moderator:read:followers scope from either the broadcaster, or a user who’s a moderator on that channel.

Without a user going through the OAuth flow and granting you a token with that permission, you can’t get if a user follows the channel.

Oh… This is much clearer now.
Thank you.
As it happens I’m myself a chanell creator and (probably) moderator.
So how do I obtain a User Access token for my bot in this case?
Thank
Vadim

The same way as you would for yourself

Do a user auth flow. But logged into twitch as the bot account.

I tend to open incognito mode to help ensure I use the right twitch account or use force verify flag

Barry,
I don’t see how i can login into twitch as under my bot account.
I’m using attached code to create user access token.
It works, but twhen I try to get_channel_followers I’m getting:
Error checking follower status: 401: You’re not authorized to use this route.: missing scope moderator:read:followers
And as you can see in my code I do request this scope for the token.
I’m at loss…

======

import os
from re import T
import sys
from numpy import dot
import requests
import threading
import webbrowser
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs
from dotenv import load_dotenv
import os


load_dotenv(".env", override=True)

# Replace these with your Twitch Client ID and Client Secret
CLIENT_ID = os.getenv("BOT_CLIENT_ID")
CLIENT_SECRET = os.getenv('BOT_CLIENT_SECRET')


# The port number should match the one specified in the redirect URI
REDIRECT_URI = 'http://localhost:8080'
AUTHORIZATION_BASE_URL = 'https://id.twitch.tv/oauth2/authorize'
TOKEN_URL = 'https://id.twitch.tv/oauth2/token'

# Scopes required for the bot
SCOPES = [
    'chat:read',
    'chat:edit',
    'whispers:read',
    'whispers:edit',
    'channel:read:subscriptions',
    'moderator:read:followers',
]

# Global variable to store the authorization code
authorization_code = ''

class OAuthHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        global authorization_code
        # Parse the URL to get the code parameter
        parsed_url = urlparse(self.path)
        query_params = parse_qs(parsed_url.query)
        if 'code' in query_params:
            authorization_code = query_params['code'][0]
            self.send_response(200)
            self.send_header('Content-type', 'text/html')
            self.end_headers()
            self.wfile.write(b'<h1>Authorization Successful</h1><p>You can close this window.</p>')
        else:
            self.send_response(400)
            self.end_headers()
            self.wfile.write(b'<h1>Authorization Failed</h1><p>No authorization code found.</p>')

def start_server(server_class=HTTPServer, handler_class=OAuthHandler):
    server_address = ('', 8080)  # Listen on port 8080
    httpd = server_class(server_address, handler_class)
    httpd.handle_request()  # Handle a single request
    httpd.server_close()

def main():
    global authorization_code

    # Step 1: Construct the authorization URL
    auth_url = (
        f"{AUTHORIZATION_BASE_URL}"
        f"?client_id={CLIENT_ID}"
        f"&redirect_uri={REDIRECT_URI}"
        f"&response_type=code"
        f"&scope={' '.join(SCOPES)}"
    )

    # Step 2: Start the local server in a separate thread
    server_thread = threading.Thread(target=start_server)
    server_thread.start()

    # Step 3: Open the authorization URL in the default web browser
    print('Opening the authorization URL in your browser...')
    webbrowser.open(auth_url)

    # Wait for the server thread to finish (i.e., wait for the authorization code)
    server_thread.join()

    if not authorization_code:
        print('Failed to obtain authorization code.')
        sys.exit(1)

    # Step 4: Exchange the authorization code for an access token
    print('Exchanging authorization code for access token...')
    token_params = {
        'client_id': CLIENT_ID,
        'client_secret': CLIENT_SECRET,
        'code': authorization_code,
        'grant_type': 'authorization_code',
        'redirect_uri': REDIRECT_URI
    }

    response = requests.post(TOKEN_URL, data=token_params)
    if response.status_code != 200:
        print('Failed to obtain access token.')
        print(f'Status Code: {response.status_code}')
        print(f'Response: {response.text}')
        sys.exit(1)

    token_data = response.json()
    access_token = token_data['access_token']
    refresh_token = token_data.get('refresh_token', '')

    # Step 5: Print the access token and refresh token
    print('Access Token:', access_token)
    print('Refresh Token:', refresh_token)
    print('Token Type:', token_data['token_type'])
    print('Expires In:', token_data['expires_in'])
    print('Scopes:', ', '.join(token_data['scope']))

    # Save tokens to a file (optional)
    with open('tokens.txt', 'w') as token_file:
        token_file.write(f"Access Token: {access_token}\n")
        token_file.write(f"Refresh Token: {refresh_token}\n")
        token_file.write(f"Expires In: {token_data['expires_in']}\n")
        token_file.write(f"Scopes: {', '.join(token_data['scope'])}\n")

    print('Tokens saved to tokens.txt')

if __name__ == '__main__':
    main()

The same way as you logged into twitch as you.

In your web browser
Hit logut.
Hit login
Fill in the login form as your bot account

Or just open your tool in incognito as you start logged out of twitch

I have only one twitch account, and I registered my bot under the same account.
You say I need to create another twitch account for the bot specifically?

Yes, all bots on twitch are just real twitch accounts that have authenticated against a clientID.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.