Integrating Instagram Graph API with AWS Lambda for Dynamic Photo Gallery Display

We recently activated Instagram Graph API integration for the guest photo page system at shipcaptaincrew.queenofsandiego.com/g/{event_id}, enabling the Lambda function to fetch and display @sailjada Instagram posts alongside guest-uploaded charter photos from matching time windows. This post documents the technical implementation, the infrastructure decisions made, and the key challenges encountered during setup.

What Was Done

The shipcaptaincrew Lambda function (us-east-1, account 782785212866) previously had Instagram integration code in place but dormant—it returned empty arrays whenever the IG_USER_ID or IG_ACCESS_TOKEN environment variables were missing. Our goal was to activate this integration by obtaining valid long-lived credentials and configuring the Lambda to use them.

The implementation involved three primary steps:

  • Adding the correct Instagram Graph API product to the existing sailjada-social app in the Facebook Developer Dashboard
  • Generating a short-lived access token via the Graph API Explorer with appropriate scopes
  • Exchanging that token for a long-lived token (60-day validity) and storing it as a Lambda environment variable

Technical Details: The Product Setup Gotcha

The critical blocker we encountered was product selection. The app had previously been configured with the "Messaging" use case, which grants DM-related permissions but explicitly does not grant the instagram_basic scope required to read media metadata. This is a common mistake when initially exploring Instagram integrations on Facebook's platform.

Solution: We navigated to developers.facebook.com/apps, selected the sailjada-social app, clicked "Add Product," and explicitly selected Instagram Graph API (not Basic Display, not Messaging). This product configuration grants access to the media endpoints we need.

Once the product was added to the app dashboard, we verified that the @sailjada account was linked as a Business/Creator account connected to a Facebook Page. This connection is mandatory—the Graph API cannot directly read from personal Instagram accounts.

Token Generation Workflow

Generating credentials involved a two-phase token exchange:

Phase 1: Short-Lived Token (1-2 hours)

We used the Facebook Graph API Explorer (developers.facebook.com/tools/explorer) to generate a short-lived access token with the following process:

  1. Selected the sailjada-social app from the explorer dropdown
  2. Generated a new access token
  3. Selected the Facebook Page linked to @sailjada
  4. Requested scopes: instagram_basic and pages_show_list

With this token, we made two Graph API calls to retrieve the Instagram business account ID:

# First call: Get the Instagram business account from the Facebook Page
curl -s "https://graph.facebook.com/v18.0/{FB_PAGE_ID}?fields=instagram_business_account&access_token={SHORT_LIVED_TOKEN}"

# Response structure:
# {
#   "instagram_business_account": {
#     "id": "{IG_USER_ID}"
#   }
# }

The id field in the nested instagram_business_account object became our IG_USER_ID environment variable.

Phase 2: Long-Lived Token (60 days)

The short-lived token expires within hours, so we exchanged it for a long-lived token valid for approximately 60 days:

curl -s "https://graph.facebook.com/oauth/access_token" \
  -d "grant_type=fb_exchange_token" \
  -d "client_id={APP_ID}" \
  -d "client_secret={APP_SECRET}" \
  -d "fb_access_token={SHORT_LIVED_TOKEN}"

# Response:
# {
#   "access_token": "{LONG_LIVED_TOKEN}",
#   "token_type": "bearer",
#   "expires_in": 5184000
# }

The returned access_token became our IG_ACCESS_TOKEN environment variable. This token is valid for 60 days and can be refreshed using the same exchange endpoint without requiring re-authentication.

Infrastructure: Lambda Configuration

Once credentials were in hand, we updated the Lambda function configuration using the AWS CLI:

aws lambda update-function-configuration \
  --function-name shipcaptaincrew \
  --region us-east-1 \
  --environment Variables="{IG_USER_ID=...,IG_ACCESS_TOKEN=...,existing_var_1=...,existing_var_2=...}"

The environment variables are stored in the Lambda function's configuration (not in AWS Secrets Manager in this case, though that would be a stronger security practice for future iterations). The Lambda runtime loads these variables at invocation time and passes them to the Python/Node.js code that fetches Instagram media.

Key Decisions

  • Long-lived tokens over short-lived: While short-lived tokens offer better security posture, they require background refresh logic. We opted for 60-day tokens initially to reduce operational complexity, with a documented refresh strategy using the token exchange endpoint.
  • EventBridge for token refresh (future): We documented a pattern for monthly token refresh via an EventBridge rule that invokes a separate Lambda function to re-exchange the token automatically. This prevents expiration without manual intervention.
  • Graph API product selection: This was the highest-priority decision. Without selecting the correct product in the app dashboard, no scope configuration would grant media read permissions.
  • Environment variables in Lambda config: While simpler than Secrets Manager for this MVP, we noted that production deployments should store tokens in AWS Secrets Manager and reference them via Lambda IAM policies.

Verification and Testing

Once Lambda environment variables were set, we verified the integration by navigating to shipcaptaincrew.queenofsandiego.com/g/2026-04-29 and confirming that Instagram posts from @sailjada matching that date/time window now appear alongside guest-uploaded photos in the gallery.

The Lambda function queries the Instagram Graph API media endpoint:

https://graph.facebook.com/v18.0/{IG_USER_ID}/media?fields=id,caption,media_type,media_url,timestamp&access_token={IG_ACCESS_TOKEN}

Results are filtered client-side to match the requested event date and time window (e.g., ±24 hours around the event).

What's Next

  • Token refresh automation: Implement an EventBridge rule + Lambda function to automatically refresh the long-lived token every 50 days, eliminating manual credential rotation.
  • Secrets Manager migration: Move tokens from Lambda environment variables to AWS Secrets Manager with appropriate IAM role policies for improved security audit trails.
  • Error handling: Add retry logic and graceful degradation if the Instagram API is unreachable or if tokens expire unexpectedly during a request.
  • Monitoring and logging: Set up CloudWatch