Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Twitter Api Php Laravel Package

j7mbo/twitter-api-php

A lightweight, dependency-free PHP wrapper for the Twitter API. It handles OAuth signing and lets you send GET and POST requests through cURL using a single include file, making it easy to drop into any PHP project for quick Twitter integration.

Deep Wiki
Context7

Getting Started

This is a single-class wrapper (TwitterAPIExchange) around Twitter API v1.1. Installation is usually either composer require j7mbo/twitter-api-php if your Composer setup still resolves it, or simply vendoring TwitterAPIExchange.php into your project. Because the package is archived, vendoring or pinning a fork is often the safer day-to-day choice.

Start by reading the class itself — it is small and all behavior is in buildOauth(), performRequest(), and the setters. Then create a Twitter app in the developer portal, generate an access token/secret with Read and Write permissions, and put all four credentials in .env:

TWITTER_CONSUMER_KEY=
TWITTER_CONSUMER_SECRET=
TWITTER_ACCESS_TOKEN=
TWITTER_ACCESS_TOKEN_SECRET=

Minimal first call — verify credentials:

require 'TwitterAPIExchange.php';

$twitter = new TwitterAPIExchange([
    'oauth_access_token'        => env('TWITTER_ACCESS_TOKEN'),
    'oauth_access_token_secret' => env('TWITTER_ACCESS_TOKEN_SECRET'),
    'consumer_key'              => env('TWITTER_CONSUMER_KEY'),
    'consumer_secret'           => env('TWITTER_CONSUMER_SECRET'),
]);

$url = 'https://api.twitter.com/1.1/account/verify_credentials.json';
$response = $twitter->buildOauth($url, 'GET')->performRequest();

$user = json_decode($response, true);

For a GET with parameters, set the query string before signing:

$url = 'https://api.twitter.com/1.1/statuses/user_timeline.json';
$getfield = '?' . http_build_query(['screen_name' => 'twitter', 'count' => 5]);

$response = $twitter
    ->setGetfield($getfield)
    ->buildOauth($url, 'GET')
    ->performRequest();

Your first real use case is almost always reading a timeline, looking up a user, or posting a status. Confirm credentials and app permissions with a standalone script before wiring anything into Laravel.

Implementation Patterns

Wrap it behind a service. Do not scatter new TwitterAPIExchange(...) through controllers and jobs. Create a small client class that owns configuration, request construction, JSON decoding, and error normalization. Bind it in the container and inject it.

final class TwitterClient
{
    public function __construct(private array $config) {}

    public function get(string $endpoint, array $query = []): array
    {
        $url = 'https://api.twitter.com/1.1/' . ltrim($endpoint, '/');
        $twitter = new TwitterAPIExchange($this->config);

        if ($query !== []) {
            $twitter->setGetfield('?' . http_build_query($query));
        }

        return $this->decode(
            $twitter->buildOauth($url, 'GET')->performRequest()
        );
    }

    public function post(string $endpoint, array $body = []): array
    {
        $url = 'https://api.twitter.com/1.1/' . ltrim($endpoint, '/');
        $twitter = new TwitterAPIExchange($this->config);
        $twitter->setPostfields($body);

        return $this->decode(
            $twitter->buildOauth($url, 'POST')->performRequest()
        );
    }

    private function decode(string|false $raw): array
    {
        if ($raw === false || $raw === '') {
            throw new RuntimeException('Twitter transport failure');
        }

        $data = json_decode($raw, true);

        if (!is_array($data)) {
            throw new RuntimeException('Invalid Twitter response: ' . $raw);
        }

        if (isset($data['errors'])) {
            throw new RuntimeException('Twitter API error: ' . json_encode($data['errors']));
        }

        return $data;
    }
}

Instantiate per request, not as a singleton. The object accumulates request state: getfield, postfields, URL, and method persist between calls. A singleton can silently reuse a previous query string or POST body. Either construct a fresh instance per call or carefully reset all fields.

Set fields before buildOauth(). buildOauth() computes the OAuth signature from the accumulated request state, and performRequest() later appends the GET query string. Calling setGetfield() after buildOauth() produces a signature for a different URL and usually results in a 401.

Use config, not hard-coded credentials. Add a twitter block to config/services.php, read from .env, and pass that array into your wrapper. Never log the access token, secret, or generated Authorization header.

Cache reads aggressively. Twitter rate limits are low and per-user. Wrap timeline/user lookups in Cache::remember() with a short TTL, and queue writes rather than posting inline from web requests.

Posting a tweet:

$client->post('statuses/update.json', [
    'status' => 'Deployed via Laravel',
]);

For simple media uploads, base64-encode the file and post to upload.twitter.com/1.1/media/upload.json with media_data, then attach the returned media_id_string to a subsequent statuses/update.json call. Videos and large images require chunked upload (INIT/APPEND/FINALIZE), which this wrapper can theoretically do but has no helpers for.

Testing. The package performs real cURL calls and exposes no injectable HTTP transport. Test against your own TwitterClient interface — bind a fake implementation in the container — rather than trying to mock the package directly.

Consider migration paths. For new work, a maintained OAuth 1.0a client such as abraham/twitteroauth is usually a better long-term dependency. If you need Twitter API v2, note that this package is built around v1.1 conventions; v2 uses different endpoints and JSON body semantics. Keeping your own wrapper makes the eventual replacement much cheaper.

Gotchas and Tips

  • Archived and stale. Last release was 2017, and Twitter/X API access has changed dramatically since then. Many v1.1 endpoints now require paid or elevated access. Verify your app's current tier before building on this.
  • No status code or headers exposed. performRequest() returns only the response body. A 401, 429, or 500 looks like ordinary JSON or an empty string. Parse errors from the body, and for rate-limit work you must either fork the class or set CURLOPT_HEADER => true via setCurlOptions() and parse headers out of the response.
  • cURL failures return false with no error message. Always check for false and an empty string before json_decode(). If you need curl_error(), you will have to modify the class, because it closes the handle internally.
  • OAuth nonce collisions. Older versions generate the nonce from time(). Loops that fire multiple requests in the same second can produce duplicate nonces and intermittent 401s. Add a small delay, or fork the class to use bin2hex(random_bytes(16)).
  • GET parameters must be in the signature. Pass them through setGetfield() before buildOauth(), not by appending to the URL manually. Passing a URL with a query string directly to buildOauth() can cause signature mismatches.
  • POST bodies are form/multipart encoded by default. When setPostfields() receives an array, cURL sends multipart form data. Twitter v1.1 generally accepts this, but if you need JSON you must pass a JSON string and set the content type through setCurlOptions(). Nested arrays will not encode usefully.
  • Default timeouts are short. The class defaults to roughly 5 seconds. Media uploads and slow connections need setTimeouts() if available in your version, or CURLOPT_TIMEOUT / CURLOPT_CONNECTTIMEOUT through setCurlOptions().
  • SSL verification and CA bundles. Some copies and forks disable peer verification. Do not ship that. If you hit certificate errors, point cURL at a valid CA bundle with CURLOPT_CAINFO via setCurlOptions() instead of turning verification off.
  • Token permissions. If you change your Twitter app from Read to Read and Write after generating tokens, existing access tokens keep their old permissions. Regenerate them.
  • Signed URL must match the endpoint exactly. Use the documented https://api.twitter.com/1.1/... URLs. A stray slash, http, or unexpected query ordering can break the signature.
  • Debug outside Laravel first. If you get 401s, run a standalone PHP script with account/verify_credentials.json, dump the raw body, and check server clock skew. OAuth 1.0a signatures are time-sensitive.
  • Extension points are limited. Public $url and $requestMethod exist, but most state is private. Subclassing for retries, logging, or header parsing is awkward; forking the single file is often more practical than fighting the API.
  • Lock the dependency. If you use Composer, commit composer.lock and consider a VCS fork. If Packagist resolution changes or disappears, the archived package can become an install-time liability.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle