Which GraphQL API Does Bilistream Use for Twitch?
Bilistream uses Twitch's public GraphQL endpoint https://gql.twitch.tv/gql with a persisted query named StreamMetadata to check if a channel is broadcasting.
The bilistream repository implements a live-status monitoring system that queries Twitch's internal GraphQL API to detect stream activity. Instead of using Twitch's documented REST API (Helix), the project leverages the same persisted queries that power Twitch's web interface, enabling lightweight status checks without OAuth tokens. This approach is implemented in the Twitch plugin located at src/plugins/twitch.rs.
Twitch GraphQL Endpoint Configuration
Bilistream communicates with Twitch's unofficial but publicly accessible GraphQL infrastructure using a specific set of headers and a pre-hashed query.
Endpoint URL and Authentication
The plugin sends POST requests to the following configuration:
- Endpoint:
https://gql.twitch.tv/gql - Client-ID header:
kimne78kx3ncx6brgo4mv6wki5h1ko(Twitch's public web client identifier) - Content-Type:
application/json
This Client-ID is the standard identifier used by Twitch's own web application, allowing anonymous access to basic stream metadata without requiring a personal access token.
Persisted Query Details
The implementation utilizes a persisted GraphQL query—a query that has been previously registered with Twitch's servers and is referenced by its SHA-256 hash rather than the full query string:
- Operation name:
StreamMetadata - SHA-256 hash:
1c719a40e481453e5c48d9bb585d971b8b372f8ebb105b17076722264dfa5b3e - Version: 1
Persisted queries reduce payload size and improve caching performance by sending only the hash instead of the full GraphQL syntax.
Implementation in src/plugins/twitch.rs
The core logic resides in the Twitch struct's get_status async method, which constructs the HTTP request and parses the response to determine live status.
The get_status Method
According to the source code in src/plugins/twitch.rs (lines 27-45), the method builds a JSON payload containing the operation name, variables, and persisted query hash. The method signature accepts a channel identifier and returns a boolean indicating live status by examining the GraphQL response structure.
The request payload structure follows this exact format:
{
"operationName": "StreamMetadata",
"variables": {
"channelLogin": "<channel_name>"
},
"extensions": {
"persistedQuery": {
"version": 1,
"sha256Hash": "1c719a40e481453e5c48d9bb585d971b8b372f8ebb105b17076722264dfa5b3e"
}
}
}
Request Payload Structure
The variables object accepts a single parameter channelLogin (the Twitch channel name in lowercase), while the extensions.persistedQuery object contains the version and SHA-256 hash required by Twitch's APQ (Automatic Persisted Queries) system.
Practical Code Examples
Building the GraphQL Request in Rust
The following Rust code demonstrates how bilistream constructs the request using serde_json and reqwest_middleware:
use serde_json::json;
use reqwest_middleware::ClientWithMiddleware;
// ClientWithMiddleware instance and channel name
let payload = json!({
"operationName": "StreamMetadata",
"variables": {
"channelLogin": &room,
},
"extensions": {
"persistedQuery": {
"version": 1,
"sha256Hash":
"1c719a40e481453e5c48d9bb585d971b8b372f8ebb105b17076722264dfa5b3e"
}
}
});
let resp: serde_json::Value = client
.post("https://gql.twitch.tv/gql")
.header("Client-ID", "kimne78kx3ncx6brgo4mv6wki5h1ko")
.json(&payload)
.send()
.await?
.json()
.await?;
Parsing the Live Status Response
After receiving the JSON response, bilistream checks the data.user.stream.type field to determine if the channel is broadcasting:
let is_live = resp["data"]["user"]["stream"]["type"] == "live";
println!("Channel {} is live: {}", room, is_live);
Minimal Standalone Example
For testing outside the bilistream context, this complete example requires tokio and reqwest-middleware:
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = reqwest_middleware::ClientBuilder::new(reqwest::Client::new())
.build();
let channel = "example_channel";
let payload = json!({
"operationName": "StreamMetadata",
"variables": { "channelLogin": channel },
"extensions": {
"persistedQuery": {
"version": 1,
"sha256Hash":
"1c719a40e481453e5c48d9bb585d971b8b372f8ebb105b17076722264dfa5b3e"
}
}
});
let resp: serde_json::Value = client
.post("https://gql.twitch.tv/gql")
.header("Client-ID", "kimne78kx3ncx6brgo4mv6wki5h1ko")
.json(&payload)
.send()
.await?
.json()
.await?;
let is_live = resp["data"]["user"]["stream"]["type"] == "live";
println!("Stream live status: {}", is_live);
Ok(())
}
Response Handling and Live Detection
The GraphQL response returns a nested JSON structure where the stream object is null when offline. Bilistream specifically evaluates resp["data"]["user"]["stream"]["type"] against the string literal "live" to set the boolean live status. This check occurs within the get_status method implementation, allowing the plugin system to trigger recording or relay actions when the condition evaluates to true.
Summary
- Bilistream queries
https://gql.twitch.tv/gqlusing the public Client-IDkimne78kx3ncx6brgo4mv6wki5h1ko. - It uses the
StreamMetadatapersisted query with SHA-256 hash1c719a40e481453e5c48d9bb585d971b8b372f8ebb105b17076722264dfa5b3eto minimize payload size. - The
get_statusmethod insrc/plugins/twitch.rsconstructs the request and parsesdata.user.stream.typeto detect live broadcasts. - No OAuth tokens are required for this specific query, making it suitable for lightweight monitoring applications.
Frequently Asked Questions
What is the exact Twitch GraphQL endpoint URL used by bilistream?
Bilistream sends POST requests to https://gql.twitch.tv/gql, which is Twitch's public GraphQL gateway. This endpoint accepts persisted queries and standard GraphQL operations, though the project specifically uses the persisted query protocol for efficiency.
Why does bilistream use a persisted query instead of a full GraphQL query string?
Persisted queries reduce network overhead by transmitting only a SHA-256 hash rather than the complete query syntax. This approach also leverages Twitch's APQ (Automatic Persisted Queries) caching layer, resulting in faster response times and reduced bandwidth consumption compared to sending full query strings.
What Client-ID does bilistream use for Twitch API requests?
The implementation uses the Client-ID kimne78kx3ncx6brgo4mv6wki5h1ko, which is the public identifier used by Twitch's own web interface. This Client-ID allows unauthenticated access to basic stream metadata through the GraphQL endpoint without requiring an OAuth access token or application registration.
How does bilistream determine if a Twitch channel is currently live?
The get_status method examines the JSON path data.user.stream.type in the GraphQL response. When this field equals the string "live", the channel is broadcasting; if the field is null or any other value, the channel is considered offline. This logic is implemented in src/plugins/twitch.rs as part of the Twitch struct's live detection system.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →