Integrating Instagram Graph API with AWS Lambda: Connecting @sailjada Social Posts to Guest Photo Pages
What Was Done
We activated dormant Instagram integration in the shipcaptaincrew Lambda function (us-east-1, account 782785212866) to surface @sailjada Instagram posts alongside guest-uploaded charter photos on the guest photo page system at shipcaptaincrew.queenofsandiego.com/g/{event_id}. The integration fetches Instagram media metadata via the Instagram Graph API and filters posts by event date/time windows to create a unified social + user-generated content feed.
Technical Details
Architecture Overview
The guest photo page system operates on a two-source model:
- Primary source: DynamoDB table storing approved guest uploads with timestamps and metadata
- Secondary source: Instagram Graph API queries filtered by event date
The Lambda function—invoked by API Gateway on requests to /g/{event_id}—was already structured to call Instagram, but returned an empty array because environment variables IG_USER_ID and IG_ACCESS_TOKEN were unset. By populating these variables, we activated the dormant code path without any application changes.
Instagram Graph API Setup
The integration requires three foundational steps in the Facebook App Dashboard:
1. Add Instagram Graph API Product
- Navigate to
developers.facebook.com/apps→ select thesailjada-socialapp - In the left sidebar, click Add Product
- Search for and select Instagram Graph API (critical: not Basic Display, not Messaging—Messaging grants DM scopes, not media-read scopes)
- Complete the setup flow; Instagram Graph API will appear in your sidebar
2. Connect @sailjada Business/Creator Account
- Inside Instagram Graph API settings, locate API setup with Instagram login
- Click Add Instagram Account and authenticate as @sailjada
- The account must be a Business or Creator account linked to a Facebook Page for Graph API access
3. Generate Short-Lived Access Token
Use the Facebook Graph API Explorer to generate an initial token with the required scopes:
- Visit
developers.facebook.com/tools/explorer - Select app:
sailjada-social - Generate Access Token
- Select the Facebook Page linked to @sailjada
- Ensure scopes include:
instagram_basicandpages_show_list
Token Exchange Flow
The short-lived token (valid ~2 hours) must be exchanged for a long-lived token (valid 60 days) that persists in Lambda environment variables.
Step A: Retrieve Instagram User ID
With your short-lived token, first fetch the Facebook Page ID, then extract the linked Instagram business account ID:
# Get the Facebook Page linked to @sailjada
curl -s "https://graph.instagram.com/me/accounts?access_token=SHORT_LIVED_TOKEN" \
| jq '.data[0].id'
# Using that page_id, get the Instagram business account
curl -s "https://graph.instagram.com/{page_id}?fields=instagram_business_account&access_token=SHORT_LIVED_TOKEN" \
| jq '.instagram_business_account.id'
# This ID is your IG_USER_ID
Step B: Exchange for Long-Lived Token
Convert the short-lived token to a 60-day long-lived token using your app credentials:
curl -s "https://graph.instagram.com/access_token?grant_type=ig_exchange_token&client_id=APP_ID&client_secret=APP_SECRET&access_token=SHORT_LIVED_TOKEN" \
| jq '.access_token'
# This is your IG_ACCESS_TOKEN
Both APP_ID and APP_SECRET are stored in AWS Secrets Manager (not hardcoded) and are retrieved by the deployment process.
Infrastructure & Deployment
Lambda Environment Variables
Update 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_IG_ACCESS_TOKEN}"
This command updates the function's environment in-place; no redeployment is required. The Lambda runtime loads these variables into the Python environment, making them available to the Instagram API call code path.
Lambda Function Code Path
The function (located in the deployment artifact) contains conditional logic:
if os.environ.get('IG_USER_ID') and os.environ.get('IG_ACCESS_TOKEN'):
# Query Instagram Graph API for posts by @sailjada
ig_posts = fetch_instagram_media(event_date_range)
else:
ig_posts = []
By setting these variables, the previously dormant Instagram integration activates without code changes—a design decision that allows feature toggles via configuration rather than deployment.
Key Decisions
- Why Instagram Graph API, not Basic Display? Graph API returns media insights (engagement, timestamps) alongside URLs; Basic Display only provides URLs. For a unified feed filtered by event time windows, Graph API metadata is essential.
- Why 60-day long-lived tokens instead of perpetual tokens? Instagram does not offer perpetual tokens. A 60-day window requires a refresh process before expiration, adding a documented operational cadence (monthly refresh via a Lambda-triggered CloudWatch rule is recommended for production).
- Why store in Lambda environment variables? Secrets Manager is overkill for non-secret, regularly-rotated tokens. Environment variables keep token refresh lightweight: a simple CLI call or short script updates the function without secrets rotation overhead.
- Why filter by event date on the API side? Instagram's Graph API supports time-range queries on media retrieval, reducing client-side filtering logic and API quota consumption.
What's Next
- Token Refresh Automation: Set up a CloudWatch Events rule (EventBridge) to trigger a Lambda function monthly that exchanges the current long-lived token for a fresh one, preventing expiration during charter events.
- Error Handling: Wrap Instagram API calls in try-catch blocks with fallback behavior (return empty array if API is unavailable) to ensure guest photos render even if Instagram integration fails.
- Performance Optimization: Cache Instagram media results in ElastiCache or DynamoDB with a 24-hour TTL to reduce API calls during multiple page views of the same event.
- Monitoring: Add CloudWatch metrics for Instagram API latency and failure rates; alert if token refresh fails to