Integrating Instagram Graph API with AWS Lambda: Adding Social Media Context to Guest Photo Pages

What Was Done

We activated dormant Instagram Graph API integration in the shipcaptaincrew Lambda function (us-east-1, account 782785212866) to display @sailjada Instagram posts alongside guest-uploaded charter photos on the guest photo page system at shipcaptaincrew.queenofsandiego.com/g/{event_id}. The infrastructure was already in place but non-functional due to missing environment variables. This post walks through the token generation, Lambda configuration, and verification process.

Technical Details: Token Generation Strategy

Instagram Graph API requires a two-stage token lifecycle: short-lived tokens (valid for ~1 hour) must be exchanged for long-lived tokens (valid for 60 days). Rather than manually regenerating tokens monthly, we implemented a sustainable refresh pattern that can be automated via EventBridge if needed.

Stage 1: Setting Up the App Product

The initial blocker was product misconfiguration in the Facebook App Dashboard. The app had Instagram Messaging added (from an earlier implementation attempt), which grants DM-related scopes but not the instagram_basic scope required to read media:

  • Navigate to developers.facebook.com/apps
  • Select the sailjada-social app
  • Click Add Product in the left sidebar
  • Search for and select Instagram Graph API (distinct from Instagram Basic Display or Instagram Messaging)
  • Complete the setup flow

This product addition is a one-time operation and automatically appears in the app dashboard sidebar.

Stage 2: Connecting the Instagram Business Account

The @sailjada account must be a Business or Creator account linked to a Facebook Page (verified beforehand). From the Instagram Graph API product settings:

  • Navigate to API Setup
  • Click Add Instagram Account
  • Authenticate as @sailjada
  • Authorize the app to access the account's media endpoints

This creates the authorization link between the app and the Instagram business account.

Stage 3: Generating Short-Lived Token

Using the Facebook Graph API Explorer at developers.facebook.com/tools/explorer:

  • Select sailjada-social from the app dropdown
  • Click Generate Access Token
  • Select the Facebook Page linked to @sailjada from the account picker
  • Ensure scopes include instagram_basic and pages_show_list
  • Copy the resulting short-lived token (valid ~1 hour)

Stage 4: Retrieving IG_USER_ID

The Instagram business account ID is nested within the Facebook Page structure. Two API calls extract it:

curl -s "https://graph.instagram.com/v18.0/{PAGE_ID}?fields=instagram_business_account&access_token={SHORT_LIVED_TOKEN}" | jq .

Record the PAGE_ID from this response, then query the Instagram business account:

curl -s "https://graph.instagram.com/v18.0/{PAGE_ID}?fields=instagram_business_account&access_token={SHORT_LIVED_TOKEN}" | jq '.instagram_business_account.id'

The returned id field is your IG_USER_ID. This value is account-specific and remains static across token refreshes.

Stage 5: Exchanging for Long-Lived Token

Convert the short-lived token to a long-lived token (60-day expiry) using the app credentials:

curl -s "https://graph.instagram.com/v18.0/access_token?grant_type=ig_exchange_token&client_id={APP_ID}&client_secret={APP_SECRET}&access_token={SHORT_LIVED_TOKEN}" | jq .

The response contains an access_token field with extended validity. This is your IG_ACCESS_TOKEN.

Infrastructure Changes

Lambda Environment Variables

Update the shipcaptaincrew Lambda function configuration in us-east-1:

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 function path varies with deployment, typically within the photo-fetching logic that currently returns an empty array when these vars are absent).

Token Refresh Strategy

Long-lived tokens expire after 60 days. Two approaches:

  • Manual refresh: Re-run the token exchange call every 58-59 days and update Lambda environment via AWS CLI or Console
  • Automated refresh: Create an EventBridge rule triggering a Lambda function every 55 days to exchange the current token for a fresh one and update the parent Lambda's environment (requires IAM permissions for lambda:UpdateFunctionConfiguration)

For initial deployment, manual refresh is sufficient. Document the token generation date in your deployment notes or a private wiki.

Key Architectural Decisions

Why Not Direct Credentials in Code?

The Lambda function never stores APP_ID or APP_SECRET in code or environment. These values remain in the Facebook App Dashboard and are only used during the initial token exchange, which happens in your local shell or a secured CI/CD pipeline. This limits the blast radius of accidental credential exposure.

Why Long-Lived Tokens?

Short-lived tokens expire hourly, requiring a new generation every Lambda invocation. Long-lived tokens eliminate this overhead and are sufficient for read-only media access. The 60-day expiry is a Facebook limitation; refresh cadence is manageable via calendar reminders or automation.

Why Separate IG_USER_ID and IG_ACCESS_TOKEN?

The user ID is static; the token is ephemeral. Separating them allows token rotation without code changes. The Lambda function queries Instagram media using the pattern /v18.0/{IG_USER_ID}/media, keeping the endpoint structure stable across refreshes.

Verification

After Lambda environment update propagates (usually within seconds):

  1. Navigate to shipcaptaincrew.queenofsandiego.com/g/2026-04-29 (or another known event date)
  2. Open browser DevTools → Network tab
  3. Look for API responses that previously returned empty arrays for Instagram posts
  4. Verify that posts from @sailjada around the event timestamp now appear alongside guest photos

What's Next

Consider implementing automated token refresh via EventBridge to eliminate manual intervention. This requires a secondary Lambda function with appropriate