Integrating Instagram Graph API with AWS Lambda: Setting Up Social Media Photo Feeds on Dynamic Guest Pages

What Was Done

We activated the dormant Instagram integration in the shipcaptaincrew Lambda function (us-east-1, account 782785212866) to display @sailjada Instagram posts alongside guest-uploaded charter photos on dynamic event pages. The integration previously returned empty arrays due to missing environment variables. This post walks through the complete setup: adding the correct Instagram product to the Meta app, generating Graph API tokens with proper scopes, securely storing credentials in Lambda environment variables, and validating the end-to-end flow.

Technical Details: Instagram Graph API Token Generation

The Instagram Graph API requires a two-stage token workflow. Understanding this flow is critical because many developers confuse Basic Display (for simple profile/media display) with Graph API (for programmatic reads and business insights).

Step 1: Add the Correct Product to Your Meta App

Navigate to developers.facebook.com/apps and open the sailjada-social app. In the left sidebar, click Add Product. Search for Instagram and select Instagram Graph API (not Basic Display or Messaging). This is the critical distinction—the Messaging product, while useful for DMs, does not grant the instagram_basic scope needed to read media objects. Select this product, complete the setup flow, and it will appear in your left sidebar.

Step 2: Connect the Business Account

The @sailjada account must be a Business or Creator account linked to a Facebook Page. This is a hard requirement for Graph API access—personal accounts cannot be queried. Navigate to Instagram Graph API → API setup and click Add Instagram account. Log in as @sailjada and authorize the app to access the account. This creates a link between your Meta app and the Instagram business account.

Step 3: Generate a Short-Lived Access Token

Go to the Graph API Explorer at developers.facebook.com/tools/explorer. Select the sailjada-social app from the dropdown. Click Generate Access Token, then select the Facebook Page linked to @sailjada. In the Permissions panel, ensure these scopes are selected:

  • instagram_basic (read media, user info, insights)
  • pages_show_list (list pages your app can access)

The generated token is short-lived (approximately 1 hour) and is used only to retrieve your Instagram Business Account ID and to exchange for a long-lived token.

Step 4: Retrieve the Instagram Business Account ID

With your short-lived token, make the following Graph API call (replace SHORT_LIVED_TOKEN with the token from Step 3):

curl "https://graph.instagram.com/v18.0/me/accounts?access_token=SHORT_LIVED_TOKEN"

The response contains an array of Facebook Pages. Note the id field of the page linked to @sailjada. Then query that page for the Instagram business account:

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

The response includes instagram_business_account.id—this is your IG_USER_ID. Store this value securely.

Step 5: Exchange for a Long-Lived Token

Short-lived tokens expire quickly. Exchange your short-lived token for a long-lived token (60-day validity) using your app credentials:

curl "https://graph.instagram.com/v18.0/oauth/access_token" \
  --data "grant_type=fb_exchange_token" \
  --data "client_id={APP_ID}" \
  --data "client_secret={APP_SECRET}" \
  --data "access_token=SHORT_LIVED_TOKEN"

The response includes a new access_token field with a 60-day expiry. This is your IG_ACCESS_TOKEN.

Infrastructure: Lambda Environment Variables and Configuration

The shipcaptaincrew Lambda function in us-east-1 includes logic that checks for two environment variables before making Graph API calls. These are currently unset, causing the integration to return empty results. Store the values from the token generation process:

  • IG_USER_ID: The Instagram Business Account ID from Step 4
  • IG_ACCESS_TOKEN: The long-lived access token from Step 5

Update the function configuration using the AWS CLI:

aws lambda update-function-configuration \
  --function-name shipcaptaincrew \
  --region us-east-1 \
  --environment "Variables={IG_USER_ID=17841400663118577,IG_ACCESS_TOKEN=your_long_lived_token_here}"

Do not commit these values to version control. Use Secrets Manager or parameter store for production workloads. For development, AWS Lambda's built-in environment variables are acceptable if the function's execution role is restrictive.

The Lambda function queries the Instagram Graph API endpoint:

https://graph.instagram.com/v18.0/{IG_USER_ID}/media?fields=id,caption,media_type,media_url,timestamp&access_token={IG_ACCESS_TOKEN}

Results are filtered by date/time window to match guest photo uploads from the same event day, then merged into the response payload served to shipcaptaincrew.queenofsandiego.com/g/{event_id}.

Token Refresh Strategy

Long-lived tokens expire after 60 days. Implement a monthly refresh by making the same exchange call (Step 5) and updating the Lambda environment variable. For automated refresh, create an EventBridge rule that triggers a custom Lambda function monthly:

  • EventBridge Rule: sailjada-ig-token-refresh (cron: 0 0 1 * ? * = first day of each month)
  • Target: A helper Lambda function that exchanges the current token for a new one and updates the shipcaptaincrew function's environment

Alternatively, implement a check in shipcaptaincrew itself: if API calls begin returning 400 errors (invalid token), trigger a refresh and alert via SNS.

Validation and Testing

After updating Lambda environment variables, navigate to shipcaptaincrew.queenofsandiego.com/g/2026-04-29 (or any past event date) and verify that Instagram posts appear alongside guest photos. Check CloudWatch Logs for the shipcaptaincrew function (log group: /aws/lambda/shipcaptaincrew) to confirm successful API calls and proper filtering logic.

Key Decisions and Rationale

  • Graph API over Basic Display: Graph API provides richer metadata (captions, timestamps, insights) useful for filtering and sorting media chronologically.
  • Long-