texhub / instagram-graph-api
Full-featured Instagram Graph API SDK for any PHP framework with first-class Laravel support: OAuth & long-lived tokens, content publishing, stories, comments, direct messaging with buttons, user info and webhooks.
Requires
- php: ^8.2
- ext-curl: *
- ext-hash: *
- ext-json: *
Requires (Dev)
- illuminate/support: ^11.0 || ^12.0 || ^13.0
- phpunit/phpunit: ^11.0 || ^12.0
Suggests
- illuminate/support: Required to use the package inside a Laravel application (service provider, facade, config publishing).
README
🌐 English · Русский
A complete, framework-agnostic PHP SDK for the Instagram Graph API (Instagram API with Instagram Login) — OAuth & long-lived tokens, content publishing, stories, comments, direct messaging with buttons, user info and webhooks — with first-class Laravel support.
Everything you need to integrate Instagram, from connecting to every feature.
Reference: https://developers.facebook.com/docs/instagram-platform
✨ What's covered
| Area | Methods |
|---|---|
| OAuth & tokens | authorization URL, short-lived → long-lived (60-day) → refresh |
| User info | profile, avatar (profile picture), followers/media counts |
| Publishing | photos, reels/video, stories, carousels, publish, list, get |
| Comments | list, comment, reply, hide/unhide, enable/disable, delete |
| Messaging | text, image, buttons, quick replies, reactions, typing, conversations |
| Webhooks | verify challenge, verify X-Hub-Signature-256, parse comment & DM events |
| Escape hatch | ->http() to call any Graph endpoint |
📦 Installation
composer require texhub/instagram-graph-api
Requirements: PHP ≥ 8.2 with curl, json and hash.
🔌 1. Connect (OAuth & tokens)
use TexHub\InstagramGraphApi\Instagram; $ig = Instagram::make('APP_ID', 'APP_SECRET'); // a) Send the user to authorize: $url = $ig->oauth()->authorizationUrl( scopes: ['instagram_business_basic', 'instagram_business_content_publish', 'instagram_business_manage_comments', 'instagram_business_manage_messages'], state: 'csrf-token', redirectUri: 'https://shop.tj/instagram/callback', ); // redirect($url) // b) On the callback, exchange the ?code= for tokens: $short = $ig->oauth()->requestShortLivedToken($_GET['code'], 'https://shop.tj/instagram/callback'); $long = $ig->oauth()->exchangeForLongLivedToken($short->token); // valid ~60 days echo $long->token; echo $long->expiresAt(); // unix timestamp $long->expiresWithinDays(7); // refresh soon? // c) Refresh before it expires: $refreshed = $ig->oauth()->refreshLongLivedToken($long->token);
Then use the token for API calls:
$ig = Instagram::make('APP_ID', 'APP_SECRET', accessToken: $long->token, igUserId: '17841...'); // or, from an existing instance: $ig = $ig->withAccessToken($long->token);
👤 2. User info & avatar
$me = $ig->users()->me(); $me->get('username'); $me->get('followers_count'); $ig->users()->avatarUrl(); // profile picture URL $ig->users()->username();
📸 3. Publish content
// Photo (create container + publish in one call): $ig->media()->publishPhoto('https://cdn.shop.tj/photo.jpg', 'Новинка! 🔥'); // Reel: $ig->media()->publishReel('https://cdn.shop.tj/reel.mp4', 'Смотрите 👀'); // Story: $ig->media()->publishStory('https://cdn.shop.tj/story.jpg'); // Carousel (multi-image post): $a = $ig->media()->createCarouselItem('https://cdn/1.jpg')->id(); $b = $ig->media()->createCarouselItem('https://cdn/2.jpg')->id(); $carousel = $ig->media()->createCarousel([$a, $b], 'Подборка')->id(); $ig->media()->publish($carousel); // Read: $ig->media()->list(); $ig->media()->get($mediaId); $ig->media()->publishingLimit(); // daily quota usage
Media URLs must be publicly reachable — Instagram fetches them.
💬 4. Comments
$ig->comments()->forMedia($mediaId); // list comments $ig->comments()->commentOnMedia($mediaId, 'Спасибо за внимание!'); $ig->comments()->reply($commentId, 'Ответ на комментарий'); $ig->comments()->hide($commentId); // hide / unhide $ig->comments()->delete($commentId);
📨 5. Direct messaging (with buttons)
The recipient id is the IGSID you receive in messaging webhooks.
use TexHub\InstagramGraphApi\Builders\Button; $ig->messages()->sendText($igsid, 'Привет! Чем помочь?'); $ig->messages()->sendImage($igsid, 'https://cdn/promo.jpg'); // Buttons: $ig->messages()->sendButtons($igsid, 'Выберите действие:', [ Button::url('Открыть сайт', 'https://texhub.pro'), Button::postback('Связаться', 'CONTACT'), ]); // Quick replies: $ig->messages()->sendQuickReplies($igsid, 'Ваш выбор?', [ Button::quickReply('Да', 'YES'), Button::quickReply('Нет', 'NO'), ]); // Typing indicator / read receipts / reactions: $ig->messages()->senderAction($igsid, 'typing_on'); $ig->messages()->react($igsid, $messageId, 'love'); // Conversations & history: $ig->messages()->conversations(); $ig->messages()->messages($conversationId);
🔔 6. Webhooks
Verification (GET) — echo the challenge:
$challenge = $ig->webhooks()->verifyChallenge($_GET); if ($challenge !== null) { echo $challenge; exit; } // HTTP 200
Events (POST) — verify the signature, then parse:
$raw = file_get_contents('php://input'); $ig->webhooks()->assertValidSignature($raw, $_SERVER['HTTP_X_HUB_SIGNATURE_256'] ?? null); foreach ($ig->webhooks()->parse($raw) as $event) { if ($event->isMessage()) { $ig->messages()->sendText($event->senderId(), 'Получили: ' . $event->messageText()); } if ($event->isComment()) { // $event->get('id'), $event->get('text'), ... } } http_response_code(200);
🧯 Error handling
use TexHub\InstagramGraphApi\Exceptions\ApiException; try { $ig->media()->publishPhoto($url, $caption); } catch (ApiException $e) { $e->httpStatus; $e->errorCode; $e->errorType; $e->errorSubcode; $e->fbtraceId; $e->isTokenError(); // code 190 — token expired/invalid $e->isRateLimit(); }
🧩 Any endpoint (escape hatch)
$ig->http()->get('17841.../insights', ['metric' => 'impressions,reach']); $ig->http()->post($mediaId, ['comment_enabled' => 'false']);
🧩 Laravel
Auto-discovered. Publish config:
php artisan vendor:publish --tag=instagram-config
.env:
INSTAGRAM_APP_ID=... INSTAGRAM_APP_SECRET=... INSTAGRAM_ACCESS_TOKEN=long-lived-token INSTAGRAM_USER_ID=17841... INSTAGRAM_REDIRECT_URI=https://shop.tj/instagram/callback INSTAGRAM_WEBHOOK_VERIFY_TOKEN=your-verify-token INSTAGRAM_API_VERSION=v23.0
Facade:
use TexHub\InstagramGraphApi\Laravel\Instagram; Instagram::media()->publishPhoto($url, 'Привет из Laravel!'); Instagram::messages()->sendText($igsid, 'Ответ');
Webhook controller
public function verify(Request $request) { return response(Instagram::webhooks()->verifyChallenge($request->query()) ?? '', 200); } public function handle(Request $request) { Instagram::webhooks()->assertValidSignature( $request->getContent(), $request->header('X-Hub-Signature-256'), ); foreach (Instagram::webhooks()->parse($request->getContent()) as $event) { // ... } return response('', 200); }
🏢 Multi-tenant / SaaS
Many customers can connect their own Instagram accounts through one Meta app. Each tenant authorizes via OAuth and gets their own long-lived token; one webhook URL serves everyone.
// Onboarding: each tenant runs the OAuth flow → store their long-lived token. $long = $ig->oauth()->exchangeForLongLivedToken($short->token); // → save {token, user_id} for this tenant // Act as any tenant — bind a client to their stored token: $ig->withAccessToken($tenant->ig_token)->media()->publishPhoto($url, $caption); // One webhook for everyone — route by the account that received it: foreach ($ig->webhooks()->parse($raw) as $event) { $tenant = Tenant::where('ig_account_id', $event->accountId())->first(); if ($event->isMessage()) { /* $event->senderId(), $event->messageText() */ } }
$event->accountId() (the connected account / entry.id) and $event->recipientId() are the tenant routing keys. Signatures are verified with your single app secret.
🧪 Testing
use TexHub\InstagramGraphApi\Instagram; use TexHub\InstagramGraphApi\Config; use TexHub\InstagramGraphApi\Tests\Support\FakeTransport; $t = (new FakeTransport())->push(['id' => 'MEDIA_1']); $ig = new Instagram(new Config('APP', 'SECRET', accessToken: 'TOKEN'), $t); // assert on $t->history / $t->lastUrl()
composer install
composer test
📚 Architecture
src/
├── Instagram.php # entry — oauth()/webhooks()/users()/media()/comments()/messages()
├── Config.php # immutable configuration
├── Http/ # Transport, CurlTransport, HttpClient, RawResponse
├── OAuth/ # OAuthClient, AccessToken
├── Webhook/ # WebhookHandler (challenge + signature + parse), WebhookEvent
├── Resources/ # Users, Media, Comments, Messages
├── Builders/ # Message, Button (buttons & quick replies)
├── Responses/ # Response (ArrayAccess), ListResponse
├── Exceptions/ # ApiException, TransportException, …
└── Laravel/ # ServiceProvider + Facade
License
MIT © TexHub Pro — built by Mahmudi Shodmehr.