Integrating Instagram Graph API with AWS Lambda: Enabling Social Photo Display in Guest Event Pages

Overview: What Was Done

We activated dormant Instagram integration in the shipcaptaincrew Lambda function to display @sailjada Instagram posts alongside guest-uploaded charter photos on event-specific gallery pages. The integration reads from Instagram's Graph API and merges results with locally-stored guest photos, providing a unified timeline view at URLs like shipcaptaincrew.queenofsandiego.com/g/{event_id}.

Problem: The Lambda function contained conditional logic that returned an empty array when IG_USER_ID and IG_ACCESS_TOKEN environment variables were missing. The Instagram integration existed but was never wired up to the Facebook app infrastructure.

Solution: Configure the Instagram Graph API product in the Facebook Developer app, generate long-lived access tokens with appropriate scopes, and populate Lambda environment variables to enable token-based requests to Instagram's media endpoints.

Technical Architecture

Current Lambda Function Structure

The shipcaptaincrew Lambda function (AWS account 782785212866, region us-east-1) uses the following conditional pattern:

if (IG_USER_ID && IG_ACCESS_TOKEN) {
  // Query Instagram Graph API for media
  // Filter by event date/time window
  // Merge with guest photos array
} else {
  // Return guest photos only (current behavior)
}

The function expects two environment variables:

  • IG_USER_ID – The Instagram Business Account ID (numeric string)
  • IG_ACCESS_TOKEN – Long-lived access token valid for ~60 days

Instagram Graph API Integration Points

The Lambda makes authenticated requests to Instagram's Graph API endpoint:

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

Results are filtered server-side by comparing the post's timestamp field against the event's date and a configurable time window (typically ±4 hours from event start).

Step-by-Step Implementation: Facebook App Configuration

Step 1: Add Instagram Graph API Product (Critical First Step)

Why this matters: The app previously had a "Messaging" use case added, which grants instagram_manage_messages scope for DM automation—not media reading. Instagram Graph API must be added as a separate product to grant instagram_basic scope.

  • Navigate to developers.facebook.com/apps
  • Open app sailjada-social
  • In the left sidebar, locate and click Add Product (typically near the bottom of the product list)
  • Search for Instagram and select Instagram Graph API (not "Basic Display" or "Messaging")
  • Complete setup; Instagram Graph API will appear as a new sidebar item

Step 2: Connect @sailjada Instagram Account

The @sailjada account must be classified as a Business or Creator account and linked to a Facebook Page.

  • In Instagram Graph API settings, click API Setup
  • Select Add Instagram Account
  • Authenticate using @sailjada's credentials
  • Authorize the app to read media and insights

Step 3: Generate Short-Lived Access Token

Using the Graph API Explorer tool ensures token generation with correct scopes:

  • Visit developers.facebook.com/tools/explorer
  • Select app sailjada-social from the top dropdown
  • Click Generate Access Token
  • Select the Facebook Page linked to @sailjada
  • Add scopes: instagram_basic and pages_show_list
  • Copy the generated token (valid for ~2 hours)

Step 4: Retrieve IG_USER_ID via API Calls

Use the short-lived token to query the Facebook Page's linked Instagram Business Account:

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

The response contains instagram_business_account.id—this is your IG_USER_ID.

Step 5: Exchange for Long-Lived Token

Why long-lived tokens: Short-lived tokens expire in ~2 hours. Exchanging them for long-lived tokens (60-day validity) eliminates the need for hourly token refresh in Lambda.

curl -s "https://graph.instagram.com/access_token?grant_type=ig_refresh_token&access_token={SHORT_LIVED_TOKEN}"

The response access_token field is your IG_ACCESS_TOKEN. Store this securely (see Infrastructure section below).

Infrastructure: AWS Secrets and Environment Configuration

Lambda Environment Variables

Update the shipcaptaincrew Lambda function configuration via AWS CLI:

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

Alternative (recommended for secrets): Store tokens in AWS Secrets Manager and reference them via Lambda execution role permissions, rather than exposing them as plaintext environment variables.

Token Refresh Strategy

Instagram long-lived tokens expire after 60 days. Implement one of two refresh strategies:

  • Manual refresh: Re-run the token exchange curl command monthly and update Lambda environment variables via AWS CLI
  • Automated refresh (optional): Create an EventBridge rule that triggers a helper Lambda monthly to exchange the token and update Secrets Manager

Key Decisions and Rationale

  • Long-lived tokens over short-lived: Reduces API call overhead and complexity. Lambda doesn't need to re-authenticate on every invocation.
  • Server-side filtering by timestamp: Instagram API doesn't offer date-range filters, so filtering occurs in Lambda after retrieving media. This is more efficient than paginating through months of posts.
  • Separate Graph API product: Ensures correct OAuth scopes are granted. Messaging and media reading require different permission sets.
  • Facebook Page intermediary: Instagram Business Accounts must be linked to a Facebook Page to use Graph API. This is a required architecture constraint from Meta.

Verification and Testing