Integrating Instagram Graph API with AWS Lambda: Building Photo Gallery Sync for Charter Events
What Was Done
We integrated Facebook's Instagram Graph API into an existing AWS Lambda function that powers a guest photo gallery system. The shipcaptaincrew Lambda function (deployed in us-east-1, account 782785212866) now has the infrastructure in place to fetch @sailjada Instagram posts and merge them with user-uploaded charter photos, displayed on dynamic event pages at the pattern /g/{event_id} (e.g., /g/2026-04-29).
The integration was previously dormant—the Lambda contained graph API fetch logic but returned empty arrays when environment variables were missing. This walkthrough documents the configuration needed to activate it.
Technical Architecture: Why This Approach
Rather than implementing a custom OAuth flow or building a separate photo sync service, we leveraged Facebook's application-level authentication model. This decision was driven by several factors:
- Long-lived token model: Instagram Graph API supports 60-day access tokens that can be refreshed server-to-server without user interaction. This eliminates the need to ask @sailjada to re-authenticate monthly.
- Business Account requirement: @sailjada must be a Business or Creator account (not a personal account) to expose the Instagram Graph API surface. This aligns with the brand's professional use case.
- Page-to-Account linkage: Instagram business accounts are accessed through their linked Facebook Page. The Graph API traversal is: Facebook App → Page → instagram_business_account node → media endpoint.
- Lambda environment variables: Storing
IG_USER_IDandIG_ACCESS_TOKENas Lambda environment variables keeps the function stateless and allows token rotation without code redeploy.
Infrastructure Setup: Exact Steps
Step 1: Add Instagram Graph API Product
The app sailjada-social (app ID: 1688884572116630) already had a "Messaging" product configured. This grants messaging-related scopes but does NOT grant instagram_basic, which is required to read media. The fix is to add a separate product:
- Navigate to
developers.facebook.com/apps→ select thesailjada-socialapp - Click Add Product in the left sidebar (near the bottom)
- Search for "Instagram" and select Instagram Graph API (not "Basic Display")
- Complete setup—this adds the product to your app's sidebar
Step 2: Connect @sailjada Account
Inside the newly added Instagram Graph API product:
- Go to Instagram Graph API → API setup with Instagram login
- Click Add Instagram account
- Log in with @sailjada credentials
- This links the business account to your app and allows API calls on its behalf
Step 3: Generate Short-Lived Access Token
Use the Graph API Explorer to generate a token with the correct scopes:
- Go to
developers.facebook.com/tools/explorer - In the top-left dropdown, select the
sailjada-socialapp - Click Generate Access Token
- Select the Facebook Page linked to @sailjada
- In the Permissions panel, ensure these scopes are selected:
instagram_basicpages_show_list
- Generate and copy the token (valid for ~2 hours)
Step 4: Retrieve IG_USER_ID
Use the short-lived token to query your page and extract the Instagram business account ID:
curl -s "https://graph.instagram.com/me/accounts?fields=instagram_business_account&access_token=YOUR_SHORT_LIVED_TOKEN" | jq '.'
# Returns: { "data": [ { "instagram_business_account": { "id": "..." }, ... } ] }
# Extract the ID, then call:
curl -s "https://graph.instagram.com/{PAGE_ID}?fields=instagram_business_account&access_token=YOUR_SHORT_LIVED_TOKEN" | jq '.instagram_business_account.id'
# This is your IG_USER_ID
Step 5: Exchange for Long-Lived Token
The short-lived token is only valid for 2 hours. Exchange it for a 60-day token:
curl -s "https://graph.instagram.com/oauth/access_token" \
-d "grant_type=fb_exchange_token" \
-d "client_id=1688884572116630" \
-d "client_secret=YOUR_APP_SECRET" \
-d "access_token=YOUR_SHORT_LIVED_TOKEN" | jq '.access_token'
# Returns: a 60-day access token (IG_ACCESS_TOKEN)
Step 6: Update Lambda Environment Variables
Configure the shipcaptaincrew function with the two values obtained above:
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_60_DAY_TOKEN}"
The Lambda function (file: /Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/lambda_function.py) checks for these variables on each invocation. If present, it fetches Instagram media within the event's time window and merges results with the guest photo database.
Key Decision: Token Refresh Strategy
Instagram's 60-day tokens must be refreshed periodically. Rather than implement a separate cron-based Lambda, we use a simple, human-driven refresh:
- Set a calendar reminder for day 55 of each cycle
- Re-run steps 3–5 above (generate short-lived token, exchange for long-lived)
- Update the environment variable with a single CLI command
This approach avoids additional Lambda functions, DynamoDB state tracking, or EventBridge rules. For higher-scale use cases, a dedicated refresh Lambda triggered by EventBridge would be warranted.
Testing & Verification
Once the environment variables are set, test the integration by visiting an event gallery page:
curl -s "https://shipcaptaincrew.queenofsandiego.com/g/2026-04-29" | grep -i "instagram\|sailjada"
If Instagram posts appear in the HTML or in the JSON response, the API is working. Check CloudWatch Logs for the Lambda function to debug any permission or API errors:
aws logs tail /aws/lambda/shipcaptaincrew --region us-east-1 --follow