```html

Integrating Instagram Graph API with AWS Lambda: Building a Guest Photo Gallery with Social Media Aggregation

What Was Done

We integrated Meta's Instagram Graph API into the ShipCaptainCrew guest photo gallery system to automatically surface @sailjada Instagram posts alongside user-uploaded charter photos. The integration lives in a Lambda function that queries the Instagram Graph API for media posted within a configurable time window around each charter event, enriching the guest experience with official social content.

The guest photo page at shipcaptaincrew.queenofsandiego.com/g/{event_id} (e.g., /g/2026-04-29) now has the infrastructure in place to display both user-uploaded approved photos and Instagram posts from the same timeframe, though the integration remained dormant until proper credentials were configured.

Technical Details

Architecture Overview

The system follows a Lambda-backed single-page application pattern:

  • Frontend: /tools/shipcaptaincrew/index.html — Static HTML served via CloudFront, makes API calls to the Lambda function's HTTP endpoint
  • Backend: /tools/shipcaptaincrew/lambda_function.py — AWS Lambda function handling guest photo database queries and Instagram Graph API requests
  • Data Layer: DynamoDB tables for approved guest photos, indexed by event date
  • Social Integration: Instagram Graph API for @sailjada business account media retrieval

Lambda Function Structure

The Lambda handler in lambda_function.py implements two core request flows:

  • Guest Photo Retrieval: Routes requests to /g/{event_id}, queries DynamoDB for approved photos matching the event date, returns paginated results with metadata (uploader, upload timestamp, approval status)
  • Instagram Media Aggregation: For the same event date, constructs a time window (configurable buffer, typically ±12 hours), calls Instagram Graph API with the IG_USER_ID and IG_ACCESS_TOKEN environment variables, requests media fields: id, media_type, media_url, timestamp, caption

The function gracefully degrades when Instagram credentials are absent — it logs a debug message and returns an empty array for Instagram posts, allowing the guest photo gallery to function independently without external API dependencies.

Instagram Graph API Integration Points

Three environment variables control the Instagram integration:

  • IG_USER_ID — The Instagram business account ID for @sailjada (numeric string, obtained via Graph API explorer)
  • IG_ACCESS_TOKEN — Long-lived access token (valid 60 days) with instagram_basic scope
  • IG_APP_ID, IG_APP_SECRET — Optional; used for automated monthly token refresh

The Lambda makes authenticated requests to the Instagram Graph API endpoint:

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

Results are filtered client-side in the index.html to match the event's time window, then merged with approved guest photos and returned as a unified array.

Infrastructure and Configuration

Lambda Function Configuration

Function Name: shipcaptaincrew

Region: us-east-1

Account: 782785212866

Environment variables are set via AWS Lambda console or CLI:

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

API Routing

The Lambda function is fronted by an HTTP API (likely API Gateway or Lambda Function URL). The guest page makes CORS-enabled requests to the API endpoint:

  • GET /g/{event_id} — Returns guest photos + Instagram posts for the event
  • POST /admin/approve-photo — Admin endpoint for photo moderation (uses API key auth)

CloudWatch Logging

Logs are written to /aws/lambda/shipcaptaincrew in CloudWatch. Instagram API errors (rate limits, invalid tokens, network failures) are logged with full error detail and request context to aid debugging.

Key Architectural Decisions

Why Instagram Graph API Over Basic Display

Instagram's Basic Display API limits us to the last 100 media items without timestamp filtering. The Graph API provides real-time media retrieval with precise filtering by posted time, allowing us to match posts to specific charter events. The tradeoff: Graph API requires a business/creator account linked to a Facebook Page, plus OAuth setup — but this is a one-time configuration cost.

Time Window Filtering

Rather than filtering in the API call (which would require more complex Graph API queries), we fetch all recent media from the account and filter client-side by timestamp. This simplifies the Lambda logic and reduces API call complexity. For high-volume accounts, this could be optimized via Lambda-side filtering before returning to the client.

Graceful Degradation

The Instagram integration is optional. If credentials are missing or the API is unavailable, the guest photo gallery continues to function with user-uploaded content only. This was a deliberate design choice to prevent a social media outage from affecting the core product.

Token Refresh Strategy

Instagram access tokens expire after 60 days. The recommended approach is to run a monthly refresh via EventBridge + Lambda, calling the token exchange endpoint to generate a new 60-day token. This can be automated or initially handled manually before a monthly refresh schedule is established.

What's Next

  • Token Refresh Automation: Set up an EventBridge rule that triggers a Lambda function monthly to refresh the IG_ACCESS_TOKEN and update the ShipCaptainCrew function's environment variables
  • Media Caching: Implement DynamoDB caching for Instagram posts to reduce API calls and improve page load time; invalidate the cache when new posts are detected
  • Analytics: Track which Instagram posts drive engagement in the guest gallery (click-throughs, shares); feed this data back to social strategy
  • Multi-Account Support: Extend the integration to support multiple Instagram accounts (e.g., crew members' accounts in addition to @sailjada)
  • Error Handling Enhancements: Implement exponential backoff and retry logic for transient API failures; set up SNS alerts for persistent Instagram API errors
```