Integrating Instagram Graph API with AWS Lambda: Connecting @sailjada Media to Guest Photo Events
What Was Done
The shipcaptaincrew Lambda function at Ship Captain Crew (a San Diego sailing charter business) needed to pull Instagram posts from the @sailjada account and display them alongside guest-uploaded photos on event pages. The infrastructure already existed to serve guest photos via S3 + CloudFront at shipcaptaincrew.queenofsandiego.com/g/{event_id} (e.g., /g/2026-04-29), but Instagram integration was dormant—the Lambda had skeleton code that returned an empty array because the required environment variables were missing.
This post documents the step-by-step process to properly configure the Instagram Graph API product in a Facebook App, generate long-lived access tokens, and inject them into the Lambda function so it can query media from @sailjada for a given date range.
Technical Details: The Token Generation Pipeline
Why Instagram Graph API, not Basic Display? The Ship Captain Crew use case requires reading media metadata (captions, timestamps, media URLs) from a business account. Basic Display only works for public feeds without timestamps; Graph API gives us the structured data needed to match posts to charter events by date.
Step 1: Product Setup in Facebook App Dashboard
- Navigate to
developers.facebook.com/appsand open thesailjada-socialapp - Click Add Product in the left sidebar
- Search for Instagram and select Instagram Graph API (not Basic Display or Messaging—messaging won't grant the
instagram_basicscope needed for media reads) - Complete the setup wizard; the product will appear in the sidebar as Instagram Graph API
Why this matters: The messaging use case was initially added but grants different OAuth scopes (instagram_manage_messages vs. instagram_basic`). Graph API is the correct choice for reading media.
Step 2: Link the Instagram Account
- In Instagram Graph API → API Setup, click Add Instagram Account
- Log in with the @sailjada Instagram credentials (must be a Business or Creator account)
- Authorize the app to access the account
- Verify that @sailjada is now listed under "Connected Instagram Accounts"
Step 3: Generate Short-Lived Token via Graph API Explorer
- Go to
developers.facebook.com/tools/explorer - Select
sailjada-socialfrom the app dropdown - Click Generate Access Token
- Select the Facebook Page linked to @sailjada from the dropdown
- In the Scopes section, confirm
instagram_basicandpages_show_listare selected - Copy the generated token (valid for ~2 hours, sufficient for testing)
Step 4: Retrieve IG_USER_ID
The Lambda needs the numeric Instagram Business Account ID. Query the Graph API with the short-lived token:
curl -s "https://graph.instagram.com/me/instagram_business_accounts?access_token=SHORT_LIVED_TOKEN" | jq '.'
Extract the Facebook Page ID from the response, then query again:
curl -s "https://graph.instagram.com/PAGE_ID?fields=instagram_business_account&access_token=SHORT_LIVED_TOKEN" | jq '.instagram_business_account.id'
The returned id is your IG_USER_ID (e.g., 17841402079XXXXXX).
Step 5: Exchange for Long-Lived Token
Short-lived tokens expire in 2 hours. Exchange it for a 60-day long-lived token using the app credentials (APP_ID and APP_SECRET from App Settings):
curl -s "https://graph.instagram.com/oauth/access_token?grant_type=fb_exchange_token&client_id=APP_ID&client_secret=APP_SECRET&access_token=SHORT_LIVED_TOKEN" | jq '.access_token'
Store this as IG_ACCESS_TOKEN. It's valid for 60 days.
Infrastructure: Lambda Configuration and Secrets Management
The Lambda function shipcaptaincrew (region: us-east-1, account: 782785212866 [redacted in production]) has the following key environment variables:
IG_USER_ID– the Instagram Business Account numeric IDIG_ACCESS_TOKEN– the 60-day long-lived access token
Update the function with:
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: These values are stored in Lambda environment variables, which are encrypted at rest using AWS KMS. In production, consider using AWS Secrets Manager for additional audit logging and rotation policies.
Lambda Code: Media Fetching Logic
The Lambda handler (/Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/lambda_function.py, around lines 1060–1120) contains a function that queries the Instagram Graph API:
- Constructs a date range filter based on the event ID (e.g.,
2026-04-29) - Calls
https://graph.instagram.com/{IG_USER_ID}/mediawithIG_ACCESS_TOKEN - Filters results by timestamp to match the charter window
- Returns media metadata (URLs, captions, timestamps) to the frontend
Previously, this function returned [] (empty array) when env vars were missing. Now it will populate actual Instagram media for event pages.
Key Decisions and Rationale
- Graph API over Basic Display: Allows structured queries by date range; Basic Display only returns the public feed without timestamps.
- 60-day tokens vs. eternal tokens: Graph API doesn't issue permanent tokens; 60-day expiration is the longest available. Mitigates the risk of a compromised token being valid indefinitely.
- Monthly token refresh strategy: The same
curlexchange call can be run monthly (via cron, EventBridge, or manual trigger) to refresh before expiration. No user re-authentication needed. - Environment variables in Lambda: Simpler than Secrets Manager for this low-sensitivity use case; the token is scoped only to read @