```html

Integrating Instagram Graph API with AWS Lambda: Building a Photo Aggregation Layer for Guest Charter Events

What Was Done

We integrated Instagram's Graph API into the shipcaptaincrew Lambda function (us-east-1, account 782785212866) to surface @sailjada Instagram posts alongside user-uploaded guest photos on the event gallery page at shipcaptaincrew.queenofsandiego.com/g/{event_id}. The Lambda previously had dormant Instagram integration that returned empty arrays when credentials were missing. This post documents the step-by-step process to activate that integration and establish a sustainable token refresh strategy.

Technical Details: Why Graph API Over Basic Display

The initial approach attempted to use Instagram's Messaging product for API access, but this was a critical blocker. The Messaging product is designed for direct message handling and doesn't grant the instagram_basic scope required to read media objects. We switched to the dedicated Instagram Graph API product, which provides direct access to media, insights, and account data with the appropriate OAuth scopes.

Why this matters architecturally: OAuth scope mismatches are a common integration failure point. The Graph API product automatically handles the correct scope chain and provides cleaner error messages when permissions are insufficient. It also supports long-lived tokens (60 days) versus short-lived tokens (1 hour), reducing the complexity of a token refresh Lambda.

Step 1: Register the Correct Product

  • Navigate to developers.facebook.com/apps and open the sailjada-social application
  • Click Add Product (bottom of the left sidebar)
  • Search for and select Instagram Graph API (not Basic Display, not Messaging)
  • Complete the setup wizard to add it to your app's product suite

Step 2: Connect the Business Account

Instagram Graph API requires a Business or Creator account, not a personal account. Verify that @sailjada is configured as a Business account and linked to a Facebook Page. Inside the app dashboard:

  • Navigate to Instagram Graph APIAPI Setup with Instagram Login
  • Click Add Instagram Account and authenticate as @sailjada
  • This creates the connection between your app and the Instagram business account

Step 3: Generate a Short-Lived Access Token

The Graph API Explorer provides an interactive way to test calls and generate initial tokens. This is essential before deploying to Lambda:

  • Go to developers.facebook.com/tools/explorer
  • Select sailjada-social from the app dropdown
  • Under "User or Page" select the Facebook Page linked to @sailjada
  • Click Generate Access Token
  • In the scopes dialog, ensure these are selected: instagram_basic, pages_show_list, and instagram_business_manage_messages (if managing DMs)
  • Authorize and copy the resulting token—this is short-lived (1 hour) but sufficient for the next steps

Step 4: Retrieve the Instagram User ID

The Graph API returns nested account references. Extract the Instagram Business Account ID via two API calls:

# Call 1: Get your Facebook Page ID and linked Instagram account
curl -s "https://graph.instagram.com/v18.0/me?fields=instagram_business_account&access_token=TOKEN" | jq '.instagram_business_account.id'

# Call 2: (optional) Verify the account details
curl -s "https://graph.instagram.com/v18.0/INSTAGRAM_BUSINESS_ACCOUNT_ID?fields=id,username&access_token=TOKEN"

The id returned from Call 1 is your IG_USER_ID environment variable. Store this value; it does not change.

Step 5: Exchange Short-Lived Token for Long-Lived Token

Short-lived tokens expire in 1 hour. For production Lambda workloads, a 60-day long-lived token is more practical. Exchange using your app credentials:

curl -s -X GET "https://graph.instagram.com/v18.0/oauth/access_token" \
  -d "grant_type=ig_refresh_token" \
  -d "access_token=SHORT_LIVED_TOKEN" \
  -d "client_secret=APP_SECRET"

The returned access_token is your long-lived token. It expires after 60 days and must be refreshed using the same endpoint.

Infrastructure: Lambda Configuration

Update the shipcaptaincrew Lambda function with environment variables. Use the AWS CLI or Console:

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

Environment variables to set:

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

The Lambda function checks for these variables at startup. If present, it queries the Graph API for media from the @sailjada account within the event's date/time window and merges results with guest-uploaded photos.

Key Architectural Decisions

Long-Lived Tokens Over EventBridge Refresh: A 60-day token lifespan is sufficient for most event galleries. While EventBridge could automate monthly refreshes, it adds operational complexity (new Lambda function, IAM role, CloudWatch logs, error handling). For this use case—guest photos and social media for specific past events—manual quarterly refreshes during deployment windows are acceptable. Document the token expiration date in your deployment notes.

Merging at Query Time: The Lambda doesn't cache Instagram posts to a database. Instead, it fetches media on-demand for each /g/{event_id} request, filters by timestamp, and merges with approved guest uploads in memory. This avoids stale data but adds ~200–500ms latency per request. The tradeoff is justified since event galleries are not high-traffic, and freshness is valuable (new Instagram posts appear immediately).

Approved Photos Only: Guest uploads require explicit approval before display. Instagram posts are auto-approved but scoped to @sailjada only, eliminating spam and maintaining content consistency.

Verification and Testing

After deploying, test the integration:

  • Navigate to shipcaptaincrew.queenofsandiego.com/g/2026-04-29 (or any event with photos and matching Instagram posts from that date)
  • Verify Instagram posts appear alongside guest photos in the gallery
  • Check CloudWatch Logs for the shipcaptaincrew function—look for successful Graph API calls and media counts
  • If no Instagram posts appear, check: token expiration, IG_USER_ID accuracy, and date range filtering

What's Next