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

Twitteroauth Laravel Package

kertz/twitteroauth

PHP Twitter OAuth library for authenticating with Twitter and making API requests via OAuth 1.0a. Provides a simple client for posting tweets, reading timelines, and accessing REST endpoints, with support for media uploads and error handling. Suitable for server-side integrations.

View on GitHub
Deep Wiki
Context7

Getting Started

kertz/twitteroauth is a maintained fork of Abraham Williams' OAuth 1.0a client. It is a plain Composer library — no service provider, no facade, no config file — so the first job in a Laravel app is wrapping it.

  1. Install:
composer require kertz/twitteroauth
  1. Add credentials to config/services.php (env-backed, never hard-coded):
'twitter' => [
    'consumer_key'    => env('TWITTER_CONSUMER_KEY'),
    'consumer_secret' => env('TWITTER_CONSUMER_SECRET'),
    'access_token'    => env('TWITTER_ACCESS_TOKEN'),
    'access_token_secret' => env('TWITTER_ACCESS_TOKEN_SECRET'),
],

Get these from the Twitter/X developer portal. The app's access level must be Read and Write, and you must regenerate the access token/secret after changing that setting — old tokens keep their old permissions.

  1. Look first at Abraham\TwitterOAuth\TwitterOAuth: get(), post(), upload(), getLastHttpCode(), getResponse(). That is 90% of the surface area.

  2. First use case — verify credentials and post a tweet:

use Abraham\TwitterOAuth\TwitterOAuth;

$conn = new TwitterOAuth(
    config('services.twitter.consumer_key'),
    config('services.twitter.consumer_secret'),
    config('services.twitter.access_token'),
    config('services.twitter.access_token_secret'),
);

$me = $conn->get('account/verify_credentials');

if ($conn->getLastHttpCode() !== 200) {
    throw new RuntimeException("Twitter auth failed: {$conn->getLastHttpCode()}");
}

$conn->post('statuses/update', ['status' => 'Hello from Laravel']);

The critical habit: always check getLastHttpCode(). The library does not throw on API errors (only on transport failures), so a failed post returns a decoded error object silently.

Implementation Patterns

Bind a singleton in a service provider. For single-account apps, one connection per request is enough; resolve it from the container so tests can swap it.

$this->app->singleton(TwitterOAuth::class, fn () => new TwitterOAuth(
    config('services.twitter.consumer_key'),
    config('services.twitter.consumer_secret'),
    config('services.twitter.access_token'),
    config('services.twitter.access_token_secret'),
));

Wrap it in a domain service, not raw calls in controllers. Typed methods (tweet(string $status): string, uploadMedia(string $path)) keep API paths in one place and make v1.1 → v2 migration a single-file change later.

Post in a queued job. Twitter calls are network-bound and rate-limited; putting them in ShouldQueue jobs with $tries = 3, $backoff = 60 gives you retries without blocking the request. The kertz fork also has setRetries() with backoff for 5xx responses — useful, but it doesn't cover 429 rate limits.

Throttle explicitly. The v1.1 limit for most write endpoints is 15 requests / 15 minutes per user. Use Cache::lock() or RateLimiter::for('twitter', ...) before dispatching a burst, otherwise you'll get 429s that look like auth problems.

Cache read endpoints. account/verify_credentials, follower counts, and similar GETs are cheap to cache:

$profile = Cache::remember('twitter.me', now()->addMinutes(10), fn () =>
    app(TwitterOAuth::class)->get('account/verify_credentials')
);

Media upload is a two-step flow. upload() uses multipart, then you attach the returned media_id_string in a normal post():

$media = $conn->upload('media/upload', ['media' => storage_path("app/{$path}")]);
$conn->post('statuses/update', [
    'status'    => $text,
    'media_ids' => $media->media_id_string,
]);

Multi-account support breaks the singleton. Bind a factory instead (TwitterClientFactory::for($account) calling setOauthToken($token, $secret)), and pass the account into the job payload.

Testing: extract an interface or use Laravel's Http fakes is not possible here (the library owns its HTTP layer). The practical approach is to bind a fake subclass of TwitterOAuth that overrides get()/post() and records calls, or bind a wrapper interface and fake that.

Gotchas and Tips

  • It is OAuth 1.0a and v1.1 only. Endpoints under /2/ (the modern API) are not supported by the request signing this library implements. Any new integration should seriously consider whether the library targets the endpoints you're allowed to use at all.
  • Access tiers are the real blocker. Modern X API free/basic tiers severely restrict posting and reads. Confirm your app's tier before writing code — a perfectly correct implementation returns 403 otherwise.
  • getLastHttpCode() returning 0 means the request never reached Twitter (DNS, TLS, timeout). Don't confuse it with a 4xx. Wrap in try/catch for Abraham\TwitterOAuth\TwitterOAuthException too.
  • 403 with code 187 is "duplicate status" — Twitter silently rejects identical tweets. 401 usually means a bad/regenerated token or wrong access level. 429 is rate limiting; read x-rate-limit-reset from getLastXHeaders() before retrying.
  • setDecodeJsonAsArray(true) makes responses arrays instead of stdClass — pick one style and set it in your service constructor so downstream code doesn't fork.
  • setTimeouts($connect, $total) exists; defaults are generous. Set something like (5, 10) so a hung request doesn't hold a queue worker.
  • Character counting is Twitter's, not strlen(). 280 "weighted" characters; URLs count as 23, CJK counts double. Use a library like twitter-text-php if you validate before posting, or you'll pass validation and still get a 403.
  • Never log the tokens. The library itself doesn't log payloads, but Laravel's HTTP/exception logging around your wrapper easily can — scrub the consumer secret and access token from any report() context.
  • Extension points are limited to subclassing TwitterOAuth and calling setApiVersion(), setOauthToken(), setRetries(), setDecodeJsonAsArray(), setTimeouts(). There is no middleware/hook system; if you need request signing changes or v2 support, you're better off with a different client.
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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core