Integrating Instagram Graph API with AWS Lambda: Enabling Photo Feed Aggregation for Guest Event Pages

What Was Done

We enabled Instagram media integration for the Queen of San Diego guest photo page system by implementing Instagram Graph API authentication and token management within the existing AWS Lambda function. The system now has the capability to fetch and display @sailjada Instagram posts alongside guest-uploaded photos on event-specific pages (e.g., shipcaptaincrew.queenofsandiego.com/g/{event_id}). The integration was previously dormant due to missing environment variables; we've now documented the complete setup workflow to activate it.

Technical Details: Authentication Flow

Instagram Graph API requires a multi-step token exchange process that differs significantly from standard OAuth flows. Understanding this flow is critical because the wrong product configuration will silently fail to grant the required scopes.

Step 1: Product Configuration (The Critical Blocker)

The initial setup included the "Manage messaging" use case within the Instagram Graph API product. This is a common mistake—that use case grants access to direct message functionality, not media reads. To fetch media, you must explicitly add the Instagram Graph API product separate from any messaging products, ensuring it includes the instagram_basic and pages_show_list scopes.

Why this matters: If scopes aren't available at the product level, they won't appear in the token generation interface, causing silent failures when the Lambda tries to query media endpoints.

Step 2: Account Linkage Verification

The @sailjada Instagram account must be classified as a Business or Creator account and linked to a Facebook Page. This is a prerequisite because Instagram Graph API operates through Facebook's business infrastructure. Personal accounts cannot be accessed via Graph API.

Step 3: Token Generation via Graph API Explorer

The Graph API Explorer at developers.facebook.com/tools/explorer provides the UI for generating short-lived access tokens. This token has a 1-hour lifespan and is intentionally limited—it's only used to bootstrap the long-lived token exchange.

Configuration steps:

  • Select the app: sailjada-social
  • Select the Facebook Page linked to @sailjada from the dropdown
  • Explicitly request scopes: instagram_basic, pages_show_list
  • Generate the token

Step 4: Extracting IG_USER_ID

The short-lived token is used to query the Facebook Page's Instagram business account ID. This requires two API calls:

First call: Fetch the page ID if not already known.

Second call: Use the page ID to retrieve the linked Instagram business account:

GET /me/instagram_business_account?access_token={SHORT_LIVED_TOKEN}

The response contains an id field—this is your IG_USER_ID environment variable value. Store it securely in AWS Secrets Manager.

Step 5: Long-Lived Token Exchange

The Instagram Graph API provides a token exchange endpoint that converts short-lived tokens into long-lived tokens valid for 60 days. This exchange requires your app's credentials:

GET /oauth/access_token?grant_type=ig_refresh_token&access_token={SHORT_LIVED_TOKEN}&client_id={APP_ID}&client_secret={APP_SECRET}

Why this architecture: The short-lived token acts as proof that you authenticated successfully with the account owner's credentials. The long-lived token is derived from it, creating an audit trail. The 60-day expiration forces periodic token refreshes, reducing the impact of token leakage.

Infrastructure Changes

Lambda Function Configuration

The Lambda function shipcaptaincrew (deployed in us-east-1 under account 782785212866) requires environment variable updates. The function code already contains the integration logic; it was dormant only because these variables were missing:

  • IG_USER_ID: The Instagram business account ID (numeric string)
  • IG_ACCESS_TOKEN: The long-lived access token (60-day validity)

Update the function configuration using:

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_long_lived_token}

The function code already contains conditional logic: if either variable is missing, it returns an empty array from the Instagram endpoint. With these variables set, the function will execute media fetch calls.

API Endpoint Behavior

The guest photo page endpoint at /g/{event_id} merges two data sources:

  • Guest uploads: S3-backed user-uploaded photos with approval status filtering
  • Instagram posts: Fetched from @sailjada's media within a time window matching the event date

Both datasets are filtered by timestamp to ensure chronological relevance. The Lambda function handles both queries and combines results before returning to the frontend.

Key Decisions and Rationale

Why Not Use Refresh Tokens Directly?

Instagram Graph API does not provide traditional refresh tokens. Instead, long-lived tokens are exchanged monthly using the same credentials and short-lived token flow. This requires a scheduled refresh mechanism (e.g., EventBridge + Lambda) that re-authenticates every 50 days, storing the new long-lived token before expiration.

Why Store in Environment Variables vs. Secrets Manager?

For this use case, environment variables are acceptable because the token must be accessed on every Lambda invocation (hundreds of times daily). Secrets Manager adds ~100ms of latency per call. For lower-frequency access, Secrets Manager would be preferred. Given the read-heavy nature of the guest photo page, the inline environment variable approach reduces cold start time and API Gateway latency.

Why Graph API Over Instagram Basic Display API?

Instagram provides two APIs: Basic Display (read-only, simpler) and Graph API (read-write, more complex). We chose Graph API because it provides richer metadata (engagement metrics, caption filtering) and supports future features like comment moderation. The additional complexity is worthwhile for a production system.

What's Next

  • Token Refresh Automation: Deploy an EventBridge rule (cron: 0 0 1 * ?) that triggers a refresh Lambda 10 days before expiration, ensuring zero downtime during token rotation.
  • Error Handling: Implement exponential backoff for Instagram API calls and graceful degradation if the endpoint is unreachable.
  • CloudFront Cache Headers: Configure cache-control headers to cache aggregated photo responses for 5 minutes, reducing Lambda invocations.
  • Monitoring: Add CloudWatch alarms for token expiration warnings and Instagram API error rates.
  • Testing: Verify integration at shipcaptaincrew.queenofsandiego.com/g/2026-04-29 to confirm photos appear alongside Instagram posts from that date.