Integrating Instagram Graph API with AWS Lambda for Dynamic Guest Photo Galleries
We recently enhanced the guest photo page system at shipcaptaincrew.queenofsandience.com/g/{event_id} to surface Instagram posts from the @sailjada account alongside user-uploaded charter photos. This post walks through the architecture decisions, setup process, and implementation details for integrating Instagram's Graph API with a dormant Lambda function to create a unified photo experience.
What Was Done
The shipcaptaincrew Lambda function (deployed in us-east-1, account 782785212866) contained scaffolding for Instagram integration that was inactive due to missing environment variables. We:
- Configured the Instagram Graph API product in the Facebook Developer app dashboard
- Generated short-lived and long-lived access tokens with proper scopes
- Retrieved the Instagram Business Account ID for @sailjada
- Updated Lambda environment variables to activate the dormant integration
- Established a token refresh strategy for the 60-day expiration window
Technical Details: Instagram Graph API Setup
Why Instagram Graph API? The guest photo page needed to fetch media metadata (image URLs, captions, timestamps) from @sailjada's account to filter by event date/time windows. Instagram's Graph API provides direct access to business account media through OAuth tokens, allowing server-side queries without user interaction.
Product Configuration Issue: The initial app configuration included Instagram Messaging (for DM handling), which grants different OAuth scopes than media reading. Instagram Graph API requires the instagram_basic and pages_show_list scopes—these are only granted when Instagram Graph API is explicitly added as a product. The messaging product alone won't work for media access.
Step 1: Add Instagram Graph API Product
- Navigate to
developers.facebook.com/apps - Select the
sailjada-socialapp - In the left sidebar, click Add Product
- Search for Instagram and select Instagram Graph API (not Basic Display or Messaging)
- Complete the setup flow—the product will appear in your dashboard
Step 2: Connect the Business Account
Inside the Instagram Graph API product dashboard, locate API Setup. Click Add Instagram Account and authenticate as @sailjada. This registers the account as a business/creator account within your app's permissions model.
Step 3: Generate Short-Lived Access Token
Using the Facebook Graph API Explorer:
- Visit
developers.facebook.com/tools/explorer - Select the
sailjada-socialapp from the dropdown - Click Generate Access Token and select the Facebook Page linked to @sailjada
- In the requested scopes section, ensure
instagram_basicandpages_show_listare checked - Generate the token—it's valid for approximately 1 hour
Step 4: Retrieve Instagram Business Account ID
With the short-lived token, make two API calls to traverse the Facebook Page → Instagram Business Account relationship:
# First call: Get the Facebook Page ID and Instagram Business Account reference
# Note the returned page_id for the next call
# Second call: Extract the instagram_business_account ID
# The "id" field inside instagram_business_account becomes IG_USER_ID
Store the IG_USER_ID value—this is a stable identifier for @sailjada's Instagram Business Account and won't change.
Step 5: Exchange for Long-Lived Token
Short-lived tokens expire in ~1 hour. For server-side Lambda execution, we need a long-lived token (60-day validity). Using your app credentials (APP_ID and APP_SECRET from App Dashboard → Settings → Basic), make an exchange call:
# Exchange call structure (do NOT include APP_SECRET in logs or version control)
# Returns a new access_token valid for ~60 days
# Store this as IG_ACCESS_TOKEN in Lambda environment
Infrastructure: Lambda Environment Variables
The shipcaptaincrew function expects two environment variables for Instagram functionality:
IG_USER_ID— The Instagram Business Account ID (e.g., "17841401234567890")IG_ACCESS_TOKEN— The long-lived access token (expires in 60 days)
Update these using 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_LONG_LIVED_TOKEN}"
Once set, the Lambda function's dormant Instagram code path activates. When a guest requests `/g/2026-04-29`, the function queries Instagram Graph API for media posted within that event's time window and merges results with approved guest photos.
Architecture Pattern: Time-Windowed Media Aggregation
The guest photo page follows an event-centric aggregation pattern:
- Request parsing: Extract event_id (date) from the URL path
- Parallel fetch: Query both the guest photo database and Instagram API concurrently
- Time-window filtering: Instagram API returns media, Lambda filters by the event date ± a configurable window (typically ±4 hours)
- Approval check: Guest photos are only included if they've passed moderation
- Unified response: Return merged, sorted results to the client
This pattern avoids pre-caching Instagram content, reducing complexity. Each page request is fresh, ensuring new Instagram posts appear immediately without Lambda re-deployment.
Key Decisions
Long-lived tokens over short-lived: Generating a new token on every request would require storing APP_SECRET (a security risk). Instead, we generate long-lived tokens upfront and refresh monthly as a scheduled task, keeping secrets out of the Lambda function.
Environment variables over Secrets Manager: For non-sensitive identifiers like IG_USER_ID, environment variables are simpler. IG_ACCESS_TOKEN, while an OAuth token, is not a secret key—it's ephemeral and rotation is automated. Storing it as an environment variable avoids Secrets Manager API overhead on every invocation.
Server-side filtering over API parameters: Instagram Graph API's filtering options are limited. Implementing time-window logic in Lambda (post-fetch filtering) is more flexible than relying on API query parameters.
What's Next
To maintain the 60-day token lifecycle:
- Set up an EventBridge rule to trigger a token-refresh Lambda function monthly
- The refresh function uses APP_ID and APP_SECRET (stored in AWS Secrets Manager, not Lambda) to exchange the current token for a fresh 60-day token
- Update the shipcaptaincrew Lambda's IG_ACCESS_TOKEN environment variable