Integrating Instagram Graph API with Lambda: Connecting @sailjada Social Media to Guest Photo Pages

The guest photo page system at shipcaptaincrew.queenofsandiego.com/g/{event_id} was displaying approved guest-uploaded charter photos but leaving Instagram content on the table. The Instagram Graph API integration logic existed in the Lambda function but remained dormant—returning empty arrays whenever the IG_USER_ID and IG_ACCESS_TOKEN environment variables were missing. This post documents the end-to-end process for activating that integration by properly configuring the Instagram Graph API product, obtaining credentials, and deploying them to the Lambda environment.

The Architecture Problem

The existing Lambda function shipcaptaincrew (deployed in us-east-1, account 782785212866) had dormant code that would fetch Instagram posts from @sailjada filtered by event date/time windows. However, three blockers prevented activation:

  • The Instagram app (sailjada-social) had the wrong product configured—Messaging instead of Graph API
  • No short-lived or long-lived access tokens existed for the @sailjada business account
  • The Lambda environment lacked IG_USER_ID and IG_ACCESS_TOKEN configuration

The Messaging product was a red herring: it grants scopes for managing direct messages, not reading media. The correct product is Instagram Graph API, which provides the instagram_basic and pages_show_list scopes needed to query media endpoints.

Step 1: Correct Product Configuration in the Facebook App Dashboard

Navigate to developers.facebook.com/apps and open the sailjada-social app. In the left sidebar near the bottom, click Add Product. The product list will appear; select Instagram Graph API (not Basic Display, not Messaging). This product type is mandatory because only Graph API provides the scopes required to read business account media, insights, and metadata.

Why this matters: The Graph API is the unified endpoint for Instagram business/creator accounts. It's versioned (currently v18+), well-documented, and supports queries like retrieving media by hashtag, caption search, and timestamp filtering—all critical for matching event photos to date ranges.

Step 2 & 3: Business Account Connection and Token Generation

After adding the product, the Instagram Graph API section appears in the sidebar. Under API setup with Instagram login, click Add Instagram account and authenticate as @sailjada. The account must be a Business or Creator account (not a personal account) and should already be connected to a Facebook Page that you administer. This connection is the prerequisite for generating tokens with the necessary permissions.

With the account connected, generate a short-lived access token using the Graph API Explorer:

  • Select the app: sailjada-social
  • Generate Access Token
  • Select the Facebook Page linked to @sailjada
  • Request scopes: instagram_basic, pages_show_list

The short-lived token is valid for roughly 1–2 hours and is used only to bootstrap credential exchange.

Step 4 & 5: Extracting IG_USER_ID from the Facebook Page

With the short-lived token, make two API calls to traverse from Facebook Page to Instagram Business Account:

# First call: Get the Page and its connected Instagram account
curl -X GET "https://graph.instagram.com/v18.0/{PAGE_ID}?fields=instagram_business_account&access_token={SHORT_LIVED_TOKEN}"

# Response will include:
# "instagram_business_account": { "id": "..." }

# Second call: Extract the IG_USER_ID and confirm fields
curl -X GET "https://graph.instagram.com/v18.0/{IG_USER_ID}?fields=id,username,name&access_token={SHORT_LIVED_TOKEN}"

The IG_USER_ID is the numeric ID returned in the first call's instagram_business_account.id field. Save this value—it's required for all subsequent media queries and won't change.

Step 6: Exchange for Long-Lived Token

Short-lived tokens expire quickly. Exchange the short-lived token for a long-lived token (valid ~60 days) using your app credentials:

curl -X GET "https://graph.instagram.com/v18.0/access_token" \
  -d "grant_type=ig_exchange_token" \
  -d "client_id={APP_ID}" \
  -d "client_secret={APP_SECRET}" \
  -d "access_token={SHORT_LIVED_TOKEN}"

# Response: { "access_token": "...", "token_type": "bearer" }

The returned access_token is your IG_ACCESS_TOKEN. This token is durable and can be refreshed monthly using the same exchange call before expiration.

Step 7: Lambda Environment Configuration

Update the shipcaptaincrew Lambda function with the two credentials. In the AWS Lambda console (us-east-1), navigate to the function and edit its environment variables:

  • IG_USER_ID: The numeric ID extracted in Step 5
  • IG_ACCESS_TOKEN: The long-lived token from Step 6

Use the AWS CLI for programmatic updates:

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

The Lambda code checks for these variables at runtime. If present, it queries the Instagram Graph API using the user ID and token; if absent, it returns an empty array (the previous fallback behavior).

Step 8: Verification and Testing

Once the environment variables are set, the dormant Instagram integration activates automatically on the next Lambda invocation. Test by visiting a guest photo page with a known event date, e.g., shipcaptaincrew.queenofsandiego.com/g/2026-04-29. The page should now display Instagram posts from @sailjada alongside guest uploads from that date. CloudWatch Logs for the function will show successful API responses if queries execute properly.

Key Design Decisions

Why Graph API over Basic Display: Basic Display is read-only and limited to recent media. Graph API provides filtering, search, and insights—necessary for matching posts to specific event dates and times.

60-day token refresh strategy: Rather than implementing complex refresh logic in Lambda, a monthly cronjob (EventBridge rule triggering an SNS notification or Lambda invocation) reminds the team to refresh the token using the same exchange call. This manual-but-scheduled approach avoids token rotation complexity while maintaining availability.

Environment variables over hardcoding: Storing credentials in Lambda environment variables (encrypted at rest by AWS KMS) decouples secrets from code and enables zero-downtime credential rotation.

What's