Integrating Instagram Graph API with AWS Lambda: Building a Photo Gallery Bridge for Charter Events
What Was Done
We implemented Instagram Graph API integration into an existing AWS Lambda function that serves guest-uploaded charter photos for the Ship Captain Crew event platform (shipcaptaincrew.queenofsandiego.com). The Lambda function, deployed in us-east-1 (account 782785212866), now has the infrastructure to fetch and display official @sailjada Instagram posts alongside user-submitted photos from matching event dates and time windows.
The integration was previously dormant—the Lambda contained conditional logic that returned an empty array when environment variables for Instagram credentials were missing. This session focused on the foundational steps: correctly configuring the Instagram Graph API product in the Facebook App Console, obtaining proper authentication credentials, and documenting the token refresh lifecycle.
Technical Details: Why the Wrong Product Setup Was a Blocker
The initial configuration added the Instagram Messaging API product to the sailjada-social app. While this product grants access to Instagram Direct Messages, it does not include the instagram_basic scope required to read media metadata (captions, timestamps, media URLs). The Messaging use case is designed for bot automation and customer support workflows, not media library queries.
The correct product is Instagram Graph API, which provides access to:
instagram_basic— read user profile data and media metadatapages_show_list— list Facebook Pages linked to the app user- Media endpoint:
GET /{ig-user-id}/mediato retrieve posts and their details
This distinction is critical because the token exchange flow validates requested scopes against the product's grant set. A token generated under Messaging will be rejected when the Lambda attempts to query media endpoints.
Authentication Flow and Token Lifecycle
Instagram Graph API uses a two-tier token system:
- Short-lived tokens (1 hour) — generated interactively via Graph API Explorer or OAuth flows
- Long-lived tokens (60 days) — obtained by exchanging a short-lived token with app credentials
The process:
- Log in to
developers.facebook.com/tools/exploreras a developer on the app - Select the
sailjada-socialapp and the Facebook Page linked to@sailjada - Generate an access token with scopes:
instagram_basic,pages_show_list - Use this short-lived token to call the Accounts endpoint:
This returns the Instagram Business Account ID (stored asGET /me/accounts?fields=instagram_business_accountIG_USER_ID) - Exchange the short-lived token for a long-lived token:
GET /oauth/access_token?grant_type=fb_exchange_token&client_id={APP_ID}&client_secret={APP_SECRET}&fb_exchange_token={SHORT_LIVED_TOKEN}
The long-lived token is then stored in the Lambda environment as IG_ACCESS_TOKEN and used in the handler to fetch media. Since Instagram long-lived tokens expire after 60 days of non-use, the deployment includes a note to refresh via the same exchange call on a monthly cadence (optional automation via EventBridge).
Infrastructure and Configuration
Lambda Function:
- Name:
shipcaptaincrew - Region:
us-east-1 - Runtime: Python 3.x
- File:
/Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/lambda_function.py
Environment Variables (to be added):
IG_USER_ID— Instagram Business Account ID (17-digit numeric string)IG_ACCESS_TOKEN— Long-lived Graph API token
Update command (template):
aws lambda update-function-configuration \
--function-name shipcaptaincrew \
--region us-east-1 \
--environment Variables={IG_USER_ID=value,IG_ACCESS_TOKEN=value}
Frontend:
- HTML:
/Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/index.html - Route:
/g/{event_id}(e.g.,/g/2026-04-29) - API calls: Lambda handler routes requests and merges approved guest photos with Instagram media
Verification:
CloudWatch Logs can be monitored to confirm successful API calls:
aws logs tail /aws/lambda/shipcaptaincrew --region us-east-1 --follow
Key Architectural Decisions
Why Lambda environment variables instead of Secrets Manager? For this use case, the tokens are non-critical metadata (media URLs, captions) and the 60-day refresh window is manageable. Secrets Manager adds operational overhead for a lower-risk credential type. If we scale to handle DMs or account modifications, migration to Secrets Manager would be warranted.
Why long-lived tokens at all? The handler must refresh media on each request without user interaction. Short-lived tokens expire in 1 hour, requiring a backend token-refresh flow. Long-lived tokens (60 days) simplify the logic and reduce API calls. The trade-off is a monthly refresh task (can be automated via EventBridge scheduler + a separate Lambda).
Why check for missing credentials gracefully? The existing Lambda code contains:
if not IG_USER_ID or not IG_ACCESS_TOKEN:
instagram_posts = []
This allows safe deployment before credentials are available, preventing runtime errors during dev iterations.
What's Next
- Implement token refresh automation (EventBridge rule + scheduled Lambda invocation)
- Add error handling for expired tokens (catch 400/401 responses, log to CloudWatch)
- Test the guest page at
/g/2026-04-29to verify photo merging and timestamp filtering - Monitor Lambda logs for API quota consumption (Instagram rate limits: 200 calls/hour per token)
- Document the 60-day refresh cadence in the deployment wiki
Summary: This session unblocked Instagram Graph API integration by correcting the app product configuration, defining the credential exchange flow, and establishing the environment variables needed for the Lambda handler. The foundation is now in place for official @sailjada photos to appear alongside guest uploads on charter event pages.
```