```html

Integrating Instagram Graph API with AWS Lambda: Enabling Social Media Photo Feeds on Guest Event Pages

What Was Done

We activated Instagram Graph API integration for the shipcaptaincrew Lambda function to display @sailjada Instagram posts alongside guest-uploaded charter photos on event-specific pages (e.g., shipcaptaincrew.queenofsandiego.com/g/{event_id}). The infrastructure was already in place but dormant—the Lambda was returning empty arrays when Instagram credentials were missing. This post walks through the exact steps to configure Instagram Graph API, generate long-lived access tokens, and deploy them to production.

Technical Details: Instagram Graph API Setup

Product Configuration in App Dashboard

The critical first step is adding the correct product to your Meta app. Navigate to developers.facebook.com/apps, select the sailjada-social app, and click Add Product in the left sidebar. Search for Instagram and select Instagram Graph API—not Basic Display, not Messaging. The Messaging product (used for Direct Messages) does not grant the instagram_basic scope required to read media metadata.

Why this matters: Instagram Graph API requires specific OAuth scopes. Without the Graph API product properly configured, the token generation flow will fail or grant insufficient permissions, and subsequent API calls to fetch media will return 403 Forbidden errors.

Account Linking and Token Generation

Once the Instagram Graph API product is active, navigate to API Setup within that product and click Add Instagram Account. Log in with the @sailjada credentials. This links the Business or Creator account to your app.

To generate a short-lived access token (valid for ~1 hour), use the Graph API Explorer tool:

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

This token is a temporary credential used only to bootstrap the process of obtaining your long-lived token.

Retrieving IG_USER_ID

With your short-lived token, execute two sequential API calls to retrieve the Instagram User ID:

# First: Get your Facebook Page ID and Instagram Business Account
curl -s "https://graph.instagram.com/me/accounts?access_token=SHORT_LIVED_TOKEN" | jq .

# Response includes page_id. Then:
curl -s "https://graph.instagram.com/{page_id}?fields=instagram_business_account&access_token=SHORT_LIVED_TOKEN" | jq .

# Extract the "id" field from instagram_business_account—this is IG_USER_ID

Store the IG_USER_ID value; you'll need it in the next step.

Token Exchange for Long-Lived Credentials

Exchange your short-lived token for a long-lived token (60-day expiration) using your app credentials:

curl -s "https://graph.instagram.com/oauth/access_token?grant_type=ig_refresh_token&access_token=SHORT_LIVED_TOKEN&client_id=YOUR_APP_ID&client_secret=YOUR_APP_SECRET" | jq .

The response includes a new access_token field—this is your IG_ACCESS_TOKEN (valid for 60 days). Store this securely.

Why a separate exchange call? Facebook's OAuth design separates token generations: short-lived tokens are tied to user sessions (Graph API Explorer sessions); long-lived tokens are suitable for server-to-server communication and can be refreshed without user interaction. This architecture allows your Lambda to maintain continuous access across the 60-day window.

Infrastructure: Lambda Environment Variables

The shipcaptaincrew Lambda function (region: us-east-1, account: 782785212866) reads two environment variables at startup:

  • IG_USER_ID—the Instagram Business Account ID from Step 4
  • IG_ACCESS_TOKEN—the long-lived token from Step 5

When both are present, the Lambda function's Instagram integration code path becomes active. When either is missing, it gracefully returns an empty array (existing behavior in dormant mode).

To deploy these variables, use the AWS CLI:

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: Store both values in AWS Secrets Manager or Parameter Store in production. Use IAM roles to grant the Lambda execution role read access to these secrets, then retrieve them at runtime rather than hardcoding in environment variables.

Lambda Code Path: How the Integration Works

Inside the shipcaptaincrew handler, when a request arrives for /g/{event_id}, the function:

  1. Parses the event date from the URL path
  2. Queries the guest photo database (DynamoDB or S3 manifest) for approved photos from that date
  3. If IG_USER_ID and IG_ACCESS_TOKEN are set, calls Instagram Graph API to fetch recent media from @sailjada within a time window matching the event
  4. Merges both datasets and returns a combined JSON response
  5. The client-side template renders all photos in chronological order

The key API call to Instagram Graph API is:

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

This returns an array of media objects. The Lambda filters by timestamp to match the event window.

Key Decisions and Trade-offs

Long-lived tokens vs. short-lived: We chose 60-day tokens because the Lambda runs autonomously without user interaction. A short-lived token + refresh flow would require either a background refresh job or a scheduled task. The 60-day window is a reasonable compromise; token refresh is a manual process currently, but can be automated via EventBridge in the future.

No caching layer: The current design calls Instagram Graph API on every request to /g/{event_id}. For high-traffic events, consider adding CloudFront caching (5–10 minute TTL) or an in-Lambda LRU cache of recent queries. This reduces Instagram API rate-limit exposure.

Graceful degradation: If the Instagram token expires or API calls fail, the endpoint still returns approved guest photos. Instagram posts are a feature enhancement, not core functionality.

Verification and Testing

After deploying the environment variables, test the integration by navigating to a recent event