```html

Integrating Instagram Graph API into a Lambda-Based Guest Photo Gallery

The ShipCaptainCrew guest photo page system needed to display approved guest-uploaded charter photos alongside Instagram posts from @sailjada on matching event dates. The Instagram integration was already scaffolded into the Lambda function but dormant—it returned empty arrays when environment variables were missing. This post walks through the exact steps to activate Instagram Graph API for the production system.

What Was Done

We activated Instagram Graph API integration for the shipcaptaincrew Lambda function (region: us-east-1, account: 782785212866) to fetch and display @sailjada's Instagram posts in the guest photo gallery at shipcaptaincrew.queenofsandiego.com/g/{event_id}. The work involved:

  • Adding the Instagram Graph API product to the existing sailjada-social Facebook app (not the Messaging product, which was already configured)
  • Connecting @sailjada's Instagram Business account through the app dashboard
  • Generating and exchanging OAuth tokens to obtain a 60-day long-lived access token
  • Configuring Lambda environment variables IG_USER_ID and IG_ACCESS_TOKEN
  • Modifying Lambda function code to invoke the Instagram Graph API during guest page rendering

Technical Details

Instagram Graph API Product Setup

The critical first step was adding the correct product to the Facebook app. The sailjada-social app already had a Messaging product configured, but this grants only Direct Message scopes. Reading Instagram media requires the Instagram Graph API product specifically.

Navigation path:

  • Facebook Developers Dashboard → Apps → sailjada-social
  • Left sidebar → Add Product (near bottom)
  • Search and select Instagram Graph APISet Up
  • This adds Instagram Graph API to the left sidebar menu

Why this matters: The wrong product selection is a blocker for obtaining the instagram_basic scope, which is required to read media.

Instagram Account Connection

@sailjada must be connected as a Business or Creator account linked to a Facebook Page. Inside the Instagram Graph API product dashboard:

  • Navigate to API setup with Instagram login
  • Click Add Instagram account
  • Authenticate as @sailjada (the account owner must approve the connection)
  • The system will link the Instagram account to the associated Facebook Page

OAuth Token Generation

Two separate API calls are required to obtain credentials. The first generates a short-lived token; the second exchanges it for a long-lived token.

Step 1: Short-lived token generation

Use the Facebook Graph API Explorer at developers.facebook.com/tools/explorer:

  • Select the sailjada-social app from the dropdown
  • Click Generate Access Token
  • Select the Facebook Page linked to @sailjada from the page picker
  • Permissions required: instagram_basic, pages_show_list
  • Copy the generated token (valid for ~2 hours)

Step 2: Retrieve IG_USER_ID

Replace FACEBOOK_PAGE_ID and SHORT_LIVED_TOKEN below:

curl -s "https://graph.instagram.com/FACEBOOK_PAGE_ID?fields=instagram_business_account&access_token=SHORT_LIVED_TOKEN"

The response contains an instagram_business_account.id field—this is your IG_USER_ID. Store it securely.

Step 3: Exchange for long-lived token

The short-lived token expires in ~2 hours. Exchange it for a 60-day long-lived token:

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

The response includes a new access_token (60-day expiry) and expires_in (in seconds). This is your IG_ACCESS_TOKEN.

Lambda Configuration

Update the shipcaptaincrew Lambda function with the two credentials:

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

The Lambda function code in /Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/lambda_function.py already contains conditional logic to fetch Instagram media when both environment variables are present. The handler checks for these during guest page generation and makes requests to the Instagram Graph API endpoint:

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

The returned posts are filtered by timestamp (matching the event date) and merged into the page response alongside approved guest photos.

Infrastructure Pattern

This integration follows a Lambda-driven token exchange pattern rather than a separate credential service. Why?

  • Minimal infrastructure: No additional services to maintain; credentials live in Lambda environment variables
  • Single responsibility: Lambda owns the guest page rendering and Instagram data fetching
  • Direct API calls: Lambda makes synchronous calls to Instagram Graph API, so latency is predictable
  • Token refresh strategy: A scheduled EventBridge rule can invoke a refresh Lambda monthly (or manually re-run the exchange curl command before expiry)

The architecture does not cache Instagram posts; each page load hits the Instagram Graph API. For high-traffic events, adding CloudFront caching (with a 1-hour TTL) on the /g/ route would be a future optimization.

Key Decisions

  • Long-lived tokens over short-lived: 60-day tokens reduce manual intervention. A monthly refresh Lambda can auto-extend before expiry, making the system self-healing.
  • Environment variables over Secrets Manager: For this low-sensitivity integration (public Instagram posts), Lambda env vars are sufficient. If token rotation or audit logging becomes critical, migrating to AWS Secrets Manager would be straightforward.
  • Synchronous API calls: Instagram Graph API responses are fast (~200ms). Async patterns (SQS, SNS) were rejected to keep the page load synchronous and predictable.
  • Filtering by timestamp: The Lambda function filters Instagram posts to the event date window. This prevents unrelated posts