twexapi / x-api-scraper
Twitter API alternative PHP SDK for tweet search, follower scraping, timelines, DMs, communities, lists, trending, and X automation. Not affiliated with X Corp.
Requires
- php: >=8.2
- brick/date-time: >=0.7.0
- brick/math: >=0.12.1
- galbar/jsonpath: >=3.0
- guzzlehttp/guzzle: ^7.15.2
- phpdocumentor/type-resolver: >=1.8
- speakeasy/serializer: ^4.0.3
Requires (Dev)
- laravel/pint: 1.29.0
- phpstan/phpstan: 2.1.44
- phpunit/phpunit: ^11.5.50 || ^12.5.8 || >=13.0.0
- roave/security-advisories: dev-latest
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Use the TwexAPI PHP SDK to search tweets, scrape Twitter followers, and read X profiles, timelines, replies, and threads. Search communities, fetch lists, articles, hashtags, cashtags, and global trending tweets with typed request objects. It is a Twitter API alternative for Composer apps and agents.
REST API | TypeScript SDK | Python SDK | Ruby SDK | Java SDK | Kotlin SDK | C# SDK | Dashboard
Speakeasy generates this SDK.
Common Twitter & X tasks
| Task | REST Route | PHP client |
|---|---|---|
| Search tweets without the X API | POST /twitter/advanced_search/page |
$sdk->search->advanced(...) |
| Search hashtags or cashtags | POST /twitter/hashtags, POST /twitter/cashtags |
$sdk->search->hashtags(), $sdk->search->cashtags() |
| Read an X profile | GET /twitter/{screen_name}/about |
$sdk->users->getAbout(...) |
| Read a profile timeline | GET /twitter/{screen_name}/timeline/page |
$sdk->timelines->userPage(...) |
| Scrape Twitter followers | POST /v3/twitter/users/followers |
$sdk->users->followers->list(...) |
| Scrape following accounts | POST /v3/twitter/users/following |
$sdk->users->following->list(...) |
| Read tweet replies | POST /twitter/tweets/{tweet_id}/replies/page |
$sdk->tweets->replies->page(...) |
| Read a tweet thread | POST /twitter/tweets/thread_by_id |
$sdk->tweets->thread(...) |
| Search communities | POST /twitter/community/search |
$sdk->communities->search(...) |
| Get global trending tweets | GET /twitter/global-trending/tweets |
$sdk->trending->tweets(...) |
Package & registry trust
- Package:
twexapi/x-api-scraper - Source: twexapi-dev/x-api-scraper-php
- Docs: docs.twexapi.io
- License: MIT
- Dashboard: twexapi.io/dashboard
Summary
X API Scraper: Speakeasy-ready OpenAPI document for the x-api-scraper TypeScript SDK.
The SDK wraps TwexAPI's X/Twitter API surface with bearer-token authentication. Profile mutation, legacy-only operations, and non-v3 follower/following endpoints remain excluded.
Not affiliated with X Corp.
Table of Contents
SDK Installation
composer require twexapi/x-api-scraper:^0.1.0
Packagist is not published yet. Install from GitHub until it is:
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/twexapi-dev/x-api-scraper-php.git"
}
],
"require": {
"twexapi/x-api-scraper": "0.1.0"
}
}
Then run:
composer update
SDK Example Usage
Example
declare(strict_types=1); require 'vendor/autoload.php'; use XapiScraper\XapiScraper; use XapiScraper\Models\Components; $sdk = XapiScraper::builder() ->setSecurity(getenv('X_API_SCRAPER_KEY')) ->build(); $request = new Components\AdvancedSearchCursorQuery( searchTerms: ['from:elonmusk'], sortBy: 'Latest', nextCursor: '', ); $response = $sdk->search->advanced(request: $request); var_dump($response);
Get an API key from the TwexAPI dashboard. Pass it to setSecurity(), or set X_API_SCRAPER_KEY.
Keep API keys out of source code, URLs, and logs.
Authentication
Per-Client Security Schemes
This SDK supports the following security scheme globally:
| Name | Type | Scheme |
|---|---|---|
bearerAuth |
http | HTTP Bearer |
To authenticate with the API the bearerAuth parameter must be set when initializing the SDK. For example:
declare(strict_types=1); require 'vendor/autoload.php'; use XapiScraper; use XapiScraper\Models\Components; $sdk = XapiScraper\XapiScraper::builder() ->setSecurity( '<YOUR_BEARER_TOKEN_HERE>' ) ->build(); $request = new Components\AdvancedSearchCursorQuery( searchTerms: [ '<value 1>', '<value 2>', '<value 3>', ], sortBy: '<value>', nextCursor: '', ); $response = $sdk->search->advanced( request: $request ); if ($response->advancedSearchCursorResponse !== null) { // handle response }
Available Resources and Operations
Available methods
Account
- balance - Get Balance
Analysis
- sentiment - Sentiment Analysis
Articles
Communities
- members - Get Community Members
- membersPage - Get Community Members by Page
- tweets - Get Community Tweets
- tweetsPage - Get Community Tweets by Page
- search - Search Community
- get - Get Community
- searchTweets - Search Community Tweets
Dm
- status - Check DM Permissions
- history - Get DM History
- media - Get DM Media
- conversations - Get Conversations
Lists
- tweets - Get List Tweets
- tweetsPage - Get List Tweets by Page
- subscribers - Get List Subscribers
- members - Get List Members
- membersPage - Get List Members by Page
- search - Search List
Search
Timelines
- tweetsAndReplies - Get All Tweets and Replies by User
- userPage - Get User Timeline by Page
- user - Get User Timeline and Fill Count
- tweetsAndRepliesPage - Get All Tweets and Replies by User by Page
Trending
- countries - List Global Trend Countries
- topics - List Global Trend Topics
- contents - List Global Trend Content Tags
- tweets - Get Global Trending Tweets
- byCountry - Get Trending Topics
Tweets
- detail - Get Tweet Detail
- thread - Get Tweet Thread by ID
- lookup - Batch Get Tweets by ID
- similar - Get Similar Tweets
Tweets.Actions
- like - Like a Tweet
- unlike - Unlike a Tweet
- retweet - Retweet a Tweet
- unretweet - Delete Retweet
- createThread - Create a Tweet Thread
- create - Create a Tweet or Reply
- quote - Create a Quote Tweet
- bookmark - Bookmark a Tweet
- unbookmark - Delete Bookmark
- deleteBatch - Delete One or More Tweets
Tweets.Engagement
- retweeters - Get Retweeters
- retweetersPage - Get Retweeters by Page
- quotes - Get Quote Tweets
- quotesPage - Get Quote Tweets by Page
Tweets.Replies
- page - Get Replies by Page
Users
- getByUsernames - Get Multiple Users by Usernames
- getByIds - Users Details by ID
- verifyAccount - Verify Account Status
- getStatuses - Batch Get User account status
- search - Search User
- follow - Follow User
- unfollow - Unfollow User
- getAccountBased - Get Twitter Account Based in
- getAbout - Get Twitter User About by Screen Name
Users.Followers
Users.Following
- list - Get Following (v3)
Retries
Some of the endpoints in this SDK support retries. If you use the SDK without any configuration, it will fall back to the default retry strategy provided by the API. However, the default retry strategy can be overridden on a per-operation basis, or across the entire SDK.
To change the default retry strategy for a single API call, simply provide an Options object built with a RetryConfig object to the call:
declare(strict_types=1); require 'vendor/autoload.php'; use XapiScraper; use XapiScraper\Models\Components; use XapiScraper\Utils\Retry; $sdk = XapiScraper\XapiScraper::builder() ->setSecurity( '<YOUR_BEARER_TOKEN_HERE>' ) ->build(); $request = new Components\AdvancedSearchCursorQuery( searchTerms: [ '<value 1>', '<value 2>', '<value 3>', ], sortBy: '<value>', nextCursor: '', ); $response = $sdk->search->advanced( request: $request, options: Utils\Options->builder()->setRetryConfig( new Retry\RetryConfigBackoff( initialInterval: 1, maxInterval: 50, exponent: 1.1, maxElapsedTime: 100, retryConnectionErrors: false, ))->build() ); if ($response->advancedSearchCursorResponse !== null) { // handle response }
If you'd like to override the default retry strategy for all operations that support retries, you can pass a RetryConfig object to the SDKBuilder->setRetryConfig function when initializing the SDK:
declare(strict_types=1); require 'vendor/autoload.php'; use XapiScraper; use XapiScraper\Models\Components; use XapiScraper\Utils\Retry; $sdk = XapiScraper\XapiScraper::builder() ->setRetryConfig( new Retry\RetryConfigBackoff( initialInterval: 1, maxInterval: 50, exponent: 1.1, maxElapsedTime: 100, retryConnectionErrors: false, ) ) ->setSecurity( '<YOUR_BEARER_TOKEN_HERE>' ) ->build(); $request = new Components\AdvancedSearchCursorQuery( searchTerms: [ '<value 1>', '<value 2>', '<value 3>', ], sortBy: '<value>', nextCursor: '', ); $response = $sdk->search->advanced( request: $request ); if ($response->advancedSearchCursorResponse !== null) { // handle response }
Error Handling
Handling errors in this SDK should largely match your expectations. All operations return a response object or throw an exception.
By default an API error will raise a Errors\APIException exception, which has the following properties:
| Property | Type | Description |
|---|---|---|
$message |
string | The error message |
$statusCode |
int | The HTTP status code |
$rawResponse |
?\Psr\Http\Message\ResponseInterface | The raw HTTP response |
$body |
string | The response content |
When custom error responses are specified for an operation, the SDK may also throw their associated exception. You can refer to respective Errors tables in SDK docs for more details on possible exception types for each operation. For example, the advanced method throws the following exceptions:
| Error Type | Status Code | Content Type |
|---|---|---|
| Errors\HTTPValidationError | 422 | application/json |
| Errors\APIException | 4XX, 5XX | */* |
Example
declare(strict_types=1); require 'vendor/autoload.php'; use XapiScraper; use XapiScraper\Models\Components; use XapiScraper\Models\Errors; $sdk = XapiScraper\XapiScraper::builder() ->setSecurity( '<YOUR_BEARER_TOKEN_HERE>' ) ->build(); try { $request = new Components\AdvancedSearchCursorQuery( searchTerms: [ '<value 1>', '<value 2>', '<value 3>', ], sortBy: '<value>', nextCursor: '', ); $response = $sdk->search->advanced( request: $request ); if ($response->advancedSearchCursorResponse !== null) { // handle response } } catch (Errors\HTTPValidationErrorThrowable $e) { // handle $e->$container data throw $e; } catch (Errors\APIException $e) { // handle default exception throw $e; }
Server Selection
Select Server by Name
You can override the default server globally using the setServer(string $serverName) builder method when initializing the SDK client instance. The selected server will then be used as the default on the operations that use it. This table lists the names associated with the available servers:
| Name | Server | Description |
|---|---|---|
production |
https://api.twexapi.io |
TwexAPI production API |
Example
declare(strict_types=1); require 'vendor/autoload.php'; use XapiScraper; use XapiScraper\Models\Components; $sdk = XapiScraper\XapiScraper::builder() ->setServer('production') ->setSecurity( '<YOUR_BEARER_TOKEN_HERE>' ) ->build(); $request = new Components\AdvancedSearchCursorQuery( searchTerms: [ '<value 1>', '<value 2>', '<value 3>', ], sortBy: '<value>', nextCursor: '', ); $response = $sdk->search->advanced( request: $request ); if ($response->advancedSearchCursorResponse !== null) { // handle response }
Override Server URL Per-Client
The default server can also be overridden globally using the setServerUrl(string $serverUrl) builder method when initializing the SDK client instance. For example:
declare(strict_types=1); require 'vendor/autoload.php'; use XapiScraper; use XapiScraper\Models\Components; $sdk = XapiScraper\XapiScraper::builder() ->setServerURL('https://api.twexapi.io') ->setSecurity( '<YOUR_BEARER_TOKEN_HERE>' ) ->build(); $request = new Components\AdvancedSearchCursorQuery( searchTerms: [ '<value 1>', '<value 2>', '<value 3>', ], sortBy: '<value>', nextCursor: '', ); $response = $sdk->search->advanced( request: $request ); if ($response->advancedSearchCursorResponse !== null) { // handle response }
Development
Maturity
This SDK is in beta, and there may be breaking changes between versions without a major version update. Therefore, we recommend pinning usage to a specific package version. This way, you can install the same version each time without breaking changes unless you are intentionally looking for the latest version.
Contributions
While we value open-source contributions to this SDK, this library is generated programmatically. Any manual changes added to internal files will be overwritten on the next generation. We look forward to hearing your feedback. Feel free to open a PR or an issue with a proof of concept and we'll do our best to include it in a future release.