mkhab7 / php-instagram-graph
A modern PHP 8.4 SDK for Instagram Graph API with comprehensive features and clean architecture
v1.0.0
2025-08-10 16:46 UTC
Requires
- php: ^8.4
- guzzlehttp/guzzle: ^7.8
- nesbot/carbon: ^3.0
- psr/http-client: ^1.0
- psr/http-message: ^2.0
- psr/log: ^3.0
- symfony/validator: ^7.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.64
- mockery/mockery: ^1.6
- pestphp/pest: ^3.0
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0
- symfony/var-dumper: ^7.0
README
A modern, comprehensive PHP 8.4+ SDK for the Instagram Graph API with clean architecture, full type safety, and extensive feature coverage.
โจ Features
- ๐ Modern PHP 8.4 - Leverages latest PHP features: typed properties, attributes, enums, match expressions, named arguments
- ๐๏ธ Clean Architecture - SOLID principles, dependency injection, service-oriented design
- ๐ Complete Authentication - OAuth 2.0 flow, token management, automatic refresh
- ๐ Full API Coverage - Users, Media, Comments, Messaging, Insights, Webhooks
- ๐ก๏ธ Type Safety - Comprehensive DTOs with strict typing and validation
- โก HTTP Client Abstraction - Robust error handling, rate limiting, retry logic
- ๐งช Fully Tested - Comprehensive test suite with Pest PHP
- ๐ Rich Documentation - Detailed examples and API reference
- ๐ Developer Friendly - Intuitive API design with IDE autocompletion
๐ Requirements
- PHP 8.4 or higher
- Composer
- Instagram Business or Creator Account
- Facebook App with Instagram API access
๐ฆ Installation
Install via Composer:
composer require mkhab7/php-instagram-graph
โ๏ธ Configuration
1. Create Facebook App
- Go to Facebook Developers
- Create a new app with Business type
- Add Instagram API product
- Configure OAuth redirect URIs
2. Environment Setup
Create a .env
file or set environment variables:
INSTAGRAM_APP_ID=your_app_id INSTAGRAM_APP_SECRET=your_app_secret INSTAGRAM_REDIRECT_URI=https://yourapp.com/callback
๐ Quick Start
Authentication Flow
<?php use Mkhab7\InstagramGraph\InstagramGraph; use Mkhab7\InstagramGraph\Auth\AccessToken; // Initialize client for authentication $instagram = InstagramGraph::forAuthentication( appId: $_ENV['INSTAGRAM_APP_ID'], appSecret: $_ENV['INSTAGRAM_APP_SECRET'], redirectUri: $_ENV['INSTAGRAM_REDIRECT_URI'] ); // Step 1: Generate authorization URL $authUrl = $instagram->auth()->getAuthorizationUrl( scopes: ['instagram_business_basic', 'instagram_business_manage_messages'], state: 'random_state_string', forceReauth: true ); // Redirect user to $authUrl // Step 2: Handle callback and exchange code for token $code = $_GET['code']; // From callback $accessToken = $instagram->auth()->completeOAuthFlow($code); // Step 3: Use authenticated client $authenticatedInstagram = InstagramGraph::withAccessToken( appId: $_ENV['INSTAGRAM_APP_ID'], appSecret: $_ENV['INSTAGRAM_APP_SECRET'], redirectUri: $_ENV['INSTAGRAM_REDIRECT_URI'], accessToken: $accessToken );
Basic Usage
// Get current user information $user = $authenticatedInstagram->users()->me(); echo "Welcome {$user->getDisplayName()}!\n"; echo "Account Type: {$user->accountType->getLabel()}\n"; echo "Followers: {$user->followersCount}\n"; echo "Media Count: {$user->mediaCount}\n"; // Get user's media $mediaResponse = $authenticatedInstagram->users()->getUserMedia(limit: 10); foreach ($mediaResponse['data'] as $mediaData) { $media = \Mkhab7\InstagramGraph\Data\Media::fromArray($mediaData); echo "Media: {$media->id}\n"; echo "Type: {$media->mediaType->getLabel()}\n"; echo "Likes: {$media->likesCount}\n"; echo "Comments: {$media->commentsCount}\n"; echo "---\n"; }
๐ Comprehensive Usage Guide
๐ค User Management
$userService = $instagram->users(); // Get current user with custom fields $user = $userService->me([ 'id', 'username', 'account_type', 'name', 'biography', 'website', 'followers_count', 'media_count' ]); // Get another user by ID $otherUser = $userService->getUser('1234567890'); // Business discovery (public account data) $publicData = $userService->getBusinessDiscovery('username'); // Check if user exists if ($userService->userExists('1234567890')) { echo "User exists!\n"; } // Get user insights (business accounts only) $insights = $userService->getAccountInsights( metrics: ['impressions', 'reach', 'profile_views'], period: 'day', since: new DateTime('-30 days'), until: new DateTime('now') );
๐ธ Media Operations
$mediaService = $instagram->media(); // Get media by ID $media = $mediaService->getMedia('media_id_here'); echo "Caption: {$media->caption}\n"; echo "Type: {$media->mediaType->getLabel()}\n"; echo "Posted: {$media->timestamp->format('Y-m-d H:i:s')}\n"; // Get media insights $insights = $mediaService->getMediaInsights( mediaId: 'media_id_here', metrics: ['impressions', 'reach', 'engagement', 'saves'] ); // Create media container for publishing $container = $mediaService->createMediaContainer( imageUrl: 'https://example.com/image.jpg', caption: 'Check out this amazing photo! #photography #art', userTags: [ ['user_id' => '1234567890', 'x' => 0.5, 'y' => 0.3] ] ); // Publish the media $published = $mediaService->publishMedia($container['id']); // Create carousel post $childContainers = [ $mediaService->createMediaContainer( imageUrl: 'https://example.com/image1.jpg', isCarouselItem: true )['id'], $mediaService->createMediaContainer( imageUrl: 'https://example.com/image2.jpg', isCarouselItem: true )['id'] ]; $carouselContainer = $mediaService->createCarouselContainer( children: $childContainers, caption: 'Swipe to see more! ๐' ); $publishedCarousel = $mediaService->publishMedia($carouselContainer['id']); // Search hashtags $hashtags = $mediaService->searchHashtags('travel', limit: 10); // Get hashtag top media $topMedia = $mediaService->getHashtagTopMedia('hashtag_id_here');
๐ฌ Comment Management
$commentService = $instagram->comments(); // Get media comments $comments = $commentService->getMediaComments( mediaId: 'media_id_here', fields: ['id', 'text', 'timestamp', 'username', 'like_count', 'replies'], limit: 50 ); // Create a comment $newComment = $commentService->createComment( mediaId: 'media_id_here', message: 'Great post! ๐' ); // Reply to a comment $reply = $commentService->replyToComment( commentId: 'comment_id_here', message: 'Thanks for the feedback!' ); // Moderate comments $moderated = $commentService->moderateComments( commentIds: ['comment1', 'comment2', 'comment3'], action: 'hide' ); // Get trending comments (most liked) $trending = $commentService->getTrendingComments( mediaId: 'media_id_here', minLikes: 5 ); // Filter comments by keyword $filtered = $commentService->filterCommentsByKeyword( mediaId: 'media_id_here', keyword: 'awesome' ); // Get comment engagement stats $stats = $commentService->getCommentEngagementStats('media_id_here'); echo "Total Comments: {$stats['total_comments']}\n"; echo "Total Likes: {$stats['total_likes']}\n"; echo "Average Likes per Comment: {$stats['average_likes_per_comment']}\n";
๐ฌ Direct Messaging
$messagingService = $instagram->messaging(); // Send text message $message = $messagingService->sendTextMessage( recipientId: 'user_id_here', text: 'Hello! Thanks for reaching out.' ); // Send image message $imageMessage = $messagingService->sendImageMessage( recipientId: 'user_id_here', imageUrl: 'https://example.com/image.jpg' ); // Send quick reply message $quickReply = $messagingService->sendQuickReplyMessage( recipientId: 'user_id_here', text: 'How can we help you today?', quickReplies: [ [ 'content_type' => 'text', 'title' => 'Support', 'payload' => 'support' ], [ 'content_type' => 'text', 'title' => 'Sales', 'payload' => 'sales' ] ] ); // Send typing indicator $messagingService->sendTypingIndicator('user_id_here', 'typing_on'); // Get conversations $conversations = $messagingService->getConversations(limit: 20); // Get conversation messages $messages = $messagingService->getConversationMessages( conversationId: 'conversation_id_here', limit: 50 ); // Mark message as seen $messagingService->markMessageSeen('user_id_here');
๐ Analytics & Insights
$insightsService = $instagram->insights(); // Get comprehensive engagement summary $summary = $insightsService->getEngagementSummary( since: new DateTime('-30 days'), until: new DateTime('now') ); echo "Total Impressions: {$summary['total_impressions']}\n"; echo "Total Reach: {$summary['total_reach']}\n"; echo "Engagement Rate: {$summary['engagement_rate']}%\n"; // Get user insights with multiple metrics $userInsights = $insightsService->getUserInsights( metrics: ['impressions', 'reach', 'profile_views', 'website_clicks'], period: 'day', since: new DateTime('-7 days') ); // Get audience insights $audience = $insightsService->getAudienceInsights( metrics: ['audience_gender_age', 'audience_locale', 'audience_country'] ); // Get media insights $mediaInsights = $insightsService->getMediaInsights( mediaId: 'media_id_here', metrics: ['impressions', 'reach', 'engagement', 'saves', 'video_views'] ); // Get story insights $storyInsights = $insightsService->getStoryInsights( storyId: 'story_id_here', metrics: ['impressions', 'reach', 'replies', 'taps_forward', 'exits'] ); // Compare content performance $comparison = $insightsService->getContentPerformanceComparison( mediaIds: ['media1', 'media2', 'media3'], metrics: ['impressions', 'engagement'] ); // Get follower growth $growth = $insightsService->getFollowerGrowthInsights( since: new DateTime('-30 days') );
๐ Webhook Management
$webhookService = $instagram->webhooks(); // Subscribe to webhooks $subscription = $webhookService->subscribeWebhook( object: 'instagram', fields: ['messages', 'messaging_postbacks', 'messaging_optins'], callbackUrl: 'https://yourapp.com/webhook', verifyToken: 'your_verify_token' ); // Process incoming webhook $payload = file_get_contents('php://input'); $signature = $_SERVER['HTTP_X_HUB_SIGNATURE_256'] ?? ''; // Verify signature if ($webhookService->verifySignature($payload, $signature, $appSecret)) { $webhookData = $webhookService->processWebhookPayload($payload); match ($webhookData['event_type']) { 'message' => $webhookService->handleMessageWebhook($webhookData), 'postback' => $webhookService->handlePostbackWebhook($webhookData), 'delivery' => $webhookService->handleDeliveryWebhook($webhookData), default => error_log('Unknown webhook event') }; } // Webhook verification (for initial setup) $mode = $_GET['hub_mode'] ?? ''; $challenge = $_GET['hub_challenge'] ?? ''; $verifyToken = $_GET['hub_verify_token'] ?? ''; if ($challenge = $webhookService->validateWebhookChallenge( $mode, $challenge, $verifyToken, 'your_verify_token' )) { echo $challenge; }
๐ฏ Advanced Features
Batch Operations
// Batch get multiple media $mediaIds = ['media1', 'media2', 'media3']; $batchResult = $mediaService->batchGetMedia($mediaIds); foreach ($batchResult as $mediaId => $result) { if (isset($result['error'])) { echo "Error for {$mediaId}: {$result['error']}\n"; } else { echo "Media {$mediaId} loaded successfully\n"; } }
Custom HTTP Options
$instagram = InstagramGraph::withAccessToken( appId: $appId, appSecret: $appSecret, redirectUri: $redirectUri, accessToken: $accessToken, options: [ 'timeout' => 60, 'connect_timeout' => 10, 'headers' => [ 'Custom-Header' => 'value' ] ] );
Error Handling
use Mkhab7\InstagramGraph\Exceptions\{ InstagramGraphException, AuthenticationException, RateLimitException, ValidationException }; try { $user = $instagram->users()->me(); } catch (AuthenticationException $e) { if ($e->isExpiredToken()) { // Refresh token logic $newToken = $instagram->auth()->refreshToken($accessToken); } } catch (RateLimitException $e) { $waitTime = $e->getSuggestedWaitTime(); echo "Rate limited. Wait {$waitTime} seconds.\n"; } catch (ValidationException $e) { echo "Validation error: {$e->getMessage()}\n"; print_r($e->getValidationErrors()); } catch (InstagramGraphException $e) { echo "API error: {$e->getMessage()}\n"; echo "Error type: {$e->getErrorType()}\n"; echo "Facebook trace ID: {$e->getFacebookTraceId()}\n"; }
๐งช Testing
Run the test suite:
# Run all tests composer test # Run tests with coverage composer test-coverage # Run static analysis composer analyse # Format code composer format
Writing Tests
use function Pest\Laravel\{get, post}; it('can get user information', function () { $httpClient = mockHttpClient(); $userService = new UserService($httpClient); $httpClient ->shouldReceive('get') ->once() ->with('me', ['fields' => 'id,username']) ->andReturn(['id' => '123', 'username' => 'test']); $user = $userService->me(['id', 'username']); expect($user->id)->toBe('123') ->and($user->username)->toBe('test'); });
๐ง Configuration Options
Access Token Management
use Mkhab7\InstagramGraph\Auth\AccessToken; // Create long-lived token $longToken = AccessToken::longLived( token: 'long_lived_token_string', userId: 'user_id', scopes: ['instagram_business_basic'] ); // Check token expiration if ($accessToken->isExpired()) { $refreshed = $instagram->auth()->refreshToken($accessToken); } // Check token scopes if ($accessToken->hasScope('instagram_business_manage_messages')) { // Can send messages } // Token persistence $tokenData = $accessToken->toJson(); // Store $tokenData in database // Restore token $restoredToken = AccessToken::fromJson($tokenData);
Logger Integration
use Monolog\Logger; use Monolog\Handler\StreamHandler; $logger = new Logger('instagram'); $logger->pushHandler(new StreamHandler('php://stdout', Logger::DEBUG)); $instagram = InstagramGraph::withAccessToken( appId: $appId, appSecret: $appSecret, redirectUri: $redirectUri, accessToken: $accessToken, logger: $logger );
๐ API Reference
Available Scopes
Scope | Description |
---|---|
instagram_business_basic |
Basic access to Instagram business accounts |
instagram_business_manage_messages |
Manage Instagram direct messages |
instagram_business_manage_comments |
Manage comments on Instagram media |
instagram_business_content_publish |
Publish content to Instagram |
pages_show_list |
Access to Facebook pages list |
pages_read_engagement |
Read page engagement data |
Rate Limits
Instagram API has the following rate limits:
- User Access Token: 200 calls per hour per user
- App Access Token: 200 calls per hour per app
- Business Discovery: 5 calls per hour per app
The SDK automatically handles rate limit errors and provides retry suggestions.
Media Types
IMAGE
- Photo postsVIDEO
- Video posts (including Reels)CAROUSEL_ALBUM
- Multiple photos/videos in one post
๐ค Contributing
We welcome contributions! Please see CONTRIBUTING.md for details.
Development Setup
# Clone repository git clone https://github.com/mkhab7/php-instagram-graph.git cd php-instagram-graph # Install dependencies composer install # Run tests composer test # Check code style composer format # Run static analysis composer analyse
Code Style
This project follows PSR-12 coding standards and uses PHP-CS-Fixer for formatting.
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
๐ Acknowledgments
- Instagram Graph API Documentation
- PHP-FIG for PSR standards
- Pest PHP for testing framework
- All contributors and users
๐ Support
- ๐ Documentation
- ๐ Issue Tracker
- ๐ฌ Discussions
Made with โค๏ธ by mkhab7