PHPackages                             simply-stream/twitch-api - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. simply-stream/twitch-api

ActiveLibrary[API Development](/categories/api)

simply-stream/twitch-api
========================

2.0.1(1mo ago)0542[1 issues](https://github.com/Simply-Stream/TwitchApi/issues)2MITPHPPHP ^8.4CI failing

Since Nov 26Pushed 1mo agoCompare

[ Source](https://github.com/Simply-Stream/TwitchApi)[ Packagist](https://packagist.org/packages/simply-stream/twitch-api)[ RSS](/packages/simply-stream-twitch-api/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (9)Dependencies (24)Versions (13)Used By (2)

PHP Twitch API implementation (by Simply-Stream.com)
====================================================

[](#php-twitch-api-implementation-by-simply-streamcom)

[![QA](https://github.com/Simply-Stream/TwitchApi/actions/workflows/qa.yaml/badge.svg?branch=main)](https://github.com/Simply-Stream/TwitchApi/actions/workflows/qa.yaml) [![codecov](https://camo.githubusercontent.com/980fe5d559d68be1b64d57833ee638aad13e8f4db486dbb5966b5ef53acefde4/68747470733a2f2f636f6465636f762e696f2f67682f53696d706c792d53747265616d2f5477697463684170692f67726170682f62616467652e7376673f746f6b656e3d48543649554a4b4d3937)](https://codecov.io/gh/Simply-Stream/TwitchApi)

---

Key Features
------------

[](#key-features)

**Framework-agnostic**: The library depends on PSR-18 (HTTP client) and PSR-17 (request/stream factories) only. It contains no framework bindings and instantiates no HTTP client of its own.

**Serializer-independent**: JSON mapping is delegated through two interfaces, `DenormalizerInterface` and `NormalizerInterface`. A Symfony Serializer bridge is available as a separate package; any other implementation works as long as it satisfies the two interfaces. The models carry no serializer-specific attributes.

**Helix coverage**: One client class per API namespace (`UsersApi`, `StreamsApi`, `ChannelsApi`, …), each extending `AbstractApi` and taking the same three constructor arguments. Requests and responses are typed objects; no associative arrays are returned.

**Typed models**: Responses map to `final readonly` classes with promoted, typed properties. Timestamps are typed as `DateTimeInterface`. Nullability follows observed API behavior, including fields where Twitch sends an empty string rather than `null`.

**EventSub webhook pipeline**: `EventSubMessageProcessor` handles signature verification (HMAC-SHA256), message freshness, deduplication, and type resolution. Subscription type and version are read from the `Twitch-Eventsub-Subscription-Type` and `-Version` headers, which allows multiple versions of the same type — such as `channel.moderate` v1 and v2 — to be registered simultaneously.

To see a full list of implemented APIs and EventSub events, have a look at the [Implemented APIs](#implemented-apis)section.

Implemented APIs
----------------

[](#implemented-apis)

APIImplementedTestedAdsApi✅✅AnalyticsApi✅✅BitsApi✅✅ChannelPointsApi✅✅ChannelsApi✅✅CharityApi✅✅ChatApi✅✅ClipsApi✅✅ContentClassificationApi✅✅EntitlementsApi✅ℹ️EventSubApi✅✅EventSub system✅ℹ️ExtensionsApi✅✅GamesApi✅✅GoalsApi✅✅GuestStarApi (Beta)✅✅HypeTrainApi✅✅ModerationApi✅✅PollsApi✅✅PredictionsApi✅✅RaidsApi✅✅ScheduleApi✅✅SearchApi✅✅StreamsApi✅✅SubscriptionsApi✅✅TeamsApi✅✅UsersApi✅✅VideosApi✅✅WhispersApi✅✅✅ = Implemented, covered by unit and integration tests
❗ = Not migrated to v2 / not covered ℹ️ = Implemented and covered, with known open questions:

- **EntitlementsApi** — `ProductData` may use camelCase keys (`inDevelopment`, `displayName`). Never verified against real payloads.
- **EventSub system** — the message pipeline (signature verification, freshness, type registry, denormalization, dispatch) is covered by functional roundtrip tests, but only for the event types the Twitch CLI can trigger. See the table below.

EventSub Events
---------------

[](#eventsub-events)

EventModelledRoundtrip testautomod.message.hold (v1, v2)✅❗automod.message.update (v1, v2)✅❗automod.settings.update✅❗automod.terms.update✅❗channel.ad\_break.begin✅✅channel.ban✅✅channel.bits.use✅❗channel.channel\_points\_automatic\_reward\_redemption.add (v1, v2)✅❗channel.channel\_points\_custom\_reward.add / remove / update✅✅channel.channel\_points\_custom\_reward\_redemption.add / update✅✅channel.charity\_campaign.donate / progress / start / stop✅✅channel.chat.clear✅❗channel.chat.clear\_user\_messages✅❗channel.chat.message✅❗channel.chat.message\_delete✅❗channel.chat.notification✅❗channel.chat.user\_message\_hold / update✅❗channel.chat\_settings.update✅❗channel.cheer✅✅channel.custom\_power\_up\_redemption.add✅❗channel.follow (v2)✅✅channel.goal.begin / end / progress✅✅channel.guest\_star\_guest.update (Beta)✅❗channel.guest\_star\_session.begin / end (Beta)✅❗channel.guest\_star\_settings.update (Beta)✅❗channel.hype\_train.begin / end / progress (v2)✅ℹ️channel.moderate (v1, v2)✅❗channel.moderator.add / remove✅✅channel.poll.begin / end / progress✅✅channel.prediction.begin / end / lock / progress✅✅channel.raid✅✅channel.shared\_chat.begin / end / update✅❗channel.shield\_mode.begin / end✅✅channel.shoutout.create / receive✅✅channel.subscribe✅✅channel.subscription.end / gift / message✅✅channel.suspicious\_user.message / update✅❗channel.unban✅✅channel.unban\_request.create✅ℹ️channel.unban\_request.resolve✅✅channel.update (v2)✅✅channel.vip.add / remove✅❗channel.warning.acknowledge / send✅❗conduit.shard.disabled✅❗drop.entitlement.grant✅✅extension.bits\_transaction.create✅✅stream.offline / online✅✅user.authorization.grant / revoke✅✅user.update✅✅user.whisper.message✅❗✅ = Modeled from the Twitch docs and exercised by a functional roundtrip test against a captured, signed webhook fixture
❗ = Modeled, but untested: `twitch event trigger` cannot produce this type, so no signed fixture exists
ℹ️ = Tested against a hand-patched fixture:

- **channel.hype\_train.\*** — the CLI only emits v1 payloads. The v2 fixtures were derived from the v1 capture plus the documented v2 fields; `all_time_high_level` and `all_time_high_total` carry invented values.
- **channel.unban\_request.create** — the CLI omits `event.id`, which the docs list as required. Added by hand.

> **Note:** "Modeled ✅" means the class matches the field-level documentation, not that it has been verified against production payloads. Twitch's example payloads proved unreliable throughout; the field tables are the authoritative source.

Usage
-----

[](#usage)

### Getting started

[](#getting-started)

#### Installation

[](#installation)

```
composer req simplystream/twitch-api
```

#### Creating an API client

[](#creating-an-api-client)

The library is framework-agnostic and ships no HTTP client of its own. Bring your own PSR-18 client and PSR-17 factories, plus a serializer that implements the library's `DenormalizerInterface` and `NormalizerInterface`.

```
use Nyholm\Psr7\Factory\Psr17Factory;
use SimplyStream\TwitchApi\Helix\Api\ApiClient;
use SimplyStream\TwitchApi\Helix\Api\UsersApi;
use SimplyStream\TwitchApi\Helix\Api\Users\Request\GetUsersRequest;

$psr17 = new Psr17Factory();
$httpClient = new Symfony\Component\HttpClient\Psr18Client();

$apiClient = new ApiClient(
    httpClient: $httpClient,
    requestFactory: $psr17,
    streamFactory: $psr17,
    clientId: 'your-client-id',
);

// Any object implementing both DenormalizerInterface and NormalizerInterface.
// A ready-made Symfony Serializer setup will be provided by the serializer bridge
// package; see "Serialization".
$serializer = /* ... */;

$usersApi = new UsersApi($apiClient, $serializer, $serializer);

$response = $usersApi->getUsers(
    new GetUsersRequest(logins: ['twitchdev']),
    $accessToken,
);

foreach ($response->data as $user) {
    echo $user->displayName, ' — ', $user->broadcasterType, PHP_EOL;
}
```

We recommend either using the all-in-one package [Guzzlehttp](https://packagist.org/packages/guzzlehttp/guzzle%5D) or [PHP-HTTP](https://packagist.org/packages/php-http/curl-client) with the PSR7 implementation [Nyholm/PSR7](https://github.com/Nyholm/psr7) or [Guzzle/PSR7](https://packagist.org/packages/guzzlehttp/psr7).

Every namespace of the Helix API has its own slim client class — `UsersApi`, `StreamsApi`, `ChannelsApi`, and so on. They all take the same three constructor arguments, so wiring them up in a DI container is a one-liner per class.

Each method takes a request object and an access token:

```
use SimplyStream\TwitchApi\Helix\Api\StreamsApi;
use SimplyStream\TwitchApi\Helix\Api\Streams\Request\GetStreamsRequest;
use SimplyStream\TwitchApi\Helix\Api\Streams\StreamType;

$streamsApi = new StreamsApi($apiClient, $serializer, $serializer);

$streams = $streamsApi->getStreams(
    new GetStreamsRequest(
        userLogins: ['twitchdev', 'twitch'],
        type: StreamType::Live,
        first: 50,
    ),
    $accessToken,
);

echo $streams->data[0]->title;
echo $streams->pagination?->cursor;
```

### Access tokens

[](#access-tokens)

This library does not implement the OAuth flow. Getting and refreshing tokens is up to you — use any OAuth client you like, or Twitch's own endpoints directly.

What the library needs is an implementation of `AccessTokenInterface`, which it uses to build the `Authorization`header. Wrapping a token string you already hold is enough:

```
use SimplyStream\TwitchApi\Helix\Authentication\AccessTokenInterface;

final readonly class MyAccessToken implements AccessTokenInterface
{
    public function __construct(
        private string $token,
    ) {
    }

    public function getAccessToken(): string
    {
        return $this->token;
    }
}
```

Which scopes a call requires is documented on each API method. Most read endpoints accept an app access token; anything that acts on a broadcaster's behalf needs a user access token with the matching scope.

Extension endpoints expect a JWT signed with your extension secret instead of an OAuth token. The library treats both the same way — wrap whichever token you hold in an `AccessTokenInterface` implementation. Creating the JWT is up to you.

### Custom base URL

[](#custom-base-url)

The `$baseUrl` constructor argument defaults to `https://api.twitch.tv/helix` and can be pointed elsewhere — at the Twitch CLI's mock API, for instance:

```
$apiClient = new ApiClient(
    httpClient: $httpClient,
    requestFactory: $psr17,
    streamFactory: $psr17,
    clientId: 'your-client-id',
    baseUrl: 'http://localhost:8080/mock',
);
```

### Serialization

[](#serialization)

*To be documented once the serializer bridge package is available.*

Supported Frameworks
--------------------

[](#supported-frameworks)

Currently, there is only an integration for [Symfony](https://symfony.com).

- [simplystream/twitch-api-bundle](https://github.com/Simply-Stream/TwitchApiBundle) (Still WIP, most of the code there has been moved to this repository)

Contribution
------------

[](#contribution)

We welcome contributions! Feel free to open issues, submit pull requests, or join our community discussions. A short guide for contribution will follow.

Support
-------

[](#support)

You really like this project and want to support us differently than contribution? Feel free to support me on Ko-fi ♥️

[![ko-fi](https://camo.githubusercontent.com/201ef269611db7eb6b5d08e9f756ab8980df3014b64492770bdf13a6ed924641/68747470733a2f2f6b6f2d66692e636f6d2f696d672f676974687562627574746f6e5f736d2e737667)](https://ko-fi.com/R6R0HV2IO)

###  Health Score

49

—

FairBetter than 94% of packages

Maintenance91

Actively maintained with recent releases

Popularity16

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity68

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~96 days

Recently: every ~223 days

Total

11

Last Release

32d ago

Major Versions

1.1.2 → 2.0.02026-07-16

PHP version history (2 changes)1.0-RC1PHP ^8.2

2.0.0PHP ^8.4

### Community

Maintainers

![](https://www.gravatar.com/avatar/5d499065cbba35901f6cb7f3ef2c7cc8805d654616c44661a7aaffbee2d0b394?d=identicon)[TobiDev](/maintainers/TobiDev)

---

Top Contributors

[![aaricdev](https://avatars.githubusercontent.com/u/84964307?v=4)](https://github.com/aaricdev "aaricdev (70 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/simply-stream-twitch-api/health.svg)

```
[![Health](https://phpackages.com/badges/simply-stream-twitch-api/health.svg)](https://phpackages.com/packages/simply-stream-twitch-api)
```

###  Alternatives

[tempest/framework

The PHP framework that gets out of your way.

2.3k37.6k21](/packages/tempest-framework)[flow-php/flow

PHP ETL - Extract Transform Load - Data processing framework

86337.5k](/packages/flow-php-flow)[sylius/sylius

E-Commerce platform for PHP, based on Symfony framework.

8.5k6.0M777](/packages/sylius-sylius)[cakephp/cakephp

The CakePHP framework

8.9k20.0M1.9k](/packages/cakephp-cakephp)[drupal/core-recommended

Locked core dependencies; require this project INSTEAD OF drupal/core.

6943.5M449](/packages/drupal-core-recommended)[civicrm/civicrm-core

Open source constituent relationship management for non-profits, NGOs and advocacy organizations.

762297.9k53](/packages/civicrm-civicrm-core)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
