Integrating Instagram Graph API with AWS Lambda: Guest Photo Gallery Enhancement for Ship Captain Crew
What Was Done
We enhanced the guest photo gallery system at shipcaptaincrew.queenofsandiego.com/g/{event_id} to display Instagram posts from @sailjada alongside user-uploaded charter photos. The Instagram integration was previously dormant in the Lambda function—it existed in the codebase but returned empty arrays due to missing environment variables. This post documents the complete workflow for activating the Instagram Graph API integration and establishing a sustainable token refresh strategy.
Architecture Overview
The system operates in three layers:
- Frontend: Static HTML at
/tools/shipcaptaincrew/index.htmlserved via CloudFront, queries a guest-specific endpoint - Backend: AWS Lambda function
shipcaptaincrewin us-east-1 (account 782785212866) handles photo aggregation and Instagram API calls - Data Sources: S3 for guest-uploaded photos; Instagram Graph API for @sailjada business account posts
Technical Details: Instagram Graph API Integration
The Core Problem
The Lambda function at /tools/shipcaptaincrew/lambda_function.py contained dormant code checking for environment variables IG_USER_ID and IG_ACCESS_TOKEN. When missing, the function returned an empty media array. The initial Facebook app configuration included only the Messaging product, which grants Direct Message scopes—insufficient for reading Instagram media. The solution required:
- Adding the Instagram Graph API product (distinct from Basic Display or Messaging)
- Generating appropriate access tokens with
instagram_basicandpages_show_listscopes - Extracting the Instagram Business Account ID from the linked Facebook Page
- Implementing a token refresh strategy (tokens expire after 60 days)
Step-by-Step Implementation
1. Add Instagram Graph API Product
Navigate to developers.facebook.com/apps and select the sailjada-social application. In the left sidebar, click Add Product and search for Instagram Graph API. This is distinct from Instagram Basic Display (read-only, no business features) and Instagram Messaging. The Graph API product grants access to business account media, insights, and related features.
2. Connect the Business Account
Inside the Instagram Graph API product settings, find API setup with Instagram login and click Add Instagram account. Log in using the @sailjada credentials. This account must be a Creator or Business account and must be linked to a Facebook Page. The app then creates the relationship between the app, the Facebook Page, and the Instagram Business Account.
3. Generate Short-Lived Access Token
Use the Graph API Explorer at developers.facebook.com/tools/explorer:
- Select app:
sailjada-social - Generate Access Token
- Select the Facebook Page linked to @sailjada
- Request scopes:
instagram_basic,pages_show_list
4. Retrieve IG_USER_ID
With the short-lived token, execute two API calls:
# Get the Facebook Page ID
curl -s "https://graph.facebook.com/me/pages?access_token=SHORT_LIVED_TOKEN" \
| jq '.data[] | {id, name}'
# Using the page id returned above, get the Instagram Business Account ID
curl -s "https://graph.facebook.com/PAGE_ID?fields=instagram_business_account&access_token=SHORT_LIVED_TOKEN" \
| jq '.instagram_business_account.id'
The returned id value from instagram_business_account is your IG_USER_ID.
5. Exchange for Long-Lived Token
Short-lived tokens expire within hours. Exchange it for a long-lived token (valid 60 days):
curl -s "https://graph.instagram.com/access_token" \
-d grant_type=ig_exchange_token \
-d client_id=APP_ID \
-d client_secret=APP_SECRET \
-d access_token=SHORT_LIVED_TOKEN \
| jq '.access_token'
The returned access token is your IG_ACCESS_TOKEN.
Infrastructure: Lambda Configuration
Once you have both values, update the Lambda function environment variables:
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}
The Lambda function definition in lambda_function.py includes conditional logic:
if os.environ.get('IG_USER_ID') and os.environ.get('IG_ACCESS_TOKEN'):
# Query Instagram Graph API for media
ig_media = fetch_instagram_posts(event_id_date)
else:
# Return empty array if credentials missing
ig_media = []
With environment variables set, the function activates the Instagram integration without code changes.
Token Refresh Strategy
Long-lived tokens expire after 60 days. Establish a monthly refresh routine:
- Option 1 (Manual): Calendar reminder to run the exchange command above monthly, updating the Lambda environment variables
- Option 2 (Automated): Create an EventBridge rule triggering a Lambda on the 25th of each month, which refreshes the token and updates the shipcaptaincrew Lambda's environment variables via boto3
Option 2 is recommended for production. The refresh Lambda would call boto3.client('lambda').update_function_configuration() with the refreshed token.
Verification
After Lambda configuration, verify the integration at:
curl -s "https://shipcaptaincrew.queenofsandiego.com/g/2026-04-29" | grep -i instagram
Check CloudWatch logs:
aws logs tail /aws/lambda/shipcaptaincrew --region us-east-1 --follow
The logs should show successful API calls to graph.instagram.com and populated media arrays in the JSON response.
Key Decisions
- Instagram Graph API over Basic Display: Graph API supports business features and long-lived tokens; Basic Display is read-only with short-lived tokens requiring reauthorization
- 60-day long-lived tokens over short-lived: Reduces manual intervention; monthly refresh is operationally sustainable
- Environment variables over hardcoding