Proper way to perform a helix API call Using JavaScript

Hey all,
I’ll be honest in that though I have experience in other languages, this is my first time working in JS and twitch. As such, I’m unfamiliar with Ajax, fetch, and pretty much making API calls in general.

Any code that I write seems to compile fine, but I cannot get any response from the twitch API in my code. I do understand that there is supposed to be a specific setup for the API call (header info and all that), but I do not know how to properly create such a call, and after browsing forums and the docs, I have no come across any examples in how to properly format it in JS. (You may be wondering at this point why I’m trying in an unfamiliar language: im adding functionality to a pre-existing Standalone script As part of a fork of a project that currently doesn’t make use of the API).

Once I get the JSON payload I think I’ll be fine, but I just can’t seem to get it.

Here is my goal:

  • user already provides their twitch auth-token into the script as part of a login process. If I can use that provided token instead of a generic one provided to me that would be preferable. (Concern here is the rate limit scalability).
  • main goal is to check if a specific Chanel is streaming or not then return a Boolean true if they are live.

For ease of testing, I tried various ‘sandbox’ websites, and as far as I can tell I keep getting ‘’ back when I try to write the response to the console. Meaning I don’t think anything I wrote actually sent a request (as I said, I’m unfamiliar with how JS can fetch data)

Assuming you mean “making a call in the front end”, your first problem is to solve the authentication requirements. here is an example that uses implicit auth to log the user in first. Which put simply, is the safest way to obtain and use a users token. (You can return/use the users “normal” oAuth token, but that means you have a server doing the token exchange store and recall, and you shouldn’t leak an app access token on the front end)

The key part is line 28, using fetch to make the call.

If you don’t want to or can’t log the user in, or it’s not practical (to use implicit auth), then you make a fetch/ajax call from your front end to your backend, and the backend will use it’s app access token to make the call instead. (and cache the result to save extra API calls). Which would be the preferred method really.

TLDR: You need a server, so you probably won’t be making calls in the frontend to helix really at all

And if you are working with a known set of streamers you can implement Webhooks instead and make no API requests at all, (except to maintain your Webhooks)

https://dev.twitch.tv/docs/api/webhooks-guide

Ideally in this example, you’d authenticate the user on the server and store their keys in your database.

An authentication token gives you 800 API requests per minute (give or take) and with that you can look up 100 streams per request. So you can look up quite a lot of streams per minute if running at full speed

Rate limit docs:

Streams documentation

You can specify up to 100 user/userID’s per request

Hey thanks for the information. That fetch call looks pretty similar to what I concocted, but I think I was missing the “.then” statements.

As for the API call info, this is a ‘distributed app’ so to speak. The base app/script is already available and in use by people, I’m just looking to add some functionality to it. Every person using this is doing so self hosted, either as a windows/Linux app or via Docker. As such, there is no way to have a centralized dB or cache system that would work in that way.

Essentially, the base script was checking the front page of a specific game, and recommended a random live stream that had a specific tag. What I want to do is check if a preferred streamer (user set) is online and recommend them over a random. Each instance of the script would at most run a api call twice every minute, but I have no way to know how many Instanced exist.

Then to log the user in you’d either

  • use implicit auth
  • have the user provide their own ClientID and ClientSecret to the app

Since you can’t share your ClientSecret, but you can share a ClientID

Rate limiting is roughly, by Token/IP key pair. So the instance count doesn’t matter in this case.

Thanks for the info on the rate limiting.

I’d be looking into implicit auth. After reviewing the implicit auth flow, Since it’s a local app, I’ll just redirect to local goat and get the generated access token that way.

But question on that: when generating the request, I see the carots in the string: should the value be between them, or replace them?

client_id=<your client ID>

Does that become ```
client_id=<xxxxxxxxxxxx>

?

Check the github pages/live example

The items you linked to helped a lot. But I am still having some trouble getting everything working properly.
I have been able to perform the call correctly, but in ordfer to streamline my code I decided to functionize the call.
My major is that my function “APICall” just refuses to ‘return’ data (either the JSON object or a simple string. Neither is returning to parent call.). I verified this by having the 'document.write(“Response Received”) line in the parent routine ‘CheckStreamerOnline’.

I have been testing my code on : https://js.do/ for quick and easy testing, so all of the ‘document.write’ stuff is to get output there. (You can copy-paste this write into that page to get a live result)

Here is my code:

<script>
const oAuthorizationURL = 'https://id.twitch.tv/oauth2/authorize?' ;
const ImplicitAuthURL = 'https://api.twitch.tv/helix/users?' ;
const TokenValidationURL = 'https://api.twitch.tv/helix/' 
//'https://id.twitch.tv/oauth2/validate'
const TokenURL = 'https://id.twitch.tv/oauth2/token?' ;
const StreamsURL = 'https://api.twitch.tv/helix/streams?'
const AppRedirectURL = 'http://localhost'
const AppClientID = 'y4344ir7sj3tem9cqnzkv288grm4e9'
var ApiToken = 'REMOVED'
 
 
async function APICall(URL, HeadersObject) {
  // Perform API Call and return a JSON object
  document.write("<br> Performing API Call to URL: " + URL)
  fetch( URL ,   { headers: HeadersObject   } )
  .then(resp => resp.json())
  .then(resp => {
  	if (resp.error) {
			document.write("<br> URL: " + URL)
        document.write("<br>")
        document.write("<br>API Response:")
        document.write("<br>")
			document.write("Error: " + resp.error)
        document.write("<br>")
        document.write("Status: " + resp.status)
        document.write("<br>")
        document.write("Message: " + resp.message)
        document.write("<br>")
        return "ERROR"
  	}else {
    	document.write("<br>API Returned without error <br>")
    	document.write("<br><br> Literal Reponse: <br>")
  			document.write(JSON.stringify(resp) + "<br><br>")
	      	return resp.json()
  	}	
  })
  .catch(resp,err => {
document.write('🤬 API Access Error: ', err);
try {
	document.write("<br><br> Literal Reponse: <br>")
  		document.write(JSON.stringify(resp) + "<br><br>")
    }
    finally{ return ERROR };
  });
  return "ERROR"
};  
 
async function CheckStreamerOnline(ChannelURL) {
  var bolONLINE = false;
  if (ChannelURL ===  'NoValueSet' || ChannelURL ===  '' || ChannelURL ==  null ) {  
  // No Value -> Ignore
  bolOnline = false; 
  } else { 
  	//Perform API check to determine if the specific channel is online
var myHeaders = new Headers(); // Currently empty
myHeaders.append("Client-ID", AppClientID)
myHeaders.append("Authorization", "Bearer " + ApiToken)

var Resp = await APICall(StreamsURL + 'user_login=' + ChannelURL, myHeaders);
document.write("<br>Response Received<br>")
if (Resp.data[0]){
	bolONLINE =  TRUE
    }else {
    bolONLINE = FALSE         
    };
    
};   // end of large if statement    
return bolONLINE
  };



async function main() {
  document.write("<br>" +"=========================");
  var R = await CheckStreamerOnline('warframe');
  document.write("<br>CheckStreamerOnline Response: <br>" + R);
};

main();
</script>

Removed your leaked generated token (ApiToken) they should be treated as “passwords” and not leaked/posted publicly to forums

I imagine you’ve messed something up with your async calls, I don’t use async to much so I can’t be sure. So I imagine you have something not calling/behaving right due to this mix.

I even converted them into non-async calls, it just isn’t returning the variable to the parent routine.

Resp = await APICall(StreamsURL + 'user_login=' + ChannelURL, myHeaders);
document.write("<br>Response Received<br>")

It never hits the ‘document.write’ line, even if not using the ‘await’ and removing the ‘async’ descriptor from the call.

Found out my problem. : Javascript is weird, and returning data to parent routines is confusing AF.

So the ‘fetch’ function I call is in and of itself a function. So by ‘returning’ inside the “.then” statement was purely returning the variable to the ‘CallAPI’ function. Call API was never returning anything to its parent routine. I tried doing what some help guides said (’Return Fetch’) but that didnt work at all, I couldn’t chain it into the .then routines. I also couldn’t return it as a boolean true/false, I had to put that into quotes to make it strings. But it will work for what I need it to.

Here is updated working code:

async function APICall(URL, HeadersObject) {
  // Perform API Call and return a JSON object
  document.write("<br> Performing API Call to URL: " + URL)
  var resp = await   fetch( URL ,   { headers: HeadersObject   } )
  var D = resp.json()
  if (D.error) {
        return "ERROR"
  	}else {
	    return D
  	}	
};  
 
 async function CheckStreamerOnline(ChannelURL) {
  var bolONLINE = false;
  if (ChannelURL ===  'NoValueSet' || ChannelURL ===  '' || ChannelURL ==  null ) {  
  // No Value -> Ignore
  bolOnline = false; 
  } else { 
  	//Perform API check to determine if the specific channel is online
	var myHeaders = new Headers(); // Currently empty
	myHeaders.append("Client-ID", AppClientID)
	myHeaders.append("Authorization", "Bearer " + ApiToken)

	var Resp = await APICall(StreamsURL + 'user_login=' + ChannelURL, myHeaders);
  	if (Resp.data[0]){
    	bolONLINE =  "TRUE"
    }else {
    	bolONLINE = "FALSE"         
    }
    
  };   // end of large if statement    
	return bolONLINE
 };

Thanks for all the help Barry, it pointed me in the right direction.
I was able to get my project working eventually, and with some streamlined code I’m fairly proud of.

For anyone that needs it in the future, here is my fairly basic module I wrote up for interacting with Helix via Javascript and NodeJS:

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