Integrating Instagram Graph API into a Lambda-Based Photo Gallery: A Step-by-Step Implementation
Our guest photo page system at shipcaptaincrew.queenofsandiego.com/g/{event_id} displays approved guest-uploaded charter photos alongside Instagram posts from @sailjada, captured during the same time window. The Instagram integration logic was already baked into the Lambda function but dormant—it returned an empty array whenever the required environment variables were missing. This post documents the process of activating that integration by properly configuring the Instagram Graph API and exchanging tokens.
What Was Done
We activated the dormant Instagram integration in the shipcaptaincrew Lambda function (us-east-1, account 782785212866) by:
- Adding the Instagram Graph API product to the existing
sailjada-socialapp in the Meta developer dashboard - Connecting the @sailjada business/creator account to the app
- Generating a short-lived access token with appropriate scopes
- Exchanging that token for a long-lived token (60-day expiration)
- Injecting
IG_USER_IDandIG_ACCESS_TOKENenvironment variables into the Lambda function configuration - Verifying the integration works by checking the guest page for Instagram media
Technical Details: The Product Setup Mistake
The initial blocker was a subtle but critical mistake: the sailjada-social app already had Instagram messaging configured (the "Manage messaging" use case). This use case grants permissions for direct message handling, not media reads. To access Instagram media via Graph API, we needed to add the Instagram Graph API product separately.
Why this matters: The messaging use case does not include the instagram_basic scope required to query media endpoints. Adding a second product was necessary because Meta's developer dashboard enforces strict product-to-scope mappings.
The steps to add the correct product:
- Navigate to
developers.facebook.com/apps - Select the
sailjada-socialapp - In the left sidebar, locate "Add Product" (typically near the bottom)
- Search for "Instagram" and select Instagram Graph API (not Basic Display, not Messaging)
- Complete the setup flow to enable it in your dashboard
Account Linking and Token Generation
Once the product was added, we connected the @sailjada account. This step requires that @sailjada be configured as a Business or Creator account and linked to a Facebook Page (in this case, the page for Queen of San Diego charters).
Step 1: Generate a short-lived token
Using the Facebook Graph API Explorer:
1. Go to developers.facebook.com/tools/explorer
2. Select app: sailjada-social (dropdown in top-left)
3. Click "Generate Access Token"
4. Select the Facebook Page linked to @sailjada
5. In the scopes field, add: instagram_basic, pages_show_list
6. Click through the login flow for @sailjada
The token generated here is short-lived (typically 1 hour) and must be exchanged immediately.
Step 2: Retrieve IG_USER_ID
With the short-lived token, make two Graph API calls. First, get your Facebook Page ID and its linked Instagram business account:
curl -s "https://graph.instagram.com/me/accounts?access_token=SHORT_LIVED_TOKEN"
The response includes a data array with your pages. Each page object contains an instagram_business_account field. Extract the id from that nested object—this is your IG_USER_ID.
Step 3: Exchange for a long-lived token
The 60-day access token is obtained via a POST request:
curl -X POST "https://graph.instagram.com/access_token" \
-d "grant_type=ig_exchange_token" \
-d "client_id=YOUR_APP_ID" \
-d "client_secret=YOUR_APP_SECRET" \
-d "access_token=SHORT_LIVED_TOKEN"
Store the returned access_token value as IG_ACCESS_TOKEN.
Infrastructure: Lambda Environment Variables
The shipcaptaincrew Lambda function expects two environment variables in its configuration:
IG_USER_ID— The numeric Instagram Business Account IDIG_ACCESS_TOKEN— The 60-day access token
Update the Lambda configuration via 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}"
The function code (location: /shipcaptaincrew/lambda_handler.py or equivalent) checks for these variables at runtime. If both are present and valid, the function queries the Instagram Graph API for media posted by @sailjada within the event's time window and merges results with approved guest photos.
Key Decisions
60-day tokens vs. indefinite access: Meta's long-lived tokens expire after 60 days. We chose not to implement automatic refresh via EventBridge initially because monthly manual refresh is operationally simpler for a low-traffic integration. If token refresh becomes a bottleneck, the same curl call can be automated and triggered by a scheduled EventBridge rule targeting a dedicated refresh Lambda.
Graph API over Basic Display: Basic Display API is read-only but returns less metadata (no timestamps, no captions). We selected Graph API because the gallery system filters media by time window, requiring the timestamp field that only Graph API provides.
Verification
After deploying the environment variables, verify the integration by visiting:
https://shipcaptaincrew.queenofsandiego.com/g/2026-04-29You should see Instagram posts from @sailjada alongside guest-uploaded photos from that date. If Instagram media is missing, check Lambda CloudWatch logs for API errors (e.g., invalid token, wrong scope, rate limiting).
What's Next
Future enhancements could include:
- Automated token refresh via EventBridge + a dedicated refresh Lambda
- Caching Instagram media in DynamoDB to reduce API calls
- Filtering by location tags or hashtags for more granular event matching
- Monitoring token expiration via CloudWatch alarms