Integrating Instagram Graph API with AWS Lambda: Building a Guest Photo Gallery with Social Media Aggregation
What Was Done
We integrated Meta's Instagram Graph API into the ShipCaptainCrew guest photo gallery system to automatically surface @sailjada Instagram posts alongside user-uploaded charter photos. The integration lives in a Lambda function that queries the Instagram Graph API for media posted within a configurable time window around each charter event, enriching the guest experience with official social content.
The guest photo page at shipcaptaincrew.queenofsandiego.com/g/{event_id} (e.g., /g/2026-04-29) now has the infrastructure in place to display both user-uploaded approved photos and Instagram posts from the same timeframe, though the integration remained dormant until proper credentials were configured.
Technical Details
Architecture Overview
The system follows a Lambda-backed single-page application pattern:
- Frontend:
/tools/shipcaptaincrew/index.html— Static HTML served via CloudFront, makes API calls to the Lambda function's HTTP endpoint - Backend:
/tools/shipcaptaincrew/lambda_function.py— AWS Lambda function handling guest photo database queries and Instagram Graph API requests - Data Layer: DynamoDB tables for approved guest photos, indexed by event date
- Social Integration: Instagram Graph API for @sailjada business account media retrieval
Lambda Function Structure
The Lambda handler in lambda_function.py implements two core request flows:
- Guest Photo Retrieval: Routes requests to
/g/{event_id}, queries DynamoDB for approved photos matching the event date, returns paginated results with metadata (uploader, upload timestamp, approval status) - Instagram Media Aggregation: For the same event date, constructs a time window (configurable buffer, typically ±12 hours), calls Instagram Graph API with the
IG_USER_IDandIG_ACCESS_TOKENenvironment variables, requests media fields:id,media_type,media_url,timestamp,caption
The function gracefully degrades when Instagram credentials are absent — it logs a debug message and returns an empty array for Instagram posts, allowing the guest photo gallery to function independently without external API dependencies.
Instagram Graph API Integration Points
Three environment variables control the Instagram integration:
IG_USER_ID— The Instagram business account ID for @sailjada (numeric string, obtained via Graph API explorer)IG_ACCESS_TOKEN— Long-lived access token (valid 60 days) withinstagram_basicscopeIG_APP_ID,IG_APP_SECRET— Optional; used for automated monthly token refresh
The Lambda makes authenticated requests to the Instagram Graph API endpoint:
GET https://graph.instagram.com/v18.0/{IG_USER_ID}/media
?fields=id,media_type,media_url,timestamp,caption
&access_token={IG_ACCESS_TOKEN}
Results are filtered client-side in the index.html to match the event's time window, then merged with approved guest photos and returned as a unified array.
Infrastructure and Configuration
Lambda Function Configuration
Function Name: shipcaptaincrew
Region: us-east-1
Account: 782785212866
Environment variables are set via AWS Lambda console or CLI:
aws lambda update-function-configuration \
--function-name shipcaptaincrew \
--region us-east-1 \
--environment Variables='{IG_USER_ID=...,IG_ACCESS_TOKEN=...'}'
API Routing
The Lambda function is fronted by an HTTP API (likely API Gateway or Lambda Function URL). The guest page makes CORS-enabled requests to the API endpoint:
GET /g/{event_id}— Returns guest photos + Instagram posts for the eventPOST /admin/approve-photo— Admin endpoint for photo moderation (uses API key auth)
CloudWatch Logging
Logs are written to /aws/lambda/shipcaptaincrew in CloudWatch. Instagram API errors (rate limits, invalid tokens, network failures) are logged with full error detail and request context to aid debugging.
Key Architectural Decisions
Why Instagram Graph API Over Basic Display
Instagram's Basic Display API limits us to the last 100 media items without timestamp filtering. The Graph API provides real-time media retrieval with precise filtering by posted time, allowing us to match posts to specific charter events. The tradeoff: Graph API requires a business/creator account linked to a Facebook Page, plus OAuth setup — but this is a one-time configuration cost.
Time Window Filtering
Rather than filtering in the API call (which would require more complex Graph API queries), we fetch all recent media from the account and filter client-side by timestamp. This simplifies the Lambda logic and reduces API call complexity. For high-volume accounts, this could be optimized via Lambda-side filtering before returning to the client.
Graceful Degradation
The Instagram integration is optional. If credentials are missing or the API is unavailable, the guest photo gallery continues to function with user-uploaded content only. This was a deliberate design choice to prevent a social media outage from affecting the core product.
Token Refresh Strategy
Instagram access tokens expire after 60 days. The recommended approach is to run a monthly refresh via EventBridge + Lambda, calling the token exchange endpoint to generate a new 60-day token. This can be automated or initially handled manually before a monthly refresh schedule is established.
What's Next
- Token Refresh Automation: Set up an EventBridge rule that triggers a Lambda function monthly to refresh the IG_ACCESS_TOKEN and update the ShipCaptainCrew function's environment variables
- Media Caching: Implement DynamoDB caching for Instagram posts to reduce API calls and improve page load time; invalidate the cache when new posts are detected
- Analytics: Track which Instagram posts drive engagement in the guest gallery (click-throughs, shares); feed this data back to social strategy
- Multi-Account Support: Extend the integration to support multiple Instagram accounts (e.g., crew members' accounts in addition to @sailjada)
- Error Handling Enhancements: Implement exponential backoff and retry logic for transient API failures; set up SNS alerts for persistent Instagram API errors