Integrating Instagram Graph API into a Lambda-Based Guest Photo Gallery: Setup and Token Management

Overview

The shipcaptaincrew Lambda function (us-east-1, account 782785212866) powers a guest photo gallery system at shipcaptaincrew.queenofsandiego.com/g/{event_id} that displays approved guest uploads alongside Instagram posts from the @sailjada account. The Instagram integration was built but dormant—returning an empty array when environment variables were missing. This post covers the end-to-end process of activating that integration by properly configuring the Instagram Graph API and managing token lifecycle.

What Was Done

We activated the dormant Instagram integration by:

  • Adding the Instagram Graph API product to the existing sailjada-social Facebook app
  • Authenticating @sailjada as a Business/Creator account connected to its Facebook Page
  • Generating short-lived access tokens via the Graph API Explorer
  • Exchanging those tokens for long-lived credentials (60-day validity)
  • Injecting IG_USER_ID and IG_ACCESS_TOKEN into the Lambda environment
  • Establishing a monthly token refresh cadence to prevent expiration

Technical Details: Product Setup and Authentication

Why We Needed a Fresh Product Configuration

The sailjada-social app initially had only the Messaging use case configured—appropriate for handling direct messages but insufficient for reading media. The Messaging product grants scopes like instagram_manage_messages but explicitly does not include instagram_basic, which is required to query media endpoints. We needed to add Instagram Graph API as a separate product.

Product Addition Steps

Via the Facebook App Dashboard:

  1. Navigate to developers.facebook.com/apps → select sailjada-social
  2. In the left sidebar, locate Add Product (typically near the bottom)
  3. Search for Instagram and select Instagram Graph API (not Basic Display or Messaging)
  4. Confirm the product appears in your left sidebar under Instagram Graph API

Once added, the Instagram Graph API section reveals the API Setup page. Here we connected @sailjada by clicking Add Instagram Account and authenticating as the account owner. This relationship is critical: @sailjada must be a Business or Creator account, and it must be linked to a Facebook Page that your app has permission to manage.

Token Generation via Graph API Explorer

Short-lived tokens (valid ~2 hours) are generated through the Graph API Explorer at developers.facebook.com/tools/explorer:

  1. Select app: sailjada-social
  2. Click Generate Access Token
  3. Select the Facebook Page linked to @sailjada
  4. Under permissions, explicitly request instagram_basic and pages_show_list
  5. Copy the generated token for use in the next steps

Retrieving IG_USER_ID

With a short-lived token in hand, we query the Facebook Graph API to extract the Instagram Business Account ID. Two API calls are required:

# Step 1: Get the Page ID
curl "https://graph.instagram.com/me?fields=id&access_token=TOKEN"

# Step 2: Retrieve the linked Instagram Business Account
curl "https://graph.instagram.com/{PAGE_ID}?fields=instagram_business_account&access_token=TOKEN"

The response contains an instagram_business_account object with an id field—this is IG_USER_ID. Store this value; it remains constant and is used to construct all subsequent media queries.

Token Exchange and Long-Lived Credential Generation

Short-lived tokens expire in ~2 hours, making them unsuitable for Lambda environment variables. Facebook's token exchange endpoint converts them to long-lived tokens (60 days):

curl -X GET \
  "https://graph.instagram.com/access_token?grant_type=ig_exchange_access_token&client_id={APP_ID}&client_secret={APP_SECRET}&access_token={SHORT_LIVED_TOKEN}"

The response includes:

  • access_token: Your long-lived token (60-day validity)
  • token_type: Always "bearer"

This long-lived token becomes IG_ACCESS_TOKEN.

Infrastructure: Lambda Environment Configuration

The shipcaptaincrew Lambda function reads two environment variables at runtime:

  • IG_USER_ID: The Instagram Business Account ID (immutable)
  • IG_ACCESS_TOKEN: The long-lived access token (expires in 60 days)

Update these via the AWS Lambda console or 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_LONG_LIVED_TOKEN}'

Once set, the Lambda function's media-fetching logic activates. Inside the function, it constructs queries like:

https://graph.instagram.com/{IG_USER_ID}/ig_hashtag_search?user_id={IG_USER_ID}&fields=id,name&access_token={IG_ACCESS_TOKEN}

Key Architectural Decisions

Why a 60-Day Token Instead of Indefinite Credentials

Instagram Graph API requires token refresh every 60 days for security. While this adds operational overhead, it prevents a single leaked credential from providing permanent access. We chose not to implement auto-refresh inside Lambda (which would require storing the app secret) but instead use a manual monthly refresh process—lower complexity, acceptable for non-critical integrations.

Separation of IG_USER_ID and IG_ACCESS_TOKEN

The user ID is immutable and can be logged/traced without security risk. The token is sensitive and should be rotated. Storing them as separate environment variables allows us to update only the token without touching application code.

No Local Caching of Media

The current implementation queries Instagram in real-time when a guest views /g/{event_id}. This ensures freshness but introduces latency and API quota consumption. A future optimization could cache results in ElastiCache or DynamoDB, but the current approach keeps the system stateless and reduces operational overhead.

Token Refresh Strategy

At day 50 (before the 60-day expiration), run the token exchange command above and update the Lambda environment variable. This can be automated via:

  • An EventBridge rule triggering a Step Functions workflow monthly