Integrating Instagram Graph API with AWS Lambda: Adding Social Media to Your Guest Photo Gallery

The guest photo page system at shipcaptaincrew.queenofsandiego.com/g/{event_id} had a dormant Instagram integration waiting for activation. This post walks through the exact steps taken to connect the Instagram Graph API to the Lambda function, enabling live @sailjada posts to display alongside guest-uploaded charter photos.

What Was Done

We activated Instagram Graph API integration for the shipcaptaincrew Lambda function (deployed in us-east-1, AWS account 782785212866). The Lambda had placeholder code ready to fetch Instagram media—it just needed two environment variables populated: IG_USER_ID and IG_ACCESS_TOKEN. The challenge: obtaining these values required navigating Facebook's developer platform and understanding the correct product setup.

Technical Details: The Instagram Graph API Setup

Step 1: Adding the Correct Product

This was the critical blocker. The app dashboard already had a "Messaging" product added, which grants DM-related scopes but not the instagram_basic scope needed to read media. The solution:

  • Navigate to developers.facebook.com/apps
  • Select the sailjada-social app
  • Click Add Product in the left sidebar
  • Search for Instagram and select Instagram Graph API (not Basic Display, not Messaging)

Why this matters: Facebook's product suite is granular. The Messaging product is for sending/receiving DMs. We need Graph API to query media metadata—this is a different OAuth scope tree. Adding the wrong product grants insufficient permissions and wastes time debugging scope errors.

Step 2: Verify @sailjada's Account Type

Before proceeding, confirm that @sailjada is a Business or Creator account linked to a Facebook Page. Consumer accounts don't expose media via Graph API. This account was already configured correctly, but it's essential to verify because the Instagram API connection step requires a linked page.

Step 3: Connect the Instagram Account

Inside the Instagram Graph API product settings:

  • Click API setup with Instagram login
  • Click Add Instagram account
  • Log in as @sailjada when prompted

This establishes the OAuth link between the app and the Instagram account, making it available for token generation.

Step 4: Generate a Short-Lived Access Token

Navigate to the Graph API Explorer:

  • Select the sailjada-social app from the dropdown
  • Click Generate Access Token
  • Select the Facebook Page linked to @sailjada
  • In the scopes dialog, request: instagram_basic and pages_show_list

This token is short-lived (valid for ~2 hours) and is used only to bootstrap the process of fetching the IG_USER_ID and exchanging for a long-lived token.

Step 5: Extract IG_USER_ID via Graph API Calls

Two sequential API calls are needed. Using the short-lived token from Step 4, first retrieve your Facebook Page's ID and its linked Instagram Business Account:

curl "https://graph.instagram.com/me/accounts?fields=instagram_business_account&access_token={SHORT_LIVED_TOKEN}"

The response contains an instagram_business_account object with an id field—this is your IG_USER_ID. Save this value; it's immutable and used in all subsequent API calls.

Step 6: Exchange for a Long-Lived Token

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

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

The response includes a new access_token—this is your IG_ACCESS_TOKEN environment variable.

Step 7: Token Refresh Strategy

Long-lived tokens expire after 60 days. Rather than manual refresh, set up a monthly renewal using the same exchange call. You can trigger this via:

  • An EventBridge rule targeting the Lambda monthly
  • A CloudWatch Events cron expression: cron(0 2 1 * ? *) (2 AM UTC on the 1st of each month)
  • A custom Lambda that calls the Graph API and updates IG_ACCESS_TOKEN via Systems Manager Parameter Store

We chose to implement manual refresh as part of the maintenance schedule, but EventBridge automation is the production-ready approach.

Infrastructure Changes

Update the shipcaptaincrew Lambda function with the two environment variables. Using the AWS CLI:

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

Alternatively, via the Lambda console:

  • Open Lambda → Functions → shipcaptaincrew
  • Scroll to Environment variables
  • Add two new entries: IG_USER_ID and IG_ACCESS_TOKEN
  • Click Deploy

Note: The Lambda runtime already contains the boto3 Instagram Graph API client code. Once these variables are present, the function will automatically begin fetching and caching Instagram media on each invocation.

Key Decisions

  • Why Graph API over Basic Display? Graph API provides metadata (caption, likes, timestamp) while Basic Display only returns image URLs. We needed full metadata for filtering posts by date/time window.
  • Why long-lived tokens? Short-lived tokens require manual refresh every 2 hours. Long-lived tokens (60 days) reduce operational overhead and fit a monthly maintenance window.
  • Why store in Lambda environment variables? Environment variables are encrypted at rest in AWS and rotated infrequently. For higher security, migrate to AWS Secrets Manager and update the Lambda IAM role with secretsmanager:GetSecretValue permission.
  • Why the 60-day refresh window? Instagram's token lifecycle policy grants 60-day validity. We refresh monthly (well before expiration) to maintain a safety margin.

Verification

Test the integration by visiting a guest photo page:

https://shipcaptaincrew.queenofsandiego.com/g/2026-04-29

The page should now display both guest-uploaded