Integrating Instagram Graph API with AWS Lambda: Building a Guest Photo Gallery with Social Media Feeds
What Was Done
The shipcaptaincrew application—a guest photo management system deployed at shipcaptaincrew.queenofsandiego.com—required integration with Instagram's Graph API to display curated @sailjada posts alongside guest-uploaded charter photos. The Lambda function had dormant Instagram integration code, but it was returning empty arrays due to missing environment variable configuration. This walkthrough documents the complete setup process: adding the Instagram Graph API product to the Facebook app, obtaining credentials, implementing token exchange for long-lived access, and wiring environment variables into the Lambda function.
Technical Details: Instagram Graph API Setup
Step 1: Adding the Correct Product to the Facebook App
The initial challenge was a product selection error. The app sailjada-social had the Messaging product added, which grants webhooks and direct messaging scopes but not the instagram_basic scope required to read media. The fix involves adding a separate product:
- Navigate to
developers.facebook.com/apps - Open the
sailjada-socialapp configuration - Locate "Add Product" in the left sidebar (near the bottom)
- Search for and select Instagram Graph API (distinct from Basic Display and Messaging)
- Complete setup; the product now appears in the sidebar
Why this matters: Each Facebook product grants a different set of OAuth scopes. The Messaging product is for DM automation; the Graph API product unlocks read access to media metadata, captions, and engagement metrics.
Step 2: Connecting the Instagram Business Account
The @sailjada account must be a Business or Creator profile and linked to a Facebook Page. Within the app:
- Open Instagram Graph API → API setup
- Click Add Instagram account
- Authenticate as
@sailjada(the account owner must authorize) - Confirm the connection to the associated Facebook Page
Step 3: Generating a Short-Lived Access Token
The Graph API Explorer provides an interactive way to test calls and obtain initial tokens:
- Visit
developers.facebook.com/tools/explorer - In the top dropdown, select the
sailjada-socialapp - Click Generate Access Token
- When prompted, select the Facebook Page linked to
@sailjada - In the scopes dialog, ensure
instagram_basicandpages_show_listare checked - Complete authorization; the token appears in the input field (valid for approximately 1–2 hours)
Step 4: Retrieving the Instagram Business Account ID
With the short-lived token, two API calls retrieve the Instagram User ID:
# First call: get the Facebook Page's Instagram business account reference
curl -s "https://graph.instagram.com/v18.0/{PAGE_ID}?fields=instagram_business_account&access_token={SHORT_LIVED_TOKEN}" | jq .
# The response includes an "instagram_business_account" object with an "id" field.
# This ID becomes IG_USER_ID.
# Second call: verify media access with the Instagram User ID
curl -s "https://graph.instagram.com/v18.0/{IG_USER_ID}/media?fields=id,caption,media_type&access_token={SHORT_LIVED_TOKEN}" | jq .
Record the IG_USER_ID value for the next step.
Step 5: Exchanging for a Long-Lived Token
Short-lived tokens expire quickly. Facebook's fb_exchange_token endpoint converts them to long-lived tokens valid for approximately 60 days:
curl -X GET "https://graph.instagram.com/oauth/access_token" \
-d "grant_type=fb_exchange_token" \
-d "client_id={APP_ID}" \
-d "client_secret={APP_SECRET}" \
-d "access_token={SHORT_LIVED_TOKEN}"
The response includes a new access_token field with an extended expiration. This becomes IG_ACCESS_TOKEN in the Lambda environment.
Infrastructure: Lambda Environment Configuration
The Lambda function is deployed in region us-east-1 under account 782785212866:
- Function name:
shipcaptaincrew - Runtime: Python 3.x
- Handler:
lambda_function.lambda_handler - File location (development):
/Users/cb/Documents/repos/sites/queenofsandiego.com/tools/shipcaptaincrew/lambda_function.py
Environment variables are updated via the AWS CLI:
aws lambda update-function-configuration \
--function-name shipcaptaincrew \
--region us-east-1 \
--environment Variables="{IG_USER_ID=,IG_ACCESS_TOKEN=}"
The Lambda code, located in lambda_function.py, contains a function that checks for these variables and constructs a Graph API request:
def fetch_instagram_posts(ig_user_id, ig_access_token, event_date_str):
if not ig_user_id or not ig_access_token:
return []
# Query Instagram media for the date range
url = f"https://graph.instagram.com/v18.0/{ig_user_id}/media"
params = {
"fields": "id,caption,media_type,timestamp",
"access_token": ig_access_token
}
# ... filter by event_date_str and return matching posts
Guest Photo Gallery Endpoint
The application serves guest photo pages at routes like /g/2026-04-29, where the path parameter represents an event date. The Lambda function:
- Receives the event ID (e.g.,
2026-04-29) from the URL path - Queries a database (or file store) for approved guest photos matching that event
- Calls
fetch_instagram_posts()with the same date to retrieve@sailjadaposts from that window - Merges and returns both datasets to the frontend (served by
index.htmlin the same directory)
The HTML front-