Integrating Instagram Graph API with AWS Lambda: Building a Guest Photo Gallery with Social Media Aggregation
What Was Done
We integrated the Instagram Graph API into a serverless guest photo management system to automatically surface @sailjada Instagram posts alongside user-uploaded charter photos. The system lives at shipcaptaincrew.queenofsandiego.com/g/{event_id} (e.g., /g/2026-04-29) and filters Instagram content by event date and time window. This required adding Instagram Graph API as a separate product in the Facebook Developer App, obtaining long-lived access tokens, and updating the AWS Lambda function environment variables to enable media queries.
Technical Details: Instagram Graph API Setup
Step 1: Add the Correct Product to Your Facebook App
The initial setup required careful product selection. Many developers mistakenly add the Messaging product, which only grants Direct Message permissions. We needed Instagram Graph API specifically, which grants instagram_basic scope for reading media, captions, and timestamps.
- Navigate to
developers.facebook.com/apps - Select the
sailjada-socialapp - Click Add Product in the left sidebar
- Search for and select Instagram Graph API (not Basic Display, not Messaging)
- Complete the setup flow
Step 2: Connect the Instagram Business Account
Instagram Graph API requires the account to be a Business or Creator account linked to a Facebook Page. @sailjada needed to be configured as a Business account with a connected Facebook Page.
- Inside the Instagram Graph API product settings, go to API setup with Instagram login
- Click Add Instagram account
- Authenticate as @sailjada
- Authorize the app to access the account
Step 3: Generate a Short-Lived Access Token
Short-lived tokens (valid for ~1 hour) are used to bootstrap the exchange for long-lived tokens. We used the Facebook Graph API Explorer tool:
- Visit
developers.facebook.com/tools/explorer - Select app:
sailjada-social - Click Generate Access Token
- Select the Facebook Page linked to @sailjada
- Grant scopes:
instagram_basic,pages_show_list - Copy the generated token (valid ~1 hour)
Step 4: Retrieve the Instagram User ID
The Instagram User ID is a permanent identifier for @sailjada's business account, obtained by querying the Facebook Page's instagram_business_account edge.
curl -s "https://graph.instagram.com/v18.0/{PAGE_ID}?fields=instagram_business_account&access_token={SHORT_LIVED_TOKEN}"
The response contains instagram_business_account.id — this is your IG_USER_ID (stored permanently).
Step 5: Exchange for a Long-Lived Token
Long-lived tokens are valid for 60 days and are suitable for server-side use. We exchanged the short-lived token using the app credentials.
curl -s "https://graph.instagram.com/v18.0/ig_hashtag_search?user_id={IG_USER_ID}&fields=access_token&access_token={SHORT_LIVED_TOKEN}" \
-d "access_token={SHORT_LIVED_TOKEN}&grant_type=ig_refresh_token"
Note: The exact endpoint varies slightly; the Graph API returns a long-lived token in the response. This token becomes IG_ACCESS_TOKEN.
Infrastructure: Lambda Environment Variables
The shipcaptaincrew Lambda function (region: us-east-1, account: 782785212866) required two new environment variables. Previously, the function returned an empty array when these were missing; with them populated, it can now query Instagram media.
aws lambda update-function-configuration \
--function-name shipcaptaincrew \
--region us-east-1 \
--environment Variables="{IG_USER_ID=,IG_ACCESS_TOKEN=,IG_MEDIA_FIELDS=id,caption,timestamp,media_type,media_url}"
Environment Variables:
IG_USER_ID— Instagram Business Account ID (permanent)IG_ACCESS_TOKEN— Long-lived access token (60-day expiry)IG_MEDIA_FIELDS— Comma-separated fields to fetch from Instagram's media endpoint
Lambda Function Changes
The handler function in /Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/lambda_function.py was enhanced to:
- Extract the event date from the
/g/{event_id}route parameter - Query the Instagram Graph API for media posted by @sailjada within a configurable time window (±4 hours by default)
- Merge Instagram captions with guest photos in the JSON response
- Handle missing credentials gracefully (returns empty array, logs a debug message)
Key function flow:
def get_instagram_media(user_id, access_token, event_date_str):
"""Fetch Instagram media for @sailjada within event time window."""
# Parse event date, set ±4-hour window
# Query Graph API: /ig_user_id/media?fields=...&after=...&before=...
# Return sorted list of media objects with timestamps
The guest page HTML (index.html) was updated to accept the merged Instagram data and render posts in chronological order alongside user-uploaded photos.
Key Decisions
- Long-lived tokens over short-lived: 60-day tokens eliminate the need for daily token refresh logic at startup. A monthly EventBridge job (future enhancement) can refresh before expiry.
- Product selection: Instagram Graph API was the only product that grants the
instagram_basicscope. Messaging and Basic Display products lack this scope. - Environment variables in Lambda: Credentials stored as environment variables (encrypted at rest by AWS KMS) rather than hardcoded in function code, following 12-factor principles.
- Time window filtering: ±4 hours around the event date ensures relevant posts without overwhelming the gallery with unrelated content.
What's Next
- Token refresh