Integrating Instagram Graph API into a Guest Photo Gallery: Architecture and Implementation
The Ship Captain Crew guest photo system needed to pull Instagram posts from @sailjada alongside user-uploaded charter photos. This post details the technical implementation of Instagram Graph API integration into a Lambda-based backend, the architectural decisions made, and the specific steps required to activate the feature.
What Was Done
We implemented a dormant Instagram Graph API integration in the Lambda function that powers the guest photo gallery at shipcaptaincrew.queenofsandiego.com/g/{event_id}. The system now fetches and displays Instagram posts from @sailjada that fall within the same day/time window as uploaded guest photos. The integration is environment-variable driven and gracefully degrades (returns empty array) when credentials are absent.
Technical Architecture
Lambda Function Structure
The core Lambda function lives at:
/Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/lambda_function.py
The handler routes requests to distinct code paths based on URI pattern matching. The /g/{event_id} route is handled by a dedicated function that:
- Queries DynamoDB for approved guest photos matching the event_id
- Attempts Instagram API calls if
IG_USER_IDandIG_ACCESS_TOKENenvironment variables are configured - Merges results and returns combined JSON to the frontend
- Returns empty array
[]for Instagram posts if credentials are missing (graceful degradation)
The frontend at /tools/shipcaptaincrew/index.html makes an API call to the Lambda endpoint and renders both guest and Instagram photos in a unified gallery view. This separation of concerns allows the API to be tested independently and the frontend to work with or without Instagram data.
Instagram Graph API Integration Points
The Instagram integration relies on three environment variables set on the Lambda function:
IG_USER_ID— The Instagram Business Account ID (numeric string)IG_ACCESS_TOKEN— Long-lived access token (60-day validity)FB_APP_IDandFB_APP_SECRET— Used for token refresh (optional but recommended)
The Lambda function makes HTTP requests to Facebook's Graph API endpoint:
https://graph.instagram.com/v18.0/{IG_USER_ID}/media?access_token={IG_ACCESS_TOKEN}&fields=id,caption,media_type,media_url,timestamp,permalink
This endpoint returns media objects filtered by timestamp to match the guest photo event window. The API response is transformed into the same JSON structure as guest photos to enable unified rendering.
Infrastructure and Deployment
Lambda Configuration
The Lambda function is deployed in the us-east-1 region under AWS account 782785212866. The function name is shipcaptaincrew.
Environment variables are managed via Lambda function configuration. Updates to environment variables use the AWS CLI:
aws lambda update-function-configuration \
--function-name shipcaptaincrew \
--region us-east-1 \
--environment Variables={IG_USER_ID=value,IG_ACCESS_TOKEN=value,FB_APP_ID=value,FB_APP_SECRET=value}
This approach keeps secrets out of version control and allows rotation without redeploying function code.
API Gateway and CloudFront
The Lambda is exposed through API Gateway, which routes traffic from the custom domain shipcaptaincrew.queenofsandiego.com. CloudFront sits in front of the API Gateway to cache static assets (the index.html and JavaScript/CSS) while ensuring dynamic API responses bypass cache headers appropriately.
The static HTML/CSS/JS are also synced to S3 and served directly via CloudFront for improved performance:
aws s3 sync /path/to/shipcaptaincrew s3://shipcaptaincrew-assets/ --delete
Logging and Monitoring
Lambda logs are available via CloudWatch Logs at:
/aws/lambda/shipcaptaincrew
To tail logs in real-time during testing:
aws logs tail /aws/lambda/shipcaptaincrew --region us-east-1 --follow
Key Technical Decisions
Why Environment Variables Over Secrets Manager
While AWS Secrets Manager would be more secure for production systems handling sensitive credentials at scale, we chose Lambda environment variables for simplicity in this case because:
- The tokens are read-only (Instagram basic scope, no permission to delete/modify posts)
- Token rotation is infrequent (60-day cycle)
- The Lambda runs in a private VPC endpoint with limited external access
- Environment variables can be updated without code redeployment
In a future iteration, integrating Secrets Manager would add negligible latency while improving audit trails and access control.
Graceful Degradation Pattern
The Instagram integration is completely optional. If credentials are missing, the API silently returns an empty array for Instagram posts rather than failing the entire request. This design decision:
- Allows the guest photo gallery to function during Instagram credential setup
- Prevents cascading failures if tokens expire unexpectedly
- Enables A/B testing (some events with Instagram, others without)
- Reduces deployment risk — we can enable Instagram gradually
Long-Lived vs. Short-Lived Tokens
Instagram Graph API issues short-lived tokens (1 hour) by default. We exchange them for long-lived tokens (60 days) because:
- Lambda invocations are stateless; generating a new token on each request would introduce latency and API quota consumption
- 60-day tokens allow for manual monthly refresh rather than complex background refresh logic
- Facebook's token exchange endpoint is idempotent, so refreshing early carries no penalty
A future improvement would be to automate monthly refresh via EventBridge + Lambda, but manual refresh is acceptable given the low-frequency nature of the endpoint.
What's Next
The integration is ready for activation. The remaining steps are:
- Step 1 (Blocker): Add the Instagram Graph API product to the Facebook app (not the Messaging product, which does not grant media-read scopes)
- Step 2: Connect @sailjada as a Business/Creator account within the app
- Step 3: Generate a short-lived token using the Graph API Explorer with correct scopes
- Step 4: Extract
IG_USER_IDfrom the FB Page's linked Instagram Business Account - Step 5: Exchange the short-lived token for a long-lived token
- Step 6: Update Lambda environment variables and verify at
/g/2026-04-29