Integrating Instagram Graph API with Lambda: Connecting @sailjada Media to Guest Photo Pages
What Was Done
We implemented Instagram Graph API integration into the shipcaptaincrew Lambda function (us-east-1, account 782785212866) to automatically surface @sailjada Instagram posts alongside guest-uploaded charter photos on dynamic guest photo pages at shipcaptaincrew.queenofsandiego.com/g/{event_id}. The Lambda previously had dormant Instagram code that returned an empty array when environment variables were missing; this work activates that capability by properly provisioning Graph API credentials and establishing the token refresh cycle.
Technical Details: Why We Needed Graph API, Not Basic Display
The initial setup attempt used Instagram's Messaging use case, which is designed for handling direct messages—not media reads. This is a critical distinction in Facebook's product architecture. To read media metadata (captions, timestamps, media URLs) from a business/creator account, you must use the Instagram Graph API product, which requires the instagram_basic scope. The Messaging use case does not grant this scope, making it a blocker for media retrieval.
Here's why this architecture matters:
- Graph API — Designed for reading business/creator account data (media, insights, follower counts). Requires a Facebook Page linked to the Instagram business account.
- Basic Display — Read-only access to media but no timestamps or captions; insufficient for our use case.
- Messaging — DM handling only; no media scope granted.
@sailjada's account must be a Business or Creator account (not a personal account) and linked to a Facebook Page for this to work. The token flow uses the page's credentials to access the linked Instagram business account.
Step-by-Step Credential Provisioning
1. Add the Instagram Graph API Product
In the Facebook App Dashboard for sailjada-social:
- Navigate to
developers.facebook.com/apps→ selectsailjada-social - In the left sidebar, locate Add Product (near the bottom)
- Search for Instagram and click Set Up
- When prompted for access type, select Instagram Graph API (not Basic Display)
- It will appear in your sidebar as a new product tile
2. Connect the @sailjada Instagram Account
Within Instagram Graph API settings:
- Click API setup with Instagram login
- Click Add Instagram account
- Authenticate as @sailjada (must be the business/creator account owner)
- Authorize the app to access the account
3. Generate a Short-Lived Token (Valid for ~1 hour)
Using the Graph API Explorer:
- Go to
developers.facebook.com/tools/explorer - In the top-left dropdown, select
sailjada-social - Click Generate Access Token
- Select the Facebook Page linked to @sailjada
- Ensure scopes include
instagram_basicandpages_show_list - Copy the generated token for the next step
4. Retrieve IG_USER_ID from Page to Instagram Business Account
Execute two API calls to traverse from Facebook Page → Instagram Business Account ID:
# First call: Get the page's linked Instagram business account
curl "https://graph.instagram.com/v18.0/{PAGE_ID}?fields=instagram_business_account&access_token={SHORT_LIVED_TOKEN}"
# Response structure:
# {
# "instagram_business_account": {
# "id": "17841400000000000"
# },
# "id": "{PAGE_ID}"
# }
# Extract the "id" from instagram_business_account — this is your IG_USER_ID
Store this IG_USER_ID value; it's immutable and tied to the Instagram account.
5. Exchange Short-Lived Token for Long-Lived Token (60-day validity)
Short-lived tokens expire in ~1 hour and are impractical for Lambda environment variables. Exchange it for a long-lived token:
curl "https://graph.instagram.com/v18.0/oauth/access_token" \
-d "grant_type=ig_refresh_token" \
-d "access_token={SHORT_LIVED_TOKEN}" \
-d "client_secret={APP_SECRET}"
# Response:
# {
# "access_token": "long_lived_token_value",
# "token_type": "bearer"
# }
The returned access_token is your IG_ACCESS_TOKEN. Store this securely in AWS Secrets Manager or Lambda environment variables.
Infrastructure: Lambda Environment Variables
The shipcaptaincrew Lambda function reads these environment variables (set via console or CLI):
aws lambda update-function-configuration \
--function-name shipcaptaincrew \
--region us-east-1 \
--environment Variables="{IG_USER_ID=17841400000000000,IG_ACCESS_TOKEN=your_60_day_token}"
When both variables are present, the Lambda's dormant Instagram integration code activates automatically and includes @sailjada posts in responses to guest photo page requests.
Token Refresh Strategy
Long-lived tokens expire after 60 days. Rather than manual renewal, we recommend:
- EventBridge rule triggered monthly (e.g.,
cron(0 0 1 * ? *)for the 1st of each month) - Lambda function that calls the same exchange endpoint with the current token, receives a refreshed token, and updates the shipcaptaincrew function's environment
- Fallback: If a token expires, Instagram endpoints return a 401; the guest photo page gracefully omits Instagram posts and shows only guest-uploaded photos
This pattern avoids hardcoding token generation logic into the main application Lambda.
Key Decisions
- Environment variables over Secrets Manager — For this use case, environment variables are sufficient because the token is rotated monthly and the exposure window is bounded. If rotation were manual/infrequent, Secrets Manager would be preferable.
- 60-day tokens vs. indefinite — Facebook intentionally limits long-lived token lifespans to encourage refresh patterns, reducing the blast radius of a leaked token. We accepted this constraint rather than working around it.
- Separate product (not Messaging) — Adding Messaging first was a process error; Graph API is the correct product for media reads and has different OAuth scopes.
- Page-level authentication — By authenticating through the Facebook Page, we avoid storing @sailjada's personal credentials. The page acts as a proxy for permissions.