Integrating Instagram Graph API with AWS Lambda: Guest Photo Gallery Architecture
What Was Done
Implemented Instagram Graph API integration into an existing AWS Lambda function to surface @sailjada Instagram posts alongside user-uploaded guest charter photos on event-specific landing pages. The system fetches approved guest photos from DynamoDB and queries Instagram's media endpoint to display contextually relevant social content from the same event date/time window.
Technical Architecture Overview
The guest photo gallery system operates as a serverless application with the following components:
- Frontend: Static HTML/JavaScript hosted on S3 behind CloudFront at
shipcaptaincrew.queenofsandiego.com - API Layer: AWS Lambda function
shipcaptaincrew(us-east-1, account 782785212866) handling photo retrieval and Instagram data aggregation - Data Store: DynamoDB table storing approved guest photo metadata with event date indexing
- Social Integration: Instagram Graph API v18+ for fetching media objects and captions
Route structure: /g/{event_id} where event_id follows ISO date format (e.g., /g/2026-04-29
Instagram Graph API Product Setup
The initial implementation attempt used the Messaging product within Instagram Graph API, which was incorrect. Messaging grants Direct Message management scopes but does not include instagram_basic or pages_show_list scopes required for media enumeration.
Correct Product Configuration:
- Navigate to Facebook App Dashboard for
sailjada-socialapplication - Select Add Product from the left sidebar
- Choose Instagram Graph API (distinct from Basic Display and Messaging)
- Complete Instagram account linking with @sailjada credentials
- The @sailjada account must be configured as a Business or Creator account and linked to a Facebook Page
This grants access to the media endpoints needed for photo feed aggregation.
Token Generation and Exchange Workflow
Instagram tokens follow a two-stage exchange model:
- Short-lived Token Generation: Use Facebook's Graph API Explorer at
developers.facebook.com/tools/explorerto generate a short-lived access token (valid ~2 hours). This requires selecting the Facebook Page linked to @sailjada and requesting scopes:instagram_basic,pages_show_list. - Retrieve IG_USER_ID: Query the Facebook Page object to extract the linked Instagram Business Account ID:
The response containsGET /me/instagram_business_accounts?access_token={SHORT_LIVED_TOKEN}instagram_business_account.id— this is yourIG_USER_IDenvironment variable. - Long-lived Token Exchange: Exchange the short-lived token for a long-lived token (valid 60 days):
The returnedGET /oauth/access_token?grant_type=fb_exchange_token&client_id={APP_ID}&client_secret={APP_SECRET}&fb_exchange_token={SHORT_LIVED_TOKEN}access_tokenbecomes yourIG_ACCESS_TOKENenvironment variable.
Why this approach: Short-lived tokens are safer for development workflows and API explorer tooling. Long-lived tokens reduce token-refresh frequency in production but still require monthly refresh cycles for security compliance.
Lambda Function Implementation
File: /Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/lambda_function.py
The Lambda handler implements conditional Instagram integration:
def handler(event, context):
event_id = event['pathParameters']['event_id']
# Fetch approved guest photos from DynamoDB
guest_photos = query_guest_photos_by_date(event_id)
# Conditionally fetch Instagram posts if credentials present
ig_user_id = os.environ.get('IG_USER_ID')
ig_access_token = os.environ.get('IG_ACCESS_TOKEN')
instagram_posts = []
if ig_user_id and ig_access_token:
instagram_posts = fetch_instagram_media(ig_user_id, ig_access_token, event_id)
return {
'statusCode': 200,
'body': json.dumps({
'guest_photos': guest_photos,
'instagram_posts': instagram_posts
})
}
The fetch_instagram_media function constructs a Graph API request to the media edge endpoint, filtering results by caption timestamps and hashtags matching the event date.
Environment Configuration
Lambda environment variables required:
IG_USER_ID— Instagram Business Account ID (extracted in token workflow, step 2)IG_ACCESS_TOKEN— Long-lived access token (exchanged in step 3, valid 60 days)DYNAMODB_TABLE— Guest photo table name
Update command structure:
aws lambda update-function-configuration \
--function-name shipcaptaincrew \
--region us-east-1 \
--environment Variables={IG_USER_ID=value,IG_ACCESS_TOKEN=value,DYNAMODB_TABLE=shipcaptain-photos}
Frontend Integration
File: /Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/index.html
The static frontend makes client-side fetch requests to the Lambda API endpoint:
fetch(`/g/${eventId}`)
.then(r => r.json())
.then(data => {
renderGuestPhotos(data.guest_photos);
renderInstagramPosts(data.instagram_posts);
})
Photos are rendered in a chronological grid with metadata (photographer name, timestamp, approval status). Instagram posts are interspersed with event-relevant contextual styling.
Key Architectural Decisions
- Conditional Integration: Instagram data is optional. If environment variables are unset, the system returns empty arrays rather than failing. This allows graceful degradation during token rotation or credential issues.
- Server-side Aggregation: Photo combining happens in Lambda, not the frontend. This reduces client complexity and enables server-side filtering/caching if needed later.
- Token Refresh Strategy: 60-day long-lived tokens require monthly manual refresh via the exchange endpoint. Future enhancement: use EventBridge scheduled rule to trigger automated token refresh Lambda function.
- Scope Minimization: Requested only
instagram_basic(read media) andpages_show_list(enumerate linked accounts