helix Go Client for Twitch Helix API
repository·main·Indexed 43 days ago
https://github.com/nicklaw5/helixA Go client library for the Twitch Helix API. It provides programmatic access to Twitch endpoints, including support for App, User, and Device access tokens, automatic token refreshing, and built-in rate limit handling. Features include methods for managing commercials via StartCommercial, retrieving game and extension analytics, and handling OAuth2 authentication flows.
What's inside helix
- helix is a Go (Golang) client library for interacting with the Twitch Helix API. It provides a structured way to access Twitch's expanding set of endpoints and features. Note that as Twitch updates its API, the maintainers of this package implement new changes on a best-effort basis.
Manage request rate limiting
mainTwitch enforces rate limits. Helix provides helper methods on response objects to retrieve rate limit headers as integers:
Response.GetRateLimit()Response.GetRateLimitRemaining()Response.GetRateLimitReset()Response.GetClipsCreationRateLimit()(specific toCreateClip)Response.GetClipsCreationRateLimitRemaining()(specific toCreateClip)
You can provide a custom
RateLimitFuncinhelix.Options. If provided, the client will re-attempt a request if it receives a 429 (Too Many Requests) response, applying the function before retrying.Helix includes a
DefaultRateLimitFuncthat automatically waits until the rate limit reset time before retrying.client, err := helix.NewClient(&helix.Options{ ClientID: "your-client-id", RateLimitFunc: helix.DefaultRateLimitFunc, })Handle API responses and errors
mainEvery API request returns a response object that includes a
ResponseCommonstruct. This struct contains the HTTP status code and any error information returned by Twitch. This is useful for detecting 500 errors or invalid requests.Common fields in
ResponseCommon:StatusCode(int): The HTTP status code.Header(http.Header): The response headers.Error(string): The error string returned by the API.ErrorStatus(int): The error status code.ErrorMessage(string): The error message returned by the API.
type ResponseCommon struct { StatusCode int Header http.Header Error string `json:"error"` ErrorStatus int `json:"status"` ErrorMessage string `json:"message"` }Cancel a Raid using helix.CancelRaid
mainTo cancel an ongoing raid, use the
CancelRaidmethod on ahelix.Clientinstance. You must provide aCancelRaidParamsobject containing theBroadcasterIDof the broadcaster whose raid is being cancelled.client, err := helix.NewClient(&helix.Options{ ClientID: "your-client-id", }) if err != nil { // handle error } resp, err := client.CancelRaid(&helix.CancelRaidParams{ BroadcasterID: "22484632", }) if err != nil { // handle error } fmt.Printf("%+v\n", resp)Paginate through drops entitlements results
mainWhen retrieving drops entitlements, use the
AfterandFirstfields inGetDropEntitlementsParamsto handle pagination. To fetch the next page of results, pass theCursorvalue from the current response's pagination object into theAfterfield of the next request.Example of fetching the first page and then the subsequent page:
// First page resp, err := client.GetDropsEntitlements(&helix.GetDropEntitlementsParams{ GameID: "your-game-id", After: "", First: 50, }) // Next page resp, err = client.GetDropsEntitlements(&helix.GetDropEntitlementsParams{ GameID: "your-game-id", After: resp.Data.Pagination.Cursor, First: 50, })// Next page resp, err = client.GetDropsEntitlements(&helix.GetDropEntitlementsParams{ GameID: "your-game-id", After: resp.Data.Pagination.Cursor, First: 50, })Revoke a User Access Token
mainUse
RevokeUserAccessTokento invalidate an existing user access token. You can retrieve the current token from the client usingGetUserAccessToken()before passing it to the revocation method.client, err := helix.NewClient(&helix.Options{ ClientID: "your-client-id", }) if err != nil { // handle error } userAccessToken := client.GetUserAccessToken() resp, err := client.RevokeUserAccessToken(userAccessToken) if err != nil { // handle error } fmt.Printf("%+v\n", resp)Set the User-Agent header
mainYou can customize the
User-Agentheader sent with every request using two methods:- During Client Initialization: Pass the
UserAgentstring within thehelix.Optionsstruct when callinghelix.NewClient(). - After Client Initialization: Call the
SetUserAgent(string)method on an existing client instance.
This is useful for identifying your application to the Helix API.
// Method 1: Via Options client, err := helix.NewClient(&helix.Options{ ClientID: "your-client-id", UserAgent: "your-user-agent-value", }) // Method 2: Via SetUserAgent method client.SetUserAgent("your-user-agent-value")- During Client Initialization: Pass the
Authenticate Extension Helix Requests using EBS JWT
mainTo make authenticated requests to Extension endpoints, you must initialize the client with
ExtensionOptions, generate claims with the appropriate permissions, sign the JWT, and then set it on the client.Note: Currently, only the
'external'role is supported by helix endpoints.Steps:
- Initialize
helix.NewClientwithExtensionOptscontainingOwnerUserID,Secret,ConfigurationVersion, andVersion. - Generate claims using
client.ExtensionCreateClaims(broadcasterID, permissions, role, expiration). - Sign the claims using
client.ExtensionJWTSign(claims). - Apply the token to the client using
client.SetExtensionSignedJWTToken(jwt).
client, err := helix.NewClient(&helix.Options{ ClientID: "your-client-id", UserAccessToken: "your-user-access-token", ExtensionOpts: helix.ExtensionOptions{ OwnerUserID: os.Getenv("EXT_OWNER_ID"), Secret: os.Getenv("EXT_SECRET"), ConfigurationVersion: os.Getenv("EXT_CFG_VERSION"), Version: os.Getenv("EXT_VERSION"), }, }) if err != nil { // handle err } // Generate claims with permissions (e.g., Broadcast) claims, err := client.ExtensionCreateClaims(broadcasterID, client.FormBroadcastSendPubSubPermissions(), 0) if err != nil { // handle err } // Sign the JWT jwt, err := client.ExtensionJWTSign(claims) if err != nil { // handle err } // Set the token before making extension endpoint requests client.SetExtensionSignedJWTToken(jwt)- Initialize
Get the Twitch Authorization URL
mainUse
GetAuthorizationURLto generate a URL that allows users to authenticate with their Twitch accounts. After authorization, the user is redirected to yourRedirectURIwith an authorization code.To use this, initialize a client with
ClientIDandRedirectURI, then callGetAuthorizationURLwith anAuthorizationURLParamsobject.client, err := helix.NewClient(&helix.Options{ ClientID: "your-client-id", RedirectURI: "https://example.com/auth/callback", }) if err != nil { // handle error } url := client.GetAuthorizationURL(&helix.AuthorizationURLParams{ ResponseType: "code", // or "token" Scopes: []string{"user:read:email"}, State: "some-state", ForceVerify: false, }) fmt.Printf("%s\n", url)Verify and handle EventSub notifications
mainWhen receiving a webhook notification from Twitch, follow these steps to ensure security and successful verification:
- Verify Signature: Use
helix.VerifyEventSubNotificationwith your configuredSecret, the requestHeader, and the raw requestbody. This ensures the notification actually came from Twitch. - Handle Challenges: If the request contains a
Challengefield (used during the initial subscription handshake), you must respond to the request by writing only theChallengestring back to the client. - Parse Event Data: Once verified, decode the
eventfield (which is ajson.RawMessage) into the specific event type struct (e.g.,helix.EventSubChannelFollowEvent). - Respond Successfully: Always return a
200 OKstatus to Twitch to prevent subscription expiration due to high failure rates.
type eventSubNotification struct { Subscription helix.EventSubSubscription `json:"subscription"` Challenge string `json:"challenge"` Event json.RawMessage `json:"event"` } func eventsubFollow(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) if err != nil { log.Println(err) return } defer r.Body.Close() // verify that the notification came from twitch using the secret. if !helix.VerifyEventSubNotification("s3cre7w0rd", r.Header, string(body)) { log.Println("no valid signature on subscription") return } else { log.Println("verified signature for subscription") } var vals eventSubNotification err = json.NewDecoder(bytes.NewReader(body)).Decode(&vals) if err != nil { log.Println(err) return } // if there's a challenge in the request, respond with only the challenge to verify your eventsub. if vals.Challenge != "" { w.Write([]byte(vals.Challenge)) return } var followEvent helix.EventSubChannelFollowEvent err = json.NewDecoder(bytes.NewReader(vals.Event)).Decode(&followEvent) log.Printf("got follow webhook: %s follows %s\n", followEvent.UserName, followEvent.BroadcasterUserName) w.WriteHeader(200) w.Write([]byte("ok")) }- Verify Signature: Use
Refresh a Device Access Token
mainUse
RefreshTokento refresh a device access token. You can retrieve the current refresh token from the client usingGetRefreshToken().client, err := helix.NewClient(&helix.Options{ ClientID: "your-client-id", }) if err != nil { // handle error } // Get the refresh token from the client refreshToken := client.GetRefreshToken() if refreshToken != "" { resp, err := client.RefreshToken(refreshToken) if err != nil { // handle error } fmt.Printf("%+v\n", resp) }Refresh a User Access Token
mainUse
RefreshUserAccessTokento obtain a new user access token using a providedrefreshToken.client, err := helix.NewClient(&helix.Options{ ClientID: "your-client-id", ClientSecret: "your-client-secret", }) if err != nil { // handle error } refreshToken := "your-refresh-token" resp, err := client.RefreshUserAccessToken(refreshToken) if err != nil { // handle error } fmt.Printf("%+v\n", resp)