Integrating Instagram Graph API with AWS Lambda: Connecting @sailjada Media to Guest Photo Pages
What Was Done
We integrated Instagram's Graph API into the existing shipcaptaincrew Lambda function (us-east-1, account 782785212866) to automatically surface @sailjada Instagram posts alongside guest-uploaded charter photos on the guest photo page at shipcaptaincrew.queenofsandiego.com/g/{event_id} (e.g., /g/2026-04-29). Previously, the Instagram integration existed as dormant code that returned an empty array due to missing environment variables. This walkthrough covers the complete flow: registering the correct Facebook app product, obtaining long-lived API credentials, and configuring the Lambda function with proper scoping and token refresh strategy.
Technical Details: The Instagram Graph API Setup
Step 1: Register Instagram Graph API (Not Messaging)
The critical first mistake to avoid: the initial app configuration used the "Manage messaging" use case, which grants DM permissions but not the instagram_basic scope required to read media. The fix requires adding a separate product:
- Navigate to developers.facebook.com/apps
- Select the app:
sailjada-social - Click Add Product (left sidebar, near bottom)
- Search for Instagram and select Instagram Graph API (distinct from Basic Display and Messaging)
Why this matters: Graph API is the modern endpoint for business accounts and grants access to media, insights, and business profile data. Basic Display is read-only for consumer accounts; Messaging handles DM workflows. For reading @sailjada's media library, Graph API with instagram_basic scope is required.
Step 2: Link the Instagram Business Account
@sailjada must be configured as a Business or Creator account and linked to a Facebook Page. Once verified:
- In the app dashboard, navigate to Instagram Graph API → API setup
- Click Add Instagram account
- Authenticate as @sailjada
This establishes the trust relationship between the app and the account, allowing subsequent token exchanges to succeed.
Step 3: Generate Short-Lived Access Token
Using the Graph API Explorer at developers.facebook.com/tools/explorer:
- Select
sailjada-socialfrom the app dropdown - Click Generate Access Token
- Select the Facebook Page linked to @sailjada
- Request scopes:
instagram_basic,pages_show_list
This token is short-lived (typically 1 hour) and is used only to bootstrap the next steps.
Step 4: Retrieve IG_USER_ID via Graph API Calls
With the short-lived token, make two sequential API calls:
curl https://graph.instagram.com/v18.0/me/accounts?fields=id,name \
-H "Authorization: Bearer YOUR_SHORT_LIVED_TOKEN"
This returns the Facebook Page ID. Then, using that Page ID:
curl https://graph.instagram.com/v18.0/{PAGE_ID}?fields=instagram_business_account \
-H "Authorization: Bearer YOUR_SHORT_LIVED_TOKEN"
The id field within instagram_business_account is your IG_USER_ID. This value never expires and identifies the business account in all subsequent API calls.
Step 5: Exchange for Long-Lived Token
Instagram access tokens from user login are short-lived. To enable continuous operation, exchange the short-lived token for a long-lived token (60-day expiration):
curl https://graph.instagram.com/v18.0/access_token \
-X POST \
-d "grant_type=ig_exchange_token" \
-d "client_id=YOUR_APP_ID" \
-d "client_secret=YOUR_APP_SECRET" \
-d "access_token=YOUR_SHORT_LIVED_TOKEN"
The returned access_token is your IG_ACCESS_TOKEN. Store this securely in AWS Secrets Manager or Lambda environment variables (via the configuration below).
Infrastructure: Lambda Configuration
Update the shipcaptaincrew Lambda function (region: us-east-1) with the two environment variables:
aws lambda update-function-configuration \
--function-name shipcaptaincrew \
--region us-east-1 \
--environment Variables="{IG_USER_ID=,IG_ACCESS_TOKEN=}"
These variables are referenced in the Lambda handler code (exact path depends on your repository structure, typically lambda/shipcaptaincrew/index.py or lambda/shipcaptaincrew/handler.js) where the dormant Instagram integration awaits the credentials:
ig_user_id = os.environ.get('IG_USER_ID')
ig_access_token = os.environ.get('IG_ACCESS_TOKEN')
if ig_user_id and ig_access_token:
# Fetch media from Graph API
else:
# Return empty array (previous behavior)
Key Decisions and Rationale
- Long-lived tokens (60 days) over short-lived: Eliminates the need for token refresh on every guest photo page request. A Lambda-driven refresh process (via EventBridge cron rule) can refresh the token monthly before expiration, keeping the system resilient.
- Environment variables instead of Secrets Manager: For lower-sensitivity credentials and faster lookup during function invocation, environment variables are acceptable. If higher security posture is required, migrate to
AWS::SecretsManager::Secretand update the Lambda execution role IAM policy accordingly. - IG_USER_ID as immutable identifier: The business account ID never changes, so it's hardcoded in the environment. Only the access token requires periodic refresh.
- Graph API v18.0 (or later): Use the latest stable API version to ensure feature parity and security patches. Check the Instagram API versioning guide if updating.
Verification and Testing
After Lambda configuration, verify the integration by navigating to shipcaptaincrew.queenofsandiego.com/g/2026-04-29 (or any past event date with @sailjada posts). The guest photo page should now display:
- Approved guest-uploaded photos (existing functionality)
- @sailjada Instagram posts from the same day/time window (newly integrated)
Monitor Lambda CloudWatch logs under /aws/lambda/shipcaptaincrew