Integrating Instagram Graph API into a Lambda-Based Guest Photo Gallery: Setup, Token Exchange, and 60-Day Refresh Strategy
We recently activated Instagram Graph API integration for the guest photo gallery system at shipcaptaincrew.queenofsandwich.com/g/{event_id}. The feature aggregates @sailjada Instagram posts alongside user-uploaded charter photos within a given date/time window. This post walks through the technical setup, token lifecycle management, and the architectural decisions that ensure long-term reliability without manual intervention.
What Was Done
The shipcaptaincrew Lambda function (deployed in us-east-1, AWS account 782785212866) already had Instagram integration scaffolding in place but was dormant due to missing environment variables. We:
- Added the Instagram Graph API product to the existing
sailjada-socialFacebook app (critical: not Messaging API, which doesn't grant media-read scopes) - Generated and exchanged short-lived access tokens for a 60-day long-lived token
- Extracted the Instagram Business Account ID (
IG_USER_ID) from the @sailjada creator account - Stored both
IG_USER_IDandIG_ACCESS_TOKENas Lambda environment variables - Established a monthly token refresh strategy to avoid expiration in production
Technical Details: Token Flow and API Integration
Step 1: Product Configuration in Facebook App Dashboard
The initial mistake was adding the Messaging use case, which grants DM-related scopes but not instagram_basic needed to read media. The correct flow:
- Navigate to
developers.facebook.com/apps - Select the
sailjada-socialapp - Click Add Product in the left sidebar
- Search for and select Instagram Graph API (distinct from Basic Display and Messaging APIs)
- Complete the setup wizard; the product now appears in your dashboard sidebar
Why this matters: Each Instagram API product grants different OAuth scopes. Graph API with instagram_basic scope is the only path to read media captions, timestamps, and image URLs.
Step 2: Account Connection and Token Generation
Inside the Instagram Graph API product settings:
- Navigate to API Setup with Instagram Login
- Click Add Instagram Account and authenticate as @sailjada
- The account must be a Creator or Business account (ours is Creator)
- Confirm the account is linked to a Facebook Page (required for Business Account lookup)
Next, generate a short-lived token via the Graph API Explorer:
- Go to
developers.facebook.com/tools/explorer - Select
sailjada-socialin the app dropdown - Click Generate Access Token
- Select the Facebook Page associated with @sailjada
- Request scopes:
instagram_basic,pages_show_list
This token is short-lived (valid for ~2 hours) and is used only to bootstrap the long-lived token exchange.
Step 3: Retrieving IG_USER_ID via REST Calls
With the short-lived token, make two sequential API calls to discover the Instagram Business Account ID:
curl -s "https://graph.instagram.com/me?fields=id&access_token=SHORT_LIVED_TOKEN" \
| python3 -m json.tool
This returns your Facebook Page ID. Store it. Then:
curl -s "https://graph.instagram.com/{FB_PAGE_ID}?fields=instagram_business_account&access_token=SHORT_LIVED_TOKEN" \
| python3 -m json.tool
The id field nested in instagram_business_account is your IG_USER_ID. This is a stable, permanent identifier for the @sailjada account.
Step 4: Short-Lived to Long-Lived Token Exchange
Instagram's long-lived tokens (valid 60 days) are exchanged server-side using your app credentials:
curl -s "https://graph.instagram.com/access_token?grant_type=ig_refresh_token&access_token=SHORT_LIVED_TOKEN" \
-d "client_id=APP_ID&client_secret=APP_SECRET" \
| python3 -m json.tool
The response includes:
{
"access_token": "LONG_LIVED_TOKEN",
"token_type": "bearer"
}
This token is your IG_ACCESS_TOKEN.
Infrastructure: Lambda Environment Variables
Update the shipcaptaincrew Lambda function with environment variables (no code change required):
aws lambda update-function-configuration \
--function-name shipcaptaincrew \
--region us-east-1 \
--environment Variables="{IG_USER_ID=ACCOUNT_ID_VALUE,IG_ACCESS_TOKEN=TOKEN_VALUE}" \
--profile default
Once deployed, the Lambda's existing code (which checks for these variables) activates the Instagram integration automatically. The guest gallery endpoint at /g/{event_id} now queries Instagram's media endpoint in parallel with the local photo database:
https://graph.instagram.com/{IG_USER_ID}/media?fields=caption,timestamp,media_type,media_url&access_token={IG_ACCESS_TOKEN}
Key Decisions and Rationale
- 60-day vs. indefinite tokens: Instagram doesn't offer indefinite tokens. A 60-day lifecycle forces a refresh cadence and mitigates the risk of a leaked token being valid forever.
- Server-side token exchange: We store
APP_SECRETsecurely (not in client-side code) and perform the exchange in Lambda, ensuring app credentials never travel over the network unnecessarily. - Environment variables over parameter store: For this use case, direct environment variables are simpler than Systems Manager Parameter Store. Both are encrypted at rest in AWS; the choice here prioritizes operational simplicity over audit logging.
- Date/time windowing in Lambda: The Lambda filters Instagram posts by comparing their
timestampfield against the event date, ensuring only relevant posts appear in the gallery view.
Token Refresh Strategy
To prevent token expiration in production, we recommend a monthly refresh:
- Manual monthly refresh: Run the long-lived token exchange curl command (Step 4 above) once per month and update Lambda environment variables via the AWS CLI.
- Automated option (future): Create a CloudWatch Events rule triggering an AWS