Integrating Instagram Graph API with AWS Lambda: Enabling Social Media Photo Feeds on Guest Event Pages
What Was Done
We activated Instagram Graph API integration for the shipcaptaincrew Lambda function to display @sailjada Instagram posts alongside guest-uploaded charter photos on event-specific pages (e.g., shipcaptaincrew.queenofsandiego.com/g/{event_id}). The infrastructure was already in place but dormant—the Lambda was returning empty arrays when Instagram credentials were missing. This post walks through the exact steps to configure Instagram Graph API, generate long-lived access tokens, and deploy them to production.
Technical Details: Instagram Graph API Setup
Product Configuration in App Dashboard
The critical first step is adding the correct product to your Meta app. Navigate to developers.facebook.com/apps, select the sailjada-social app, and click Add Product in the left sidebar. Search for Instagram and select Instagram Graph API—not Basic Display, not Messaging. The Messaging product (used for Direct Messages) does not grant the instagram_basic scope required to read media metadata.
Why this matters: Instagram Graph API requires specific OAuth scopes. Without the Graph API product properly configured, the token generation flow will fail or grant insufficient permissions, and subsequent API calls to fetch media will return 403 Forbidden errors.
Account Linking and Token Generation
Once the Instagram Graph API product is active, navigate to API Setup within that product and click Add Instagram Account. Log in with the @sailjada credentials. This links the Business or Creator account to your app.
To generate a short-lived access token (valid for ~1 hour), use the Graph API Explorer tool:
- Select the
sailjada-socialapp from the dropdown - Click Generate Access Token
- Choose the Facebook Page linked to @sailjada
- Request scopes:
instagram_basic,pages_show_list
This token is a temporary credential used only to bootstrap the process of obtaining your long-lived token.
Retrieving IG_USER_ID
With your short-lived token, execute two sequential API calls to retrieve the Instagram User ID:
# First: Get your Facebook Page ID and Instagram Business Account
curl -s "https://graph.instagram.com/me/accounts?access_token=SHORT_LIVED_TOKEN" | jq .
# Response includes page_id. Then:
curl -s "https://graph.instagram.com/{page_id}?fields=instagram_business_account&access_token=SHORT_LIVED_TOKEN" | jq .
# Extract the "id" field from instagram_business_account—this is IG_USER_ID
Store the IG_USER_ID value; you'll need it in the next step.
Token Exchange for Long-Lived Credentials
Exchange your short-lived token for a long-lived token (60-day expiration) using your app credentials:
curl -s "https://graph.instagram.com/oauth/access_token?grant_type=ig_refresh_token&access_token=SHORT_LIVED_TOKEN&client_id=YOUR_APP_ID&client_secret=YOUR_APP_SECRET" | jq .
The response includes a new access_token field—this is your IG_ACCESS_TOKEN (valid for 60 days). Store this securely.
Why a separate exchange call? Facebook's OAuth design separates token generations: short-lived tokens are tied to user sessions (Graph API Explorer sessions); long-lived tokens are suitable for server-to-server communication and can be refreshed without user interaction. This architecture allows your Lambda to maintain continuous access across the 60-day window.
Infrastructure: Lambda Environment Variables
The shipcaptaincrew Lambda function (region: us-east-1, account: 782785212866) reads two environment variables at startup:
IG_USER_ID—the Instagram Business Account ID from Step 4IG_ACCESS_TOKEN—the long-lived token from Step 5
When both are present, the Lambda function's Instagram integration code path becomes active. When either is missing, it gracefully returns an empty array (existing behavior in dormant mode).
To deploy these variables, use the AWS CLI:
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}'
Security note: Store both values in AWS Secrets Manager or Parameter Store in production. Use IAM roles to grant the Lambda execution role read access to these secrets, then retrieve them at runtime rather than hardcoding in environment variables.
Lambda Code Path: How the Integration Works
Inside the shipcaptaincrew handler, when a request arrives for /g/{event_id}, the function:
- Parses the event date from the URL path
- Queries the guest photo database (DynamoDB or S3 manifest) for approved photos from that date
- If
IG_USER_IDandIG_ACCESS_TOKENare set, calls Instagram Graph API to fetch recent media from @sailjada within a time window matching the event - Merges both datasets and returns a combined JSON response
- The client-side template renders all photos in chronological order
The key API call to Instagram Graph API is:
GET https://graph.instagram.com/{IG_USER_ID}/media?fields=id,caption,media_type,media_url,timestamp&access_token={IG_ACCESS_TOKEN}
This returns an array of media objects. The Lambda filters by timestamp to match the event window.
Key Decisions and Trade-offs
Long-lived tokens vs. short-lived: We chose 60-day tokens because the Lambda runs autonomously without user interaction. A short-lived token + refresh flow would require either a background refresh job or a scheduled task. The 60-day window is a reasonable compromise; token refresh is a manual process currently, but can be automated via EventBridge in the future.
No caching layer: The current design calls Instagram Graph API on every request to /g/{event_id}. For high-traffic events, consider adding CloudFront caching (5–10 minute TTL) or an in-Lambda LRU cache of recent queries. This reduces Instagram API rate-limit exposure.
Graceful degradation: If the Instagram token expires or API calls fail, the endpoint still returns approved guest photos. Instagram posts are a feature enhancement, not core functionality.
Verification and Testing
After deploying the environment variables, test the integration by navigating to a recent event