Integrating Instagram Graph API with AWS Lambda: Bridging Social Media to Guest Photo Galleries
The Ship Captain Crew guest photo system needed a critical capability: displaying @sailjada Instagram posts alongside user-uploaded charter photos in the same event window. While the Lambda function had dormant Instagram integration code, the infrastructure to support it was incomplete. This post details the technical decisions and implementation required to activate Instagram Graph API for a serverless photo aggregation system.
What Was Done
We activated Instagram Graph API integration for the shipcaptaincrew Lambda function, enabling it to fetch and display Instagram media from the @sailjada business account alongside guest-uploaded photos in the event galleries at shipcaptaincrew.queenofsandiego.com/g/{event_id}.
- Configured Instagram Graph API product in the
sailjada-socialFacebook App (not Basic Display or Messaging) - Generated and exchanged authentication tokens with appropriate scopes (
instagram_basic,pages_show_list) - Retrieved the business account ID for @sailjada through Graph API Explorer
- Exchanged short-lived tokens for long-lived tokens (60-day validity)
- Updated Lambda environment variables via
aws lambda update-function-configuration - Modified the Lambda handler in
/Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/lambda_function.pyto activate media fetching
Technical Details: The Instagram Graph API Integration
Why Instagram Graph API? The initial setup attempted to use Instagram Messaging, which only grants permissions for direct messages. To read media (photos, captions, engagement metrics), we need the dedicated Instagram Graph API product with instagram_basic scope. This is a common gotcha when setting up Facebook app integrations—different products grant different permissions.
Authentication Flow
The integration follows Meta's standard server-to-server authentication pattern:
- Short-lived token generation via Graph API Explorer at
developers.facebook.com/tools/explorerwith manual scoping forinstagram_basicandpages_show_list - User ID retrieval by querying the Facebook Page connected to @sailjada, extracting the
instagram_business_accountfield to obtainIG_USER_ID - Token exchange using the app credentials (APP_ID and APP_SECRET from
/Users/cb/Documents/repos/.secrets/repos.env) to convert the short-lived token to a 60-day long-lived token - Credential storage as Lambda environment variables:
IG_USER_IDandIG_ACCESS_TOKEN
Lambda Implementation
The Lambda function at /aws/lambda/shipcaptaincrew (region: us-east-1, account: 782785212866) contains the handler logic in lambda_function.py. The existing code includes an Instagram media fetching function that was dormant, returning an empty array when environment variables were missing:
def fetch_instagram_media(user_id, access_token, event_date):
"""Fetch Instagram media for @sailjada within event date window"""
if not user_id or not access_token:
return []
# Graph API call to /ig_user_id/media endpoint
# Filters by timestamp and returns media with captions
Once environment variables are configured, the handler now executes this function during request processing for routes matching /g/{event_id}, merging results with guest photo data before rendering the HTML response.
Frontend Integration
The index.html at /Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/index.html was updated to render Instagram posts in the guest gallery layout. Each Instagram media item includes the image, caption, and engagement metrics, styled consistently with user-uploaded photos. The API response from Lambda includes both datasets under separate keys, allowing the frontend to distinguish and display them appropriately.
Infrastructure & Configuration
Lambda Function Configuration
The update command follows this pattern:
aws lambda update-function-configuration \
--function-name shipcaptaincrew \
--region us-east-1 \
--environment "Variables={IG_USER_ID=value,IG_ACCESS_TOKEN=value,existing_var=existing_value}"
All environment variables must be included in the command, not just new ones. We preserved existing variables used for photo approval workflows and S3 integration.
Token Refresh Strategy
The 60-day long-lived token requires monthly refresh to avoid expiration gaps. Two approaches exist:
- Manual refresh: Run the token exchange API call monthly and update Lambda environment variables
- Automated refresh: Deploy an EventBridge rule triggering a separate Lambda function to refresh tokens and update configuration automatically
For this implementation, we chose manual refresh initially due to low event frequency and the ability to catch token expiration in logs before it impacts production.
CloudFront & CDN Considerations
The domain shipcaptaincrew.queenofsandiego.com routes through CloudFront to the Lambda function origin. Dynamic Instagram content is not cached; cache headers on the guest gallery routes specify Cache-Control: no-cache, must-revalidate to ensure fresh media is always fetched. CloudFront behavior does not cache responses with Authorization headers, preventing accidental credential leakage.
Key Decisions & Rationale
Product Selection: Instagram Graph API vs. Basic Display Basic Display only returns media URLs and captions—no metadata, no engagement metrics. Graph API provides richer data at the cost of business account requirements. Since @sailjada is a Creator account with a linked Business Page, Graph API was the correct choice.
Token Longevity: 60-day vs. Permanent Tokens Instagram does not issue permanent tokens; 60-day long-lived tokens are the maximum. This forced us to design a refresh workflow. The tradeoff is acceptable because it mirrors best practices for credential rotation and forces regular review of integration health.
Scope Minimization We requested only instagram_basic (read media) and pages_show_list (identify the business account). No write permissions, no messaging, no analytics. This reduces security surface area in the event of token compromise.
Environment Variable Storage Credentials are stored as Lambda environment variables, not hardcoded in function code. While Lambda environment variables are encrypted at rest, a production system might prefer AWS Secrets Manager for better audit trails and rotation automation. For this event-driven system with predictable manual refresh cycles, environment variables are acceptable.
Verification & Testing
After configuration, the integration is verified by accessing an existing event gallery:
curl -s "https://shipcaptaincrew.queenofsandiego.com/g/2026-04-29" | grep -i "instagram\|sailjada"
A successful response includes Instagram media objects in the JSON payload and rendered photo elements in the