Integrating Instagram Graph API with AWS Lambda: Connecting @sailjada Media to Guest Photo Events

What Was Done

The shipcaptaincrew Lambda function at Ship Captain Crew (a San Diego sailing charter business) needed to pull Instagram posts from the @sailjada account and display them alongside guest-uploaded photos on event pages. The infrastructure already existed to serve guest photos via S3 + CloudFront at shipcaptaincrew.queenofsandiego.com/g/{event_id} (e.g., /g/2026-04-29), but Instagram integration was dormant—the Lambda had skeleton code that returned an empty array because the required environment variables were missing.

This post documents the step-by-step process to properly configure the Instagram Graph API product in a Facebook App, generate long-lived access tokens, and inject them into the Lambda function so it can query media from @sailjada for a given date range.

Technical Details: The Token Generation Pipeline

Why Instagram Graph API, not Basic Display? The Ship Captain Crew use case requires reading media metadata (captions, timestamps, media URLs) from a business account. Basic Display only works for public feeds without timestamps; Graph API gives us the structured data needed to match posts to charter events by date.

Step 1: Product Setup in Facebook App Dashboard

  • Navigate to developers.facebook.com/apps and open the sailjada-social app
  • Click Add Product in the left sidebar
  • Search for Instagram and select Instagram Graph API (not Basic Display or Messaging—messaging won't grant the instagram_basic scope needed for media reads)
  • Complete the setup wizard; the product will appear in the sidebar as Instagram Graph API

Why this matters: The messaging use case was initially added but grants different OAuth scopes (instagram_manage_messages vs. instagram_basic`). Graph API is the correct choice for reading media.

Step 2: Link the Instagram Account

  • In Instagram Graph API → API Setup, click Add Instagram Account
  • Log in with the @sailjada Instagram credentials (must be a Business or Creator account)
  • Authorize the app to access the account
  • Verify that @sailjada is now listed under "Connected Instagram Accounts"

Step 3: Generate Short-Lived Token via Graph API Explorer

  • Go to 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 dropdown
  • In the Scopes section, confirm instagram_basic and pages_show_list are selected
  • Copy the generated token (valid for ~2 hours, sufficient for testing)

Step 4: Retrieve IG_USER_ID

The Lambda needs the numeric Instagram Business Account ID. Query the Graph API with the short-lived token:

curl -s "https://graph.instagram.com/me/instagram_business_accounts?access_token=SHORT_LIVED_TOKEN" | jq '.'

Extract the Facebook Page ID from the response, then query again:

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

The returned id is your IG_USER_ID (e.g., 17841402079XXXXXX).

Step 5: Exchange for Long-Lived Token

Short-lived tokens expire in 2 hours. Exchange it for a 60-day long-lived token using the app credentials (APP_ID and APP_SECRET from App Settings):

curl -s "https://graph.instagram.com/oauth/access_token?grant_type=fb_exchange_token&client_id=APP_ID&client_secret=APP_SECRET&access_token=SHORT_LIVED_TOKEN" | jq '.access_token'

Store this as IG_ACCESS_TOKEN. It's valid for 60 days.

Infrastructure: Lambda Configuration and Secrets Management

The Lambda function shipcaptaincrew (region: us-east-1, account: 782785212866 [redacted in production]) has the following key environment variables:

  • IG_USER_ID – the Instagram Business Account numeric ID
  • IG_ACCESS_TOKEN – the 60-day long-lived access token

Update the function with:

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

Security note: These values are stored in Lambda environment variables, which are encrypted at rest using AWS KMS. In production, consider using AWS Secrets Manager for additional audit logging and rotation policies.

Lambda Code: Media Fetching Logic

The Lambda handler (/Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/lambda_function.py, around lines 1060–1120) contains a function that queries the Instagram Graph API:

  • Constructs a date range filter based on the event ID (e.g., 2026-04-29)
  • Calls https://graph.instagram.com/{IG_USER_ID}/media with IG_ACCESS_TOKEN
  • Filters results by timestamp to match the charter window
  • Returns media metadata (URLs, captions, timestamps) to the frontend

Previously, this function returned [] (empty array) when env vars were missing. Now it will populate actual Instagram media for event pages.

Key Decisions and Rationale

  • Graph API over Basic Display: Allows structured queries by date range; Basic Display only returns the public feed without timestamps.
  • 60-day tokens vs. eternal tokens: Graph API doesn't issue permanent tokens; 60-day expiration is the longest available. Mitigates the risk of a compromised token being valid indefinitely.
  • Monthly token refresh strategy: The same curl exchange call can be run monthly (via cron, EventBridge, or manual trigger) to refresh before expiration. No user re-authentication needed.
  • Environment variables in Lambda: Simpler than Secrets Manager for this low-sensitivity use case; the token is scoped only to read @