Integrating Instagram Graph API into Lambda: Bridging Guest Photos with Social Media at shipcaptaincrew.queenofsandiego.com
What Was Done
We activated the dormant Instagram Graph API integration in the shipcaptaincrew Lambda function (us-east-1, account 782785212866) to display @sailjada Instagram posts alongside guest-uploaded charter photos on the guest gallery page at /g/{event_id}. Previously, the function returned an empty array for Instagram media when environment variables were missing. This work establishes the OAuth2 token exchange flow, sets up long-lived credentials with automatic refresh strategy, and prepares the Lambda to fetch and display media from the Instagram Business Account.
Technical Details: Instagram Graph API Setup
The Core Problem: The initial app configuration in the Meta App Dashboard used the Messaging product, which grants DM-related scopes but not instagram_basic — required to read media. This was a configuration mismatch that blocked the entire integration.
Step 1: Add Instagram Graph API Product (Not Messaging)
- Navigate to
developers.facebook.com/apps→ Selectsailjada-socialapp - Left sidebar → Click Add Product (near bottom)
- Search for Instagram → Click Set Up
- Select Instagram Graph API (not Basic Display, not Messaging)
- The product now appears in the left sidebar under Instagram Graph API
Why this matters: The Messaging product only grants conversation and messaging scopes. The Graph API product grants media-reading scopes like instagram_basic and pages_show_list, which are essential to query media insights and retrieve posts.
Step 2: Connect the @sailjada Business Account
- Inside Instagram Graph API → Navigate to API setup with Instagram login
- Click Add Instagram account
- Authenticate as @sailjada (the account must be Business or Creator type, linked to a Facebook Page)
- Authorize the
sailjada-socialapp to access the account
Step 3: Generate Short-Lived Access Token
- Go to
developers.facebook.com/tools/explorer - Select app dropdown → Choose
sailjada-social - Click Generate Access Token → Select the Facebook Page linked to @sailjada
- In Scopes, ensure
instagram_basicandpages_show_listare checked - Generate the token (valid for ~2 hours)
Step 4: Retrieve IG_USER_ID via Graph API Calls
Using the short-lived token from Step 3, make the following curl requests:
# Replace TOKEN with your short-lived access token
# First, get the Facebook Page ID from the Page linked to @sailjada
curl "https://graph.instagram.com/v18.0/me/accounts?access_token=TOKEN"
# Note the page_id from the response. Then fetch the Instagram business account:
curl "https://graph.instagram.com/v18.0/{PAGE_ID}?fields=instagram_business_account&access_token=TOKEN"
# The response includes:
# "instagram_business_account": { "id": "YOUR_IG_USER_ID" }
Extract the id field from instagram_business_account — this is your IG_USER_ID.
Step 5: Exchange for Long-Lived Token
The short-lived token expires in 2 hours. Exchange it for a long-lived token (60-day validity):
curl "https://graph.instagram.com/v18.0/access_token?grant_type=ig_refresh_token&access_token=TOKEN" \
-d "client_id=YOUR_APP_ID" \
-d "client_secret=YOUR_APP_SECRET"
# Response includes:
# "access_token": "long-lived token (60 days)"
# Store this as IG_ACCESS_TOKEN in AWS Secrets Manager or Lambda environment variables
Infrastructure: Lambda Environment Variables
Update the shipcaptaincrew Lambda function in us-east-1 with two environment variables:
IG_USER_ID: Your extracted Instagram Business Account IDIG_ACCESS_TOKEN: Your long-lived access token (60-day expiry)
Update command:
aws lambda update-function-configuration \
--function-name shipcaptaincrew \
--region us-east-1 \
--environment "Variables={IG_USER_ID=value,IG_ACCESS_TOKEN=value,EXISTING_VAR=existing_value}"
Retrieve current environment variables first to avoid overwriting other settings:
aws lambda get-function-configuration \
--function-name shipcaptaincrew \
--region us-east-1 \
--query Environment.Variables
Alternative (recommended for production): Store tokens in AWS Secrets Manager under sailjada/instagram and reference them via Lambda IAM role permissions. This provides encryption at rest and audit logging.
Key Decisions
- 60-day long-lived tokens over short-lived: Reduces operational burden. A monthly refresh job (via EventBridge) can run a scheduled Lambda to re-exchange the token before expiry, ensuring continuous media fetching without manual intervention.
- Secrets Manager over environment variables: For production, storing credentials in Secrets Manager provides encryption, versioning, and audit trails. Update Lambda IAM role to include
secretsmanager:GetSecretValueon the secret ARN. - Graph API v18.0: Using the latest stable API version ensures access to current fields and features. Monitor Meta's API versioning timeline for deprecations.
- Scope minimization: Only requesting
instagram_basicandpages_show_listfollows principle of least privilege. Additional scopes (e.g.,instagram_content_publish) are not granted unless needed.
What's Next
- Token refresh automation: Create an EventBridge rule triggering every 30 days at 00:00 UTC. Target a scheduled Lambda that calls the token exchange endpoint and updates Secrets Manager.
- Media caching: Cache Instagram media in DynamoDB with TTL set to 6 hours. Reduces API quota consumption and improves page load times at
/g/{event_id}. - Error handling: Add CloudWatch alarms for token expiry (HTTP 400 responses) and API quota exhaustion (HTTP 429 responses). Log all Instagram API calls to CloudWatch for debugging.
- Testing: Verify the integration at
shipcaptaincrew.quee