# API Get Chatters - Impossible?

**URL:** <https://discuss.dev.twitch.com/t/api-get-chatters-impossible/47071>\
**Category:** API\
**Created:** [August 29, 2023, 7:20am UTC](https://discuss.dev.twitch.com/t/api-get-chatters-impossible/47071 "2023-08-29T07:20:49Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![Mindconstructor](https://d3ovvz0v2ajtpu.cloudfront.net/user_avatar/discuss.dev.twitch.com/mindconstructor/32/25938_2.png) [@Mindconstructor](https://discuss.dev.twitch.com/u/Mindconstructor)\
**Post date:** [August 29, 2023, 7:20am UTC](https://discuss.dev.twitch.com/t/api-get-chatters-impossible/47071/1 "2023-08-29T07:20:49Z")

</div>

Okay. I have a big issue.

I was able to get the user list of a chatter trough the helix api. But now, its not longer possible in any way.

Regardless, what i try (new token, new client secret, oauth-login … nothing works anymore. I get 404 or 401 again and again.

And it feels, that the documentation is way outdated …

I work with nodeJS. So my current code looks like this:

```js
  async getChatters(chId) {
    const headers = {
      'Client-ID': this.api.clientId,
      'Authorization': `Bearer ${this.api.moderatorToken}`,
    };
    const userListResponse = await fetch(`https://api.twitch.tv/helix/chat/chatters?` +
      `broadcaster_id=${chId}&` +
      `moderator_id=${this.auth.id}&` +
      `first=1000`,
      { headers }
    );
    const userListResponseData = await userListResponse.json();
    console.log(userListResponseData);
    var nList = [];
    for(var i = 0;i < userListResponseData.data.length;i++) {
      nList.push(userListResponseData.data[i].user_login);
    }
    return nList;
  }

```

The placeholders:  
chld = the lower-case name of the target channel  
this.api.clientId = client\_id from dev-console  
this.api.moderatorToken = access-token with the scope “moderator:read:chatters”  
this.auth.id = the user-id of the user, where is authenticated with client id and for wich the token is created

So … what goes wrong?

---

<div class="post-metadata">

**Author:** ![BarryCarlyon](https://d3ovvz0v2ajtpu.cloudfront.net/user_avatar/discuss.dev.twitch.com/barrycarlyon/32/253_2.png) [@BarryCarlyon](https://discuss.dev.twitch.com/u/BarryCarlyon)\
**Post date:** [August 29, 2023, 11:46am UTC](https://discuss.dev.twitch.com/t/api-get-chatters-impossible/47071/2 "2023-08-29T11:46:35Z")

</div>

> [@Mindconstructor](#):
>
> I get 404 or 401 again and again.

Whats the body of the response?  
HTTP code is half the information

> [@Mindconstructor](#):
>
> And it feels, that the documentation is way outdated …

The documentation is correct

> [@Mindconstructor](#):
>
> So … what goes wrong?

Your code looks correct as it approx matches mine

I just construct the URL to call differently.

Mine looks like

```js
async function runPage(jobResult, cursor) {
    let params = [
        ['broadcaster_id', jobResult.channel_id],
        ['moderator_id', process.env.USER_ID],
        ['first', 1000]
    ];
    if (cursor) {
        params.push(['after', cursor]);
    }

    let url = new URL('https://api.twitch.tv/helix/chat/chatters');
    url.search = new URLSearchParams(params);

    let chattersReq = await fetch(
        url,
        {
            method: 'GET',
            headers: {
                'Client-ID': process.env.TWITCH_CLIENT_ID,
                'Authorization': `Bearer ${process.env.access_token}`,
                'Accept': 'application/json',
                'Accept-Encoding': 'gzip,deflate'
            }
        }
    );
    if (chattersReq.status != 200) {
        console.log('Failed to get chatters', await chattersReq.text());
        return;
    }
    let chattersData = await chattersReq.json();
    //console.log(chattersData);
    let { data, pagination, total } = chattersData;

```

This is a variation on my example at

> <https://github.com/BarryCarlyon/twitch_misc/tree/main/chat/chatters>
>
> //github.com/BarryCarlyon/twitch\_misc/tree/main/chat/chatters

(Which was created before userID’s were in Get Chatters so demonstrates how to take 1k into 10 pages and call get users with but I digress)

> [@Mindconstructor](#):
>
> I get 404 or 401 again and again.

Since the Twitch API is behind a CDN, it is not unheard of for that CDN to have some regional issues and throw back weird/unexpected error codes, or your Provider is having an issue with connectivity to AWS.

So we need to see what the body message of the error response to see what that is saying.

---

<div class="post-metadata">

**Author:** ![Mindconstructor](https://d3ovvz0v2ajtpu.cloudfront.net/user_avatar/discuss.dev.twitch.com/mindconstructor/32/25938_2.png) [@Mindconstructor](https://discuss.dev.twitch.com/u/Mindconstructor)\
**Post date:** [August 29, 2023, 1:42pm UTC](https://discuss.dev.twitch.com/t/api-get-chatters-impossible/47071/3 "2023-08-29T13:42:54Z")

</div>

Ich hab den ganzen Ablauf mal in Python gebastelt. Der funtkioniert auch soweit. Bis es darum geht, die Userliste zu bekommen.

```auto
import requests
import json

# Auth-Secret-Stuff. (In diesem Forum zensiert, aber trotzdem erkennbar)
secrets = {
    "client_id": "g12 ************************ so0",
    "client_secret": "cpr ************************ 8up",
    "moderator_oauth": "oauth:hco ************************ 3h2",
    "moderator_id": 698000000, # Letzten 6 ziffern genullt ;D
}

# moderator_id ist der user, dem auch client_id und client_secret in der dev-console gehört.
# oauth-token ist gültig und korrekt

def convert_get_data_to_string(get_data):
    params = []
    for key, value in get_data.items():
        if isinstance(value, list):
            for item in value:
                params.append(f"{key}[]={item}")
        else:
            params.append(f"{key}={value}")
    return '?' + '&'.join(params)

def get_user_data(target_broadcaster):
    # Get Data for getting a access token
    get_data_1 = {
        "client_id": secrets["client_id"],
        "client_secret": secrets["client_secret"],
        "grant_type": "client_credentials",
        "scope": ["moderator:read:chatters",]
    }

    # Post-Data for getting a access token
    post_data_1 = {
        "oauth": "bearer " + secrets["moderator_oauth"]
    }

    # Target Url build up with get data for oauth2/token
    target_url = "https://id.twitch.tv/oauth2/token" + convert_get_data_to_string(get_data_1)

    # Call the api
    auth_credentials = requests.post(target_url, data=post_data_1)
    auth_credentials = auth_credentials.json()

    # For Debug purposes
    print(auth_credentials["access_token"])

    # Header for User-List-Request
    headers = {
        "Client-Id": get_data_1["client_id"],
        "Authorization": "Bearer " + auth_credentials["access_token"]
    }

    # Get Data for User-List-Request
    get_data_2 = {
        "broadcaster_id": target_broadcaster,
        "moderator_id": secrets["moderator_id"],
        "first": "1000"
    }

    # Target Url build upo with get data for chat/chatters
    target_url = "https://api.twitch.tv/helix/chat/chatters" + convert_get_data_to_string(get_data_2)

    # Call the API
    response = requests.get(target_url, headers=headers)

    # Return the result
    return response.json()

# Call get_user_data for specific channel (it works with capital letters)
response_data = get_user_data("Mindconstructor")

# Output the result
print("Server-Response:", response_data)

```

Führe ich das (mit korrekten secret-daten natürlich) aus, erhalte ich

```auto
Server-Response: {'error': 'Unauthorized', 'status': 401, 'message': 'Missing User OAUTH Token'}

```

WO soll da der oauth-token hin? Ich habs im GET und im Header probiert. Versuch ich mit POST den request für die User-Liste zu machen, erhalte ich

```auto
Server-Response: {'error': 'Not Found', 'status': 404, 'message': ''}

```

Bitte, wo liegt mein Fehler? (Ja. Ich schreib das grad in Python, weils so einfacher war, mal fix Requests zu testen. Mein NodeJS-Script werde ich dann auf Basis dieser Infos anpassen. Und ja, ich weiß, dass ich auch oauth2/validate nutzen müsste und so … soweit ich das in den docs verstanden hab)

Es muss doch möglich sein, diese user-liste zu bekommen. die GANZE ZEIT hats funktioniert. Ohne zu zucken … Und plötzlich, von heute auf morgen, gehts nicht mehr … \<\_\<

---

<div class="post-metadata">

**Author:** ![BarryCarlyon](https://d3ovvz0v2ajtpu.cloudfront.net/user_avatar/discuss.dev.twitch.com/barrycarlyon/32/253_2.png) [@BarryCarlyon](https://discuss.dev.twitch.com/u/BarryCarlyon)\
**Post date:** [August 29, 2023, 1:44pm UTC](https://discuss.dev.twitch.com/t/api-get-chatters-impossible/47071/4 "2023-08-29T13:44:40Z")

</div>

> [@Mindconstructor](#):
>
> Führe ich das (mit korrekten secret-daten natürlich) aus, erhalte ich

You used the wrong token type.

> [@Mindconstructor](#):
>
> ` "grant_type": "client_credentials",`

Client Credentials token is not valid for this endpoint

> [@Mindconstructor](#):
>
> ` "moderator_oauth": "oauth:hco ************************ 3h2",`

The token should not be preceeded with `oauth:`

Tokens are only preceeded with `oauth:` to login to chat with.

For API usage it’s just

> Authorization: Bearer tokenhere

Not

> Authorization: Bearer oauth:tokenhere

> [@Mindconstructor](#):
>
> WO soll da der oauth-token hin? Ich habs im GET und im Header probiert. Versuch ich mit POST den request für die User-Liste zu machen, erhalte ich
> 
> ```auto
> Server-Response: {'error': 'Not Found', 'status': 404, 'message': ''}
> 
> ```

You cannot POST to this endpoint hence a 404.

You can only GET chatters you cannot POST chatters. (Post implies create and you can’t create chatters)

> [@Mindconstructor](#):
>
> Bitte, wo liegt mein Fehler? (Ja. Ich schreib das grad in Python, weils so einfacher war, mal fix Requests zu testen. Mein NodeJS-Script werde ich dann auf Basis dieser Infos anpassen. Und ja, ich weiß, dass ich auch oauth2/validate nutzen müsste und so … soweit ich das in den docs verstanden hab)

You have a User token (with a `oauth:` prefix)  
You overwrite that token with a `client_credentials` token, which is not valid for Getting cahtters with

---

<div class="post-metadata">

**Author:** ![Mindconstructor](https://d3ovvz0v2ajtpu.cloudfront.net/user_avatar/discuss.dev.twitch.com/mindconstructor/32/25938_2.png) [@Mindconstructor](https://discuss.dev.twitch.com/u/Mindconstructor)\
**Post date:** [August 29, 2023, 1:48pm UTC](https://discuss.dev.twitch.com/t/api-get-chatters-impossible/47071/5 "2023-08-29T13:48:38Z")

</div>

So … its not longer possible to get the user list from client side only? i HAVE MANUALLY get a token?

crap \<\_\<

---

<div class="post-metadata">

**Author:** ![BarryCarlyon](https://d3ovvz0v2ajtpu.cloudfront.net/user_avatar/discuss.dev.twitch.com/barrycarlyon/32/253_2.png) [@BarryCarlyon](https://discuss.dev.twitch.com/u/BarryCarlyon)\
**Post date:** [August 29, 2023, 1:51pm UTC](https://discuss.dev.twitch.com/t/api-get-chatters-impossible/47071/6 "2023-08-29T13:51:39Z")

</div>

> [@Mindconstructor](#):
>
> its not longer possible to get the user list from client side only

You mean server side? Sure it is you just need to get a refreshable user token into your script.

> [@Mindconstructor](#):
>
> i HAVE MANUALLY get a token?

manually create a user token/refresh token set.

Use the user token till it expires  
When the user token expires use the refresh token to get a new user token

So Auth code flow: [Getting OAuth Access Tokens | Twitch Developers](https://dev.twitch.tv/docs/authentication/getting-tokens-oauth/#authorization-code-grant-flow)  
And refresh flow: [Refreshing Access Tokens | Twitch Developers](https://dev.twitch.tv/docs/authentication/refresh-tokens/)

I use a variation of [https://github.com/BarryCarlyon/twitch\_misc/tree/main/authentication/user\_access\_without\_server](https://github.com/BarryCarlyon/twitch_misc/tree/main/authentication/user_access_without_server) to get a token/refresh token into my server.

And then the server runs with the access/refresh token and will get a new user token when it expires

So in summary

> [@Mindconstructor](#):
>
> i HAVE MANUALLY get a token?

Once only once.

Unless the refresh token dies.

---

<div class="post-metadata">

**Author:** ![Mindconstructor](https://d3ovvz0v2ajtpu.cloudfront.net/user_avatar/discuss.dev.twitch.com/mindconstructor/32/25938_2.png) [@Mindconstructor](https://discuss.dev.twitch.com/u/Mindconstructor)\
**Post date:** [August 29, 2023, 2:34pm UTC](https://discuss.dev.twitch.com/t/api-get-chatters-impossible/47071/7 "2023-08-29T14:34:36Z")

</div>

Okay … i have this stuff … including the refresh token … but now … i get the error:

> Server-Response: {‘error’: ‘Forbidden’, ‘status’: 403, ‘message’: ‘user 698394232 does not have moderator permissions for channel mindconstructor’}

So my token seems to work … i made everything with the bot account wich i want to use for the user-list …

But … wait a second. Oo

```auto
{
  'data': [{
    'id': '698394232',
    'login': 'intelli_bot',
    'display_name': 'Intelli_Bot',
    'type': '',
    'broadcaster_type': '',
    'description': '(cropped)',
    'profile_image_url': '(cropped)',
    'offline_image_url': '',
    'view_count': 0,
    'created_at': '2021-06-16T12:58:30Z'
  }]
}

```

 ![](https://d2opxh93rbxzdn.cloudfront.net/original/3X/9/1/91e564a64bf3eb008ef6e6e344581ea320a6fd33.png)  
You can see, this bot writes as moderator in this chat already. it HAS moderator status

it HAS mod permisisons … (happens with capital letters too btw.)

\<\_\<

Here’s my test-flow atm (refreshing and validating is the next step. but first i need the damn user list and i don’t know, why this not works … \<\_\<)

```auto
import requests
import json

# Auth-Secret-Stuff.
secrets = {
    "client_id": "g12 ************************ so0",
    "client_secret": "cpr ************************ 8up",
    "client_access_token": "nza ************************ s7i",
    "client_refresh_token": "ego ******************************************** k70",
    "moderator_oauth": "oauth:0q6 ************************ ivo",
    "moderator_id": 698394232,
}

def convert_get_data_to_string(get_data):
    params = []
    for key, value in get_data.items():
        if isinstance(value, list):
            for item in value:
                params.append(f"{key}[]={item}")
        else:
            params.append(f"{key}={value}")
    return '?' + '&'.join(params)

def get_user_data(target_broadcaster):
    # Get Data for User-List-Request
    get_data = {
        "broadcaster_id": target_broadcaster, # mindconstructor ... target channel
        "moderator_id": secrets["moderator_id"], # intelli_bot, wich is definitively moderator on the channel mindconstructor
        "first": "1000"
    }

    # Header for User-List-Request
    headers = {
        "Client-Id": secrets["client_id"],
        "Authorization": "Bearer " + secrets["client_access_token"]
    }

    # Target Url build upo with get data for chat/chatters
    target_url = "https://api.twitch.tv/helix/chat/chatters" + convert_get_data_to_string(get_data)

    # Call the API
    response = requests.get(target_url, headers=headers)

    # Return the result
    return response.json()

# Call get_user_data for specific channel (it works with capital letters)
response_data = get_user_data("mindconstructor")

# Output the result
print("Server-Response:", response_data)

```

Maybe i have messed around with the scopes?  
i requested only the “moderator:read:chatters” scope …

---

<div class="post-metadata">

**Author:** ![BarryCarlyon](https://d3ovvz0v2ajtpu.cloudfront.net/user_avatar/discuss.dev.twitch.com/barrycarlyon/32/253_2.png) [@BarryCarlyon](https://discuss.dev.twitch.com/u/BarryCarlyon)\
**Post date:** [August 29, 2023, 2:39pm UTC](https://discuss.dev.twitch.com/t/api-get-chatters-impossible/47071/8 "2023-08-29T14:39:08Z")

</div>

You did call the Get Chatters API using a broadcaster\_id of `158219238` and you didn’t pass in a broadcaster\_id of `mindconstructor`

The API might not check what you passed in was numeric.

So `698394232` is not a moderator for channel ID `mindconstructor` since `mindconstructor` isn’t an ID

---

<div class="post-metadata">

**Author:** ![Mindconstructor](https://d3ovvz0v2ajtpu.cloudfront.net/user_avatar/discuss.dev.twitch.com/mindconstructor/32/25938_2.png) [@Mindconstructor](https://discuss.dev.twitch.com/u/Mindconstructor)\
**Post date:** [August 29, 2023, 2:43pm UTC](https://discuss.dev.twitch.com/t/api-get-chatters-impossible/47071/9 "2023-08-29T14:43:05Z")

</div>

the call to the get chatters api accepts the text name of the broadcaster id before the whole time. without any issue. (the user-list was used SINCE A YEAR already this way!)

so … now i have to grab the user id of the target channel too? \<\_\< this is b\*llshit now … why twitch makes this more and more complicated?

btw: this worked before without a issue just with the display-name of the target channel! really!

---

<div class="post-metadata">

**Author:** ![Mindconstructor](https://d3ovvz0v2ajtpu.cloudfront.net/user_avatar/discuss.dev.twitch.com/mindconstructor/32/25938_2.png) [@Mindconstructor](https://discuss.dev.twitch.com/u/Mindconstructor)\
**Post date:** [August 29, 2023, 2:44pm UTC](https://discuss.dev.twitch.com/t/api-get-chatters-impossible/47071/10 "2023-08-29T14:44:56Z")

</div>

getting a user id from a channel name is way the most complicated stuff trough api … \<\_\<

---

<div class="post-metadata">

**Author:** ![BarryCarlyon](https://d3ovvz0v2ajtpu.cloudfront.net/user_avatar/discuss.dev.twitch.com/barrycarlyon/32/253_2.png) [@BarryCarlyon](https://discuss.dev.twitch.com/u/BarryCarlyon)\
**Post date:** [August 29, 2023, 2:46pm UTC](https://discuss.dev.twitch.com/t/api-get-chatters-impossible/47071/11 "2023-08-29T14:46:05Z")

</div>

> [@Mindconstructor](#):
>
> the call to the get chatters api accepts the text name of the broadcaster id before the whole time. without any issue. (the user-list was used SINCE A YEAR already this way!)

That is the _old_ API that was on tmi.twitch that old API is removed.

Helix uses numeric ID’s everywhere

> [@Mindconstructor](#):
>
> getting a user id from a channel name is way the most complicated stuff trough api … \<\_\<

Call get users, it’s not complicated I got it _very_ quickly

> **[Reference](https://dev.twitch.tv/docs/api/reference/#get-users)**
>
> Twitch Developer tools and services to integrate Twitch into your development or create interactive experience on twitch.tv.

CLI example

![image](https://d2opxh93rbxzdn.cloudfront.net/original/3X/9/7/9758b7d23ffad0fda04055bfae94504fad46f97f.png)

> [@Mindconstructor](#):
>
> so … now i have to grab the user id of the target channel too? \<\_\< this is b\*llshit now … why twitch makes this more and more complicated?

ID’s never change.

So if userID `123` called `fred` chooses to change their username  
They’ll become `123` called `bob`

The userID is static but the “label” on the account (the username) can and will change.

---

<div class="post-metadata">

**Author:** ![Mindconstructor](https://d3ovvz0v2ajtpu.cloudfront.net/user_avatar/discuss.dev.twitch.com/mindconstructor/32/25938_2.png) [@Mindconstructor](https://discuss.dev.twitch.com/u/Mindconstructor)\
**Post date:** [August 29, 2023, 2:56pm UTC](https://discuss.dev.twitch.com/t/api-get-chatters-impossible/47071/12 "2023-08-29T14:56:39Z")

</div>

damn … so now i have to rebuild my WHOLE BOT, because of this …

_sigh_

okay. thank you. Btw. i finally got my required response. Thank you twitch for not giving a single clue about THIS problem …

so now i’ll work on the /validate and reauthenticate stuff …

and when i solved THIS, i have to place this inside my bot code in javascript. and rewrite the database too \<\_\<

---

<div class="post-metadata">

**Author:** ![BarryCarlyon](https://d3ovvz0v2ajtpu.cloudfront.net/user_avatar/discuss.dev.twitch.com/barrycarlyon/32/253_2.png) [@BarryCarlyon](https://discuss.dev.twitch.com/u/BarryCarlyon)\
**Post date:** [August 29, 2023, 3:03pm UTC](https://discuss.dev.twitch.com/t/api-get-chatters-impossible/47071/13 "2023-08-29T15:03:52Z")

</div>

> [@Mindconstructor](#):
>
> damn … so now i have to rebuild my WHOLE BOT, because of this …

You are also aware that all the chat commands (/ban /timeout) are API calls now? (Since februrary)

Which requires the numeric channel\_id (and numeric moderator ID and the numeric id of the user in question which you can pull from the chat tags)

Every time my bot restarts the first thing it does is channelID to login so it can check it’s joining the right channel, in case some of my clients are susprirsed changed names on me

> [@Mindconstructor](#):
>
> so now i’ll work on the /validate and reauthenticate stuff …

You are not already doing this for the token the bot uses to login to chat with?

> [@Mindconstructor](#):
>
> Thank you twitch for not giving a single clue about THIS problem …

The Dev Forums and Dev Discord are the locations for assistance with Third Party development, so Twitch Support, if thats whom you reached out to will have sent you here.

---

<div class="post-metadata">

**Author:** ![system](https://d2opxh93rbxzdn.cloudfront.net/original/2X/0/05909729875cce3b53e59d5561c4d23bb14948fc.png) [@system](https://discuss.dev.twitch.com/u/system)\
**Post date:** [September 28, 2023, 3:04pm UTC](https://discuss.dev.twitch.com/t/api-get-chatters-impossible/47071/14 "2023-09-28T15:04:05Z")

</div>

Closed on September 28, 2023, 3:04pm UTC

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