spatie / twitter-streaming-api
Easily work with the Twitter Streaming API
Requires
- php: ^8.5
- redwebcreation/twitter-stream-api: ^2.2.1
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.95.28
- phpunit/phpunit: ^13.4.6
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Listen for public posts matching keywords, authors, languages, or bounding boxes using the X API v2 Filtered Stream.
This package does not subscribe to likes, follows, private activity, or legacy user events. Those require a separate X Activity API integration with its own subscriptions and authentication.
Requires PHP 8.5+. CI exercises PHP 8.5 and PHP 8.6 development builds on Linux and Windows.
use Spatie\TwitterStreamingApi\PublicStream; PublicStream::create($bearerToken) ->whenHears('@spatie_be', function (object $message) { if (isset($message->data)) { echo $message->data->text; } }) ->startListening();
There is no polling. The client reads a persistent HTTPS stream until it closes or your callback throws. Reconnection, backoff, application supervision, and fast callback processing are the caller's responsibility.
Support us
We invest a lot of resources into creating best in class open source packages. You can support us by buying one of our paid products.
We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on our contact page. We publish all received postcards on our virtual postcard wall.
Postcardware
You're free to use this package (it's MIT-licensed), but if it makes it to your production environment we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using.
Our address is: Spatie, Kruikstraat 22, 2018 Antwerp, Belgium.
The best postcards will get published on the open source page on our website.
Installation
You can install the package via composer:
composer require spatie/twitter-streaming-api
Getting credentials
Create an app in the X Developer Console and obtain its app Bearer Token. Your account must have access to Filtered Stream and the operators you use. Access, billing, rule limits, and geo restrictions are controlled by X; installing this package does not grant them. No API key or API secret argument is needed for these requests.
Usage
Rule ownership: startListening() deletes all existing filtered-stream rules for the app, then adds the configured
rule before connecting. Use a dedicated app; do not share these rules with other consumers. Starting again replaces the
rules again. API and callback exceptions propagate to the caller.
Callbacks receive the upstream client's decoded object envelope, not a legacy tweet array:
$message->data->id; $message->data->text; $message->includes; // Only when returned by the API. $message->matching_rules;
Keep-alive blank lines are not delivered to callbacks. Handle envelopes without data, including any streamed error
information your application receives. User details are not automatically hydrated at $tweet['user'].
Keywords and languages
whenHears accepts a keyword/rule string or an array of alternatives. Arrays are grouped with OR, so the language filter
applies to every alternative. Different filter methods chained together are combined with AND; the last callback is used.
PublicStream::create($bearerToken) ->whenHears(['php', 'laravel'], function (object $message) { // Process $message->data when present. }) ->setLocale('en') ->startListening();
This submits (php OR laravel) lang:en. Raw strings use X's rule syntax; consult the
rule-building guide for quoting and operators.
Posts by authors
whenTweets accepts an author handle or an array of alternative handles, without @. For example:
PublicStream::create($bearerToken) ->whenTweets(['spatie_be', 'freekmurze'], function (object $message) { // Matches either author, not both simultaneously. }) ->startListening();
UserStream is a convenience wrapper for a single from:handle post filter, not an activity stream:
use Spatie\TwitterStreamingApi\UserStream; UserStream::create('spatie_be', $bearerToken) ->onTweet(function (object $message) { // Public posts authored by spatie_be, not likes or follows. }) ->startListening();
Locations
whenFrom accepts one or more [west longitude, south latitude, east longitude, north latitude] boxes. Multiple boxes
are grouped with OR. X validates operator access and permitted box dimensions; a whole-world box is not promised.
PublicStream::create($bearerToken) ->whenFrom([[-74, 40, -73, 41]], function (object $message) { // Process matching geolocated posts. }) ->startListening();
Stream options
Pass a configured Felix\TwitterStream\Streams\FilteredStream to startListening() for fields, expansions, or a post limit:
use Felix\TwitterStream\Streams\FilteredStream; $options = (new FilteredStream()) ->fields(['tweet' => ['author_id']]) ->expansions('author_id') ->withTweetLimit(100); $publicStream->startListening($options);
Both factories also accept a configured Felix\TwitterStream\TwitterConnection instead of a token, for example to use
its setClient() transport configuration. A custom HTTP client must supply its own authentication headers.
Upgrading from 2.x
- PHP 8.0–8.4 and the 0.x streaming client are no longer supported. The client is now
redwebcreation/twitter-stream-api2.2+. - Pass only the Bearer Token (or a
TwitterConnection) toPublicStream::create(). Pass a handle and token toUserStream::create(). - Use object envelopes (
$message->data->text) instead of legacy tweet arrays or top-leveltext/userfields. - Rename
UserStream::onEvent()toonTweet(). The old likes/follows example was never implemented by the filtered-post path; no legacy event translation is provided. - Replace
RWC\TwitterStream\Setswith a configuredFelix\TwitterStream\Streams\FilteredStream. - Author arrays now use OR, bounding boxes use the 2.2 builder signature, and
setLocale()uses thelangoperator.
Testing and limitations
composer test composer test-lint composer test-coverage # Requires a coverage driver such as PCOV or Xdebug.
The suite exercises real rule generation, HTTP request bodies, stream parsing, payload delivery, limits, restart behavior,
and error propagation using a mocked HTTP transport. It makes no authenticated X calls and proves neither account
entitlement nor live delivery. The latest upstream client (2.2.1) currently emits a PHP implicit-nullability deprecation
from RuleManager::save() on PHP 8.5/8.6. The lowest-dependency resolution also emits a Guzzle promises class-name
deprecation on PHP 8.6. These remain visible in test output, not suppressed.
For lower-level functionality, see the upstream client.
Changelog
Please see CHANGELOG for more information what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Security
If you've found a bug regarding security please mail security@spatie.be instead of using the issue tracker.
Credits
About Spatie
Spatie is a webdesign agency based in Antwerp, Belgium. You'll find an overview of all our open source projects on our website.
License
The MIT License (MIT). Please see License File for more information.