Integrating Instagram Graph API into a Serverless Photo Gallery: Lambda Environment Configuration and Token Exchange
What Was Done
We integrated Instagram's Graph API into the Ship Captain Crew guest photo gallery system to automatically surface @sailjada Instagram posts alongside user-uploaded charter photos. The integration required adding the Instagram Graph API product to our existing Facebook app, establishing secure token exchange workflows, and configuring AWS Lambda environment variables to support monthly token refresh cycles.
The guest photo page at shipcaptaincrew.queenofsandiego.com/g/{event_id} (e.g., /g/2026-04-29) previously had dormant Instagram logic in the Lambda function that returned an empty array when credentials were missing. This work activated that integration.
Technical Details: Architecture and Implementation
File Structure and Lambda Function
The core Lambda function resides at:
/Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/lambda_function.py
This function handles two primary responsibilities:
- Routing HTTP requests to the guest photo gallery (
/g/{event_id}pattern) - Fetching approved guest photos from DynamoDB and Instagram posts from the Graph API using stored credentials
The function expects two environment variables to activate Instagram integration:
IG_USER_ID— The Instagram Business Account ID tied to @sailjadaIG_ACCESS_TOKEN— A long-lived access token (60-day validity) for Graph API calls
Facebook App Configuration
The integration uses the existing sailjada-social Facebook app (AWS account: 782785212866, region: us-east-1). A critical discovery during setup: the app had the Messaging use case added, which grants scopes for Instagram Direct Messages but not for reading media. We added the Instagram Graph API product separately through the app dashboard to unlock the correct scopes:
instagram_basic— Read media, user profile data, and basic metadatapages_show_list— List pages linked to the app for token exchange
Token Exchange Workflow
Instagram access tokens have a 60-day lifespan. The implementation uses a two-token strategy:
Short-lived token (3 hours) — Generated via the Graph API Explorer for initial testing and to bootstrap the process:
# Example: Graph API Explorer call (not executable, conceptual)
GET https://graph.instagram.com/me?fields=id,username&access_token={SHORT_LIVED_TOKEN}
Long-lived token (60 days) — Obtained by exchanging the short-lived token with your app credentials:
# Token exchange (no credentials shown)
GET https://graph.instagram.com/oauth/access_token
?grant_type=fb_exchange_token
&client_id={APP_ID}
&client_secret={APP_SECRET}
&access_token={SHORT_LIVED_TOKEN}
The IG_USER_ID is extracted from the Instagram Business Account object returned when querying the Facebook Page's Instagram connection:
# Get the page's Instagram business account
GET https://graph.instagram.com/{PAGE_ID}?fields=instagram_business_account&access_token={TOKEN}
The response includes an instagram_business_account object with an id field — this is your IG_USER_ID.
Infrastructure: AWS Lambda Configuration
The Lambda function is deployed in us-east-1 and invoked through CloudFront, which routes requests from shipcaptaincrew.queenofsandiego.com. Environment variables are updated using 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}
Function logs are monitored via CloudWatch:
aws logs tail /aws/lambda/shipcaptaincrew --region us-east-1 --follow
The HTML frontend at /Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/index.html makes a synchronous fetch to the Lambda root endpoint, which returns the guest photos and Instagram posts as JSON.
Key Decisions and Rationale
Why Long-lived Tokens Over Refresh Tokens
Instagram's Graph API doesn't provide refresh tokens; instead, short-lived tokens can be exchanged for long-lived tokens using app credentials. Since our Lambda has access to app secrets via environment variables, we use the 60-day token approach with a manual monthly refresh strategy. This avoids token expiry during peak charter season.
Why Environment Variables Over Secrets Manager
While AWS Secrets Manager would add encryption at rest, environment variables are sufficient here because:
- The token is already encrypted in transit (HTTPS)
- Lambda execution role policies can restrict environment variable access
- Updating variables via CLI is faster than Secrets Manager for monthly rotations
Why Query the Facebook Page Instead of Direct Account Connection
Instagram Business Accounts are linked to Facebook Pages, not directly to apps. By querying the Page's instagram_business_account field, we avoid hardcoding the IG account ID and support future account migrations if needed.
What's Next
- Automated Token Refresh — Implement an EventBridge scheduled rule to trigger a Lambda function monthly that exchanges the current token for a fresh one, preventing expiry-related outages
- Error Handling — Add retry logic in the guest photo Lambda for Instagram API rate limits or transient failures
- Scoped Tokens by Event — Consider caching Instagram posts by event_id to reduce API calls during peak traffic
- Public Sharing — Extend the HTML to include Instagram post captions and engagement metrics
The integration is now live and verified at shipcaptaincrew.queenofsandiego.com/g/2026-04-29, where guest photos and @sailjada Instagram posts from the same day appear side-by-side.