Integrating Instagram Graph API with AWS Lambda: Connecting Guest Photo Galleries to Social Media
Overview
The Ship Captain Crew guest photo gallery system needed to display Instagram posts from @sailjada alongside user-uploaded charter photos. While the Lambda function had dormant Instagram integration code, the Facebook App lacked proper Graph API configuration and credential management. This post walks through the exact steps taken to activate Instagram media retrieval, including product setup, token generation, and Lambda environment variable configuration.
What Was Done
Three core changes were made to enable Instagram integration:
- Added Instagram Graph API product to the Facebook app
sailjada-social(previously only Messaging was configured) - Generated and exchanged access tokens using the Graph API Explorer with proper scopes
- Updated the Lambda function environment variables
IG_USER_IDandIG_ACCESS_TOKENto activate dormant code inlambda_function.py
Technical Details: Instagram Graph API Setup
Step 1: Adding the Correct Product to the Facebook App
The initial blocker: the sailjada-social app had "Instagram Messaging" added, which grants DM-related scopes but NOT instagram_basic (required to read media). The solution required adding a separate product:
- Navigate to
developers.facebook.com/apps→ selectsailjada-social - Left sidebar → Add Product → search for "Instagram"
- Select Instagram Graph API (distinct from Basic Display and Messaging)
- Complete setup wizard, which adds the product to your app's available APIs
Why this matters: Each Facebook product grants different OAuth scopes. Instagram Graph API specifically provides instagram_basic, pages_show_list, and media-reading capabilities. Messaging is for conversation threads, not image galleries.
Step 2: Account Connection and Token Generation
Once the product was added, the next step connected the actual Instagram business account:
- Inside the Instagram Graph API product settings → API setup with Instagram login
- Click Add Instagram account → authenticate as @sailjada
- Confirm the account is linked to a Facebook Business Page (required for media APIs)
After account connection, a short-lived access token was generated via the Graph API Explorer:
https://developers.facebook.com/tools/explorer
- Select app: sailjada-social
- Select access token type: User
- Scopes: instagram_basic, pages_show_list
- Generate Token
Step 3: Retrieving IG_USER_ID
The Instagram Graph API requires both a user ID (IGID format, ~17 digits) and an access token. The user ID was retrieved via two chained API calls:
# First, get the Facebook Page ID linked to @sailjada
curl -s "https://graph.instagram.com/me?fields=id,username&access_token=YOUR_TOKEN" \
| jq '.id'
# Then, fetch the Page's linked Instagram business account
curl -s "https://graph.instagram.com/PAGE_ID?fields=instagram_business_account&access_token=YOUR_TOKEN" \
| jq '.instagram_business_account.id'
The returned ID becomes IG_USER_ID (stored as Lambda environment variable).
Step 4: Exchanging for Long-Lived Token
The Graph API Explorer generates short-lived tokens (1 hour). For production use, a 60-day long-lived token was generated:
curl -s "https://graph.instagram.com/oauth/access_token" \
-X POST \
-d "grant_type=fb_exchange_token" \
-d "client_id=YOUR_APP_ID" \
-d "client_secret=YOUR_APP_SECRET" \
-d "access_token=SHORT_LIVED_TOKEN" \
| jq '.access_token'
This returned token is valid for 60 days and is used as IG_ACCESS_TOKEN in Lambda.
Infrastructure and Lambda Configuration
Lambda Function Details
File: /Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/lambda_function.py
The Lambda function (runtime: Python 3.11+, region: us-east-1, account: 782785212866) contains dormant Instagram code that checks for these environment variables:
IG_USER_ID: Instagram business account ID (required to query media)IG_ACCESS_TOKEN: Long-lived Graph API token (permissions: instagram_basic, media access)
When both variables are set, the Lambda function queries Instagram's /ig_hashtag_search endpoint filtered by event date/time window, enriching the guest photo gallery with official charter Instagram posts.
Environment Variable Update
Configuration was applied via 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_ACCESS_TOKEN}
Verification
The integration was tested by accessing the guest photo page at:
https://shipcaptaincrew.queenofsandiego.com/g/2026-04-29
AWS CloudWatch logs for the Lambda function verified successful API calls:
aws logs tail /aws/lambda/shipcaptaincrew --region us-east-1 --follow
Key Architectural Decisions
Why Long-Lived Tokens Instead of App-to-App Flows
Instagram Graph API supports two token types: user tokens (60-day refresh required) and app tokens (non-expiring but read-only). User tokens were chosen because they grant full instagram_basic scope, necessary for querying the @sailjada business account's media. This requires monthly token refresh but enables future features like insights and video metadata.
Token Refresh Strategy
The 60-day window requires a refresh mechanism. Two approaches were considered:
- Manual refresh (chosen initially): Operator runs the exchange call monthly, updates Lambda env vars. Simple, reduces complexity.
- EventBridge automation (future): CloudWatch Events trigger a Lambda that auto-refreshes tokens. Would require secure storage (Secrets Manager) and adds operational overhead.
For now, manual refresh via Lambda update command is sufficient given the low-frequency nature of charter events.
Scope Minimization
Only two OAuth scopes were requested: instagram_basic (read media metadata) and pages_show_list (enumerate linked Pages). No access to DMs, insights, or shop features was granted, following the principle of least privilege