Integrating Instagram Graph API with AWS Lambda: Adding Social Media Context to Charter Photo Galleries
What Was Done
We activated Instagram Graph API integration for the shipcaptaincrew Lambda function (us-east-1, account 782785212866) to surface @sailjada Instagram posts alongside guest-uploaded charter photos on the guest gallery page at shipcaptaincrew.queenofsandiego.com/g/{event_id}. The Lambda had dormant Instagram support waiting for environment variables; this work involved properly provisioning API credentials and establishing the token refresh strategy.
Technical Details
Instagram Graph API Product Configuration
The initial blocker was an incorrect product selection in the Meta app dashboard. The app had "Messaging" configured, which grants DM-related scopes but does not include instagram_basic — the minimum scope required to read media metadata. The fix required adding a separate product:
- Navigate to developers.facebook.com/apps and select the
sailjada-socialapp - Click Add Product in the left sidebar
- Search for and select Instagram Graph API (critical distinction: not Basic Display, not Messaging)
- Complete the setup wizard to enable the product
Why this matters: Instagram's permission model is granular by use case. Messaging grants access to conversation threads; Graph API grants access to media, insights, and business account data. Conflating these causes silent permission failures where API calls return empty arrays despite valid credentials.
Account Linking and Token Generation
Once the product was active, we needed to establish a verifiable connection between the app and the @sailjada Instagram Business account. Instagram requires accounts to be either Business or Creator accounts linked to a Facebook Page. The account @sailjada meets this requirement.
Step 1: Generate a Short-Lived Token
Using the Facebook Graph API Explorer at developers.facebook.com/tools/explorer:
- Select app
sailjada-socialfrom the dropdown - Click Generate Access Token
- Select the Facebook Page linked to @sailjada's business account
- In the requested scopes dialog, ensure
instagram_basicandpages_show_listare checked - Approve the permission request
This produces a short-lived token (valid ~2 hours) used only for bootstrapping.
Step 2: Retrieve the Instagram User ID
With the short-lived token, query the Graph API to find the Instagram Business Account ID associated with the Facebook Page:
# First, get the page's instagram_business_account
curl -s "https://graph.facebook.com/v18.0/{PAGE_ID}?fields=instagram_business_account&access_token={SHORT_LIVED_TOKEN}"
# Response includes: "instagram_business_account": { "id": "..." }
# Store this ID as IG_USER_ID
The returned id value becomes the IG_USER_ID environment variable for the Lambda.
Step 3: Exchange for a Long-Lived Token
Short-lived tokens expire quickly. Exchange it for a long-lived token (valid 60 days) using the app's credentials:
curl -s "https://graph.instagram.com/access_token" \
-d "grant_type=ig_refresh_token" \
-d "access_token={SHORT_LIVED_TOKEN}"
# Response: { "access_token": "...", "token_type": "bearer" }
# The access_token is your IG_ACCESS_TOKEN
This long-lived token is what gets stored in Lambda environment variables and used in production queries.
Infrastructure Changes
Lambda Environment Variables
Update the shipcaptaincrew Lambda function (deployed in us-east-1) with two new environment variables:
aws lambda update-function-configuration \
--function-name shipcaptaincrew \
--region us-east-1 \
--environment Variables="{IG_USER_ID=,IG_ACCESS_TOKEN=}"
These variables are referenced in the Lambda handler to conditionally enable Instagram photo fetching. When both are present, the function executes Instagram Graph API queries; when missing, it returns an empty array (the original dormant behavior).
Code Paths Activated
The Lambda handler at shipcaptaincrew contains logic that was previously inert. With environment variables populated, the function now:
- Extracts the event date from the gallery URL parameter
{event_id}(format: YYYY-MM-DD) - Queries the Instagram Graph API endpoint:
https://graph.instagram.com/v18.0/{IG_USER_ID}/mediawith filters for the date window - Filters results to media posted within ±24 hours of the charter event
- Merges Instagram media metadata with approved guest uploads for display
Why this architecture: Keeping Instagram integration optional via environment variables allows the gallery to gracefully degrade. If credentials are missing or expired, guests still see uploaded photos. This design reduces operational burden during token refresh windows.
Key Decisions
60-Day Token Lifecycle
Instagram Graph API long-lived tokens expire after 60 days. We chose manual refresh over automation for now because:
- Low frequency: Refresh every 2 months is operationally simple
- Visibility: Manual steps make token rotation explicit and auditable
- Future flexibility: If refresh becomes frequent, an EventBridge rule can trigger a Lambda to auto-exchange tokens using stored app secrets
The refresh is a straightforward repeat of Step 3 above—no code changes required, just a credential rotation.
Scope Selection
We requested only instagram_basic and pages_show_list, not additional scopes like instagram_manage_messages or insights`. This principle of least privilege:
- Reduces the blast radius if credentials are compromised
- Simplifies Facebook app review if needed in the future
- Makes the token's purpose explicit: read media only
Verification and Testing
After updating Lambda environment variables, verify the integration by visiting:
https://shipcaptaincrew.queenofsandiego.com/g/2026-04-29
If Instagram posts from @sailjada on 2026-04-29 exist, they should appear alongside guest uploads. Check CloudWatch Logs for the shipcaptaincrew function to inspect API response times and any errors.
What's Next
- Token refresh automation: If manual refresh becomes tedious, implement an EventBridge rule