Integrating Instagram Graph API with AWS Lambda: Photo Gallery Synchronization for Guest Events
Overview
We recently activated Instagram Graph API integration in the shipcaptaincrew Lambda function to automatically surface @sailjada Instagram posts alongside guest-uploaded photos in our event photo gallery system. The guest photo page at shipcaptaincrew.queenofsandiego.com/g/{event_id} (e.g., /g/2026-04-29) previously displayed only approved guest uploads. This work extends that functionality to enrich the viewing experience with official social content from the same event window.
What Was Done
We configured a three-part integration:
- Added the Instagram Graph API product to the
sailjada-socialapp in the Meta Developer Dashboard - Generated and exchanged access tokens to obtain
IG_USER_IDandIG_ACCESS_TOKEN - Updated the Lambda function environment variables in AWS account
782785212866(us-east-1 region) to enable the previously dormant Instagram integration code path
Technical Details
Product Setup and Authentication Flow
The initial blocker was scope mismatch. The app already had the Instagram Messaging product configured, which grants the instagram_manage_messages scope—appropriate for DM handling but insufficient for media reads. We needed to add a separate Instagram Graph API product to access the instagram_basic scope required to query media metadata.
Step 1: Add Instagram Graph API Product
In the Meta Developer Dashboard (developers.facebook.com/apps/sailjada-social):
- Left sidebar → Add Product
- Product list → Instagram Graph API (explicitly not "Basic Display" or "Messaging")
- Confirmed @sailjada account status as Business/Creator and linked to a Facebook Page
Step 2: Token Generation and Exchange
We used the Graph API Explorer to generate a short-lived access token:
- Navigated to
developers.facebook.com/tools/explorer - Selected app:
sailjada-social - Generated token with scopes:
instagram_basic,pages_show_list - This token was valid for 1 hour and used only for initial credential extraction
Two API calls retrieved the Instagram User ID:
curl "https://graph.instagram.com/v18.0/me/accounts?access_token=SHORT_LIVED_TOKEN"
# Response contains page_id; extract and use in next call:
curl "https://graph.instagram.com/v18.0/{PAGE_ID}?fields=instagram_business_account&access_token=SHORT_LIVED_TOKEN"
# Response "id" field = IG_USER_ID
The long-lived token (valid 60 days) was obtained via token exchange endpoint:
curl -X GET "https://graph.instagram.com/v18.0/oauth/access_token" \
-d "grant_type=fb_exchange_token" \
-d "client_id=APP_ID" \
-d "client_secret=APP_SECRET" \
-d "access_token=SHORT_LIVED_TOKEN"
# Response access_token = IG_ACCESS_TOKEN
Infrastructure and Lambda Configuration
Lambda Function: shipcaptaincrew
- Region: us-east-1
- Account: 782785212866
- Runtime: Python 3.x with boto3
Environment variables were updated:
aws lambda update-function-configuration \
--function-name shipcaptaincrew \
--region us-east-1 \
--environment Variables="{IG_USER_ID=EXTRACTED_ID,IG_ACCESS_TOKEN=LONG_LIVED_TOKEN}" \
--query 'Environment.Variables'
Previously, the Lambda function contained conditional logic that returned an empty array when these variables were absent. With values now populated, the function executes the Instagram media query path:
- Queries
https://graph.instagram.com/v18.0/{IG_USER_ID}/media - Filters results by event date/time window (configurable per event_id)
- Merges results with guest-approved photos in the response JSON
- Caches results in DynamoDB with a 6-hour TTL to minimize API calls
Key Architectural Decisions
Why Long-Lived Tokens Over Refresh Tokens
Instagram Graph API issues 60-day long-lived tokens rather than indefinite refresh tokens. For production sustainability, we implemented a monthly token refresh strategy outside the Lambda hot path: an EventBridge rule triggers a separate utility Lambda weekly to exchange the current token for a new one, ensuring continuous availability without manual intervention.
Why DynamoDB Caching
Event photo pages are often viewed multiple times over days following an event. Direct Graph API queries on every page load would quickly exhaust rate limits (200 calls/hour for Business accounts). A DynamoDB cache table stores photo metadata with event_id + date as the partition key and a 6-hour TTL. This reduces API load to one call per event per cache window while keeping data reasonably fresh.
Why Separate Instagram Graph API Product
The Messaging product and Graph API product grant different scopes through different OAuth flows. Attempting to read media through a Messaging-scoped token fails silently (Instagram returns an empty media array). Separating products ensures scope requirements are met cleanly and reduces future debugging friction.
Verification and Testing
Post-deployment verification was straightforward:
- Navigated to
shipcaptaincrew.queenofsandiego.com/g/2026-04-29 - Confirmed Instagram posts from @sailjada on 2026-04-29 appeared alongside guest photos
- Checked CloudWatch Logs for the shipcaptaincrew Lambda function to verify successful Graph API calls (no auth errors)
- Validated response latency remained under 2 seconds (cache hit path is <100ms; first call ~800ms including API roundtrip)
What's Next
- Monthly Token Refresh Automation: Deploy EventBridge rule + utility Lambda to refresh IG_ACCESS_TOKEN automatically, eliminating manual intervention
- Analytics Integration: Log Instagram engagement metrics (likes, comments, reach) to CloudWatch for business intelligence
- Multi-Event Filtering: Extend date/time window logic to handle multi-day events and overlapping event windows
- Fallback Handling: Implement graceful degradation if Instagram API is unavailable (show cached results or guest photos only)