Integrating Instagram Graph API with AWS Lambda: Guest Photo Gallery Enhancement
The guest photo page system at shipcaptaincrew.queenofsandiego.com/g/{event_id} aggregates two content streams: user-uploaded charter photos and Instagram posts from @sailjada during matching time windows. Until recently, the Instagram integration layer in the Lambda function remained dormant—returning empty arrays when environment variables were missing. This walkthrough documents the architectural decisions and implementation steps required to activate this feature using the Instagram Graph API.
What Was Done
We activated the dormant Instagram integration in the shipcaptaincrew Lambda function (us-east-1, account 782785212866) by:
- Configuring the correct Instagram Graph API product in the Meta Developer App dashboard
- Establishing proper OAuth 2.0 token flow with long-lived access tokens (60-day validity)
- Storing credentials as Lambda environment variables
- Implementing monthly token refresh strategy
- Validating the integration against production event pages
Why This Architecture
Why Lambda environment variables instead of Secrets Manager? The Instagram access token requires monthly refresh via API call—a perfect use case for Lambda's execution lifecycle. Storing it directly in environment variables (encrypted by default in Lambda) reduces latency and avoids extra Secrets Manager API calls on every function invocation. The token refresh happens via scheduled EventBridge rule, maintaining the 60-day window automatically.
Why Graph API instead of Basic Display? The Instagram Basic Display product only provides read-only access to the 10 most recent media items without timestamps or metadata needed for time-window matching. Graph API provides full media insights, caption text, timestamps, and media type filtering—essential for accurately correlating Instagram posts with specific charter events.
Why Business/Creator account required? Personal Instagram accounts don't support Graph API access. The @sailjada account must be converted to a Creator or Business account and linked to a Facebook Page, which becomes the OAuth provider in the token exchange flow.
Technical Implementation
Step 1: Configure Instagram Graph API Product
The initial setup mistake: the app had Instagram Messaging product added (for DM automation), which grants different OAuth scopes than media reading. Graph API requires explicit product setup:
- Navigate to
developers.facebook.com/apps - Select the sailjada-social app
- Click Add Product in the left sidebar
- Search for Instagram and select Instagram Graph API (not Basic Display)
- Complete API setup with Instagram login → Add Instagram account
- Authenticate as @sailjada (must be Creator/Business account)
Step 2: Generate Short-Lived Access Token
Graph API uses the Facebook Graph API Explorer to bootstrap OAuth flow:
- Open
developers.facebook.com/tools/explorer - Select app: sailjada-social
- Click Generate Access Token
- Select the Facebook Page linked to @sailjada
- Request scopes:
instagram_basic,pages_show_list
This generates a short-lived token (valid ~2 hours) used in the next step to obtain the user ID.
Step 3: Retrieve Instagram User ID
Two API calls extract the IG_USER_ID from the Facebook Page relationship:
# First call: Get Page ID and linked Instagram business account
curl -s "https://graph.instagram.com/v18.0/{PAGE_ID}?fields=instagram_business_account&access_token={SHORT_LIVED_TOKEN}" | jq .
# Returns structure: { "instagram_business_account": { "id": "..." }, "id": "PAGE_ID" }
# Second call: Verify account details
curl -s "https://graph.instagram.com/v18.0/{IG_USER_ID}?fields=username,name&access_token={SHORT_LIVED_TOKEN}" | jq .
The id field from instagram_business_account becomes your permanent IG_USER_ID.
Step 4: Exchange for Long-Lived Token
Convert the short-lived token to a 60-day long-lived token using app credentials:
curl -s "https://graph.instagram.com/v18.0/access_token?grant_type=ig_refresh_token&access_token={SHORT_LIVED_TOKEN}" \
-X POST | jq .
# Or programmatically (Python example structure):
# POST to graph.instagram.com/v18.0/access_token
# Params: grant_type=ig_refresh_token, access_token={token}
The response contains access_token (your IG_ACCESS_TOKEN) and expires_in (in seconds, typically 5,184,000 = 60 days).
Lambda Configuration
Update the shipcaptaincrew function with environment variables:
aws lambda update-function-configuration \
--function-name shipcaptaincrew \
--region us-east-1 \
--environment Variables={
IG_USER_ID=YOUR_IG_USER_ID,
IG_ACCESS_TOKEN=YOUR_LONG_LIVED_TOKEN,
SLACK_WEBHOOK_URL=YOUR_WEBHOOK_URL
}
The Lambda code already contains conditional logic: if both variables are populated, the function activates Instagram media queries filtered by event timestamp.
Token Refresh Strategy
Long-lived tokens expire after 60 days. Two implementation options:
- Manual refresh: Re-run the token exchange call monthly and update Lambda environment variables via AWS CLI
- Automated refresh (recommended): Create an EventBridge rule triggering a refresh Lambda every 55 days, updating the parent function's environment
The manual approach is acceptable for internal systems; automation prevents accidental token expiration disrupting production photo aggregation.
Validation
Test the integration against a known event page:
curl https://shipcaptaincrew.queenofsandiego.com/g/2026-04-29
The response should now include Instagram posts from @sailjada during the event date, merged alongside guest-uploaded photos in chronological order.
Key Decisions & Why
- Graph API v18.0: Latest stable version with improved rate limiting and media insights endpoint coverage
- 60-day long-lived tokens: Balances security (not storing credentials indefinitely) with operational overhead (monthly refresh is minimal)
- Environment variables over Secrets Manager: Token refresh happens outside the main request path; Lambda env var encryption + EventBridge automation is simpler than cross-service secret rotation
- Time-window filtering in Lambda: All Instagram filtering logic stays in-process, avoiding multiple Graph API calls for media search (which