Integrating Instagram Graph API with AWS Lambda: Adding Social Media Context to Guest Photo Galleries
What Was Done
We implemented Instagram Graph API integration into the shipcaptaincrew Lambda function (us-east-1, account 782785212866) to automatically surface @sailjada Instagram posts alongside guest-uploaded charter photos on the guest gallery page at shipcaptaincrew.queenofsandiego.com/g/{event_id}. The Lambda function previously had dormant Instagram integration code that returned empty arrays when environment variables were missing. This work activated that integration by obtaining the necessary API credentials and configuring the Lambda execution environment.
Technical Details: Instagram Graph API Setup
The Instagram Graph API differs fundamentally from Instagram's Basic Display API and Messaging API—it's the only product that grants media read permissions. This distinction matters because the Facebook app (sailjada-social) initially had only the Messaging product configured, which is designed for Direct Message operations, not media queries.
Step 1: Add the Correct Product to Your App
- Navigate to
developers.facebook.com/apps - Select the
sailjada-socialapp - Click Add Product in the left sidebar
- Search for and select Instagram Graph API (not Basic Display, not Messaging)
- Complete the setup flow; the product will appear in your left sidebar
Why this matters: Each Facebook app product grants different OAuth scopes. Messaging alone cannot request the instagram_basic scope needed to read media metadata. Graph API is the superset product that includes media reading capabilities.
Step 2: Connect the Instagram Business Account
- Within Instagram Graph API → API Setup
- Click Add Instagram Account
- Authenticate as @sailjada (must be the Business or Creator account, not a personal account)
This creates the OAuth linkage between the Facebook app and the Instagram business account. The Graph API requires a business-class account to access media endpoints.
Step 3: Generate a Short-Lived Access Token
- Visit
developers.facebook.com/tools/explorer - Select
sailjada-socialfrom the app dropdown - Click Generate Access Token
- Select the Facebook Page linked to @sailjada from the account selector
- In the permissions dialog, ensure these scopes are checked:
instagram_basic(read media, profile data)pages_show_list(enumerate pages linked to your account)
- Copy the generated token (valid for ~2 hours)
Step 4: Extract the Instagram Business Account ID
The Graph API requires the numeric Instagram Business Account ID (not the username). With your short-lived token, make this call:
curl -s "https://graph.instagram.com/me?fields=instagram_business_account&access_token=TOKEN" \
| python3 -m json.tool
The response will contain an instagram_business_account object with an id field. This numeric ID is your IG_USER_ID.
Step 5: Exchange for a Long-Lived Token
Short-lived tokens expire quickly; Instagram Graph API supports 60-day long-lived tokens for server-to-server applications. Exchange your short-lived token using your app credentials:
curl -s "https://graph.instagram.com/access_token" \
-X POST \
-d "grant_type=ig_refresh_token" \
-d "access_token=SHORT_LIVED_TOKEN" \
-d "client_secret=YOUR_APP_SECRET" \
| python3 -m json.tool
The returned access_token is valid for 60 days and is your IG_ACCESS_TOKEN.
Infrastructure: Lambda Environment Configuration
The Lambda function shipcaptaincrew reads two environment variables at startup:
IG_USER_ID— the numeric Instagram Business Account ID from Step 4IG_ACCESS_TOKEN— the 60-day token from Step 5
Update these via the AWS CLI:
aws lambda update-function-configuration \
--function-name shipcaptaincrew \
--region us-east-1 \
--environment Variables={IG_USER_ID=123456789,IG_ACCESS_TOKEN=REDACTED} \
--no-cli-pager
Alternatively, update via AWS Console: Lambda → shipcaptaincrew → Configuration → Environment variables.
The Lambda code checks for these variables at invocation time. When both are present, it queries the Instagram Graph API endpoint:
GET https://graph.instagram.com/v18.0/{IG_USER_ID}/media?fields=id,caption,media_type,media_url,timestamp&access_token={IG_ACCESS_TOKEN}
Results are filtered by timestamp to match the event date window and merged with guest-uploaded photos in the response JSON.
Key Decisions
Why 60-Day Tokens Instead of OAuth Flow
A production system would implement OAuth 2.0 code flow to request fresh tokens on behalf of users. Since this is a single-account integration (always pulling @sailjada's posts), a long-lived token is simpler and avoids the overhead of maintaining an OAuth state store. The 60-day window requires a quarterly refresh routine.
Why Graph API v18.0
Facebook deprecates older API versions every 2 years. Using a recent stable version (v18.0 as of early 2024) ensures access to current features and a ~2-year upgrade window before deprecation. Hardcoding the version in Lambda prevents automatic upgrades that could introduce breaking changes.
Timestamp Filtering in Lambda
The Media endpoint returns posts in reverse chronological order without date filtering. The Lambda function filters results in-process using event_id (which embeds the charter date) to show only posts from the same day/time window. This approach reduces network I/O and keeps business logic in Lambda rather than scattering it across multiple API calls.
What's Next
- Token Refresh Automation: Implement an EventBridge rule (cron: monthly) that triggers a Lambda function to refresh the 60-day token using the same token-exchange endpoint. Store the refreshed token in Secrets Manager or Parameter Store rather than updating function environment variables each month.
- Error Handling: Add CloudWatch alarms for Instagram API failures. If the token expires or is revoked, the gallery should degrade gracefully (show only guest photos, log the error) rather than fail the entire page request.
- Testing: Add integration tests to verify the guest gallery page at
/g/2026-04-29returns both guest photos and Instagram posts when credentials are configured. - Documentation: Document the quarterly token refresh process in your runbook so future operators