Integrating Instagram Graph API into a Lambda-Based Photo Gallery: Setup, Token Exchange, and Long-Lived Access
We recently enabled Instagram media integration in the guest photo gallery system at shipcaptaincrew.queenofsandiego.com/g/{event_id}.
The Lambda function responsible for rendering this page (deployed as shipcaptaincrew in us-east-1, account 782785212866) had dormant
Instagram integration code that required proper Graph API credentials and token management. This post walks through the exact steps we took to activate
it, the architectural decisions made, and the token refresh strategy implemented.
What Was Done
The guest photo page aggregates two content sources: user-uploaded photos (stored in S3 and approved via a moderation workflow) and real-time posts
from the @sailjada Instagram account during the same event window. Previously, the Instagram integration returned an empty array because the Lambda
environment lacked the required IG_USER_ID and IG_ACCESS_TOKEN variables. We completed the full Instagram Graph API
onboarding flow, generated credentials, and deployed them to the Lambda function configuration.
Technical Details: Instagram Graph API Setup
Step 1: Add the Correct Product
The critical first mistake to avoid: the app already had an Instagram product added—but it was configured for Messaging, which handles
direct message flows, not media reads. This product does not grant the instagram_basic scope needed to fetch media data.
- Navigate to
developers.facebook.com/apps - Select the
sailjada-socialapp - Click Add Product in the left sidebar
- Search for and select Instagram Graph API (distinct from "Instagram Basic Display" and "Instagram Messaging")
- Complete the setup flow—this adds the product to your sidebar
The Graph API product is the correct choice because it provides read access to media metadata, likes, comments, and insights for Business and Creator accounts. Our use case requires listing recent media from @sailjada's account, which is a Graph API operation.
Step 2: Verify Account Type and Link the Instagram Account
Instagram Graph API requires that the account being queried is either a Business Account or Creator Account. Personal accounts cannot be queried via the Graph API. We verified that @sailjada was already converted to a Creator account and confirmed it was linked to a Facebook Page (necessary for the token exchange flow).
Inside the Instagram Graph API section of the app dashboard, we used the API setup with Instagram login option to connect @sailjada by logging in with its credentials. This establishes the relationship between the app and the account.
Step 3: Generate a Short-Lived Access Token
Access tokens in the Instagram Graph API come in two forms: short-lived (valid for ~1 hour) and long-lived (valid for ~60 days). The short-lived token is the entry point to the system.
- Open
developers.facebook.com/tools/explorer - In the dropdown at the top, select the
sailjada-socialapp - Click Generate Access Token
- Select the Facebook Page linked to @sailjada
- Grant the scopes:
instagram_basicandpages_show_list - Copy the resulting token (this is a short-lived token, valid ~1 hour)
We kept this token temporarily in a secure location for the next steps. It is not stored long-term because short-lived tokens expire quickly and cannot be refreshed directly—instead, they are exchanged for long-lived tokens.
Step 4: Retrieve the IG_USER_ID
The Instagram User ID (business account ID) is required to query media. We obtained it using two API calls with the short-lived token:
curl -s "https://graph.instagram.com/me/accounts?access_token=SHORT_LIVED_TOKEN" \
| jq '.data[] | select(.name == "YourPageName") | .id'
This returns the Facebook Page ID. Then, using that Page ID:
curl -s "https://graph.instagram.com/PAGE_ID?fields=instagram_business_account&access_token=SHORT_LIVED_TOKEN" \
| jq '.instagram_business_account.id'
The id field in the instagram_business_account object is the IG_USER_ID. This ID is stable and does not change;
it identifies the Instagram account you are querying.
Step 5: Exchange Short-Lived Token for Long-Lived Token
The short-lived token is exchanged for a long-lived token (valid ~60 days) using the app's credentials. This is a server-side operation that requires the app secret (not exposed to the client).
curl -s "https://graph.instagram.com/access_token" \
-d "grant_type=ig_refresh_token" \
-d "access_token=SHORT_LIVED_TOKEN" \
| jq '.access_token'
The returned access_token is the long-lived token and becomes the IG_ACCESS_TOKEN environment variable. This token is now
ready to be deployed.
Infrastructure: Lambda Environment Configuration
With the IG_USER_ID and IG_ACCESS_TOKEN in hand, we deployed them to the Lambda function:
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}"
These environment variables are now accessible within the Lambda function code. The function (written in Python) reads them on invocation:
import os
ig_user_id = os.environ.get('IG_USER_ID')
ig_access_token = os.environ.get('IG_ACCESS_TOKEN')
When both variables are present, the function's Instagram media fetch logic becomes active. The page now displays Instagram posts alongside user-uploaded photos.
Key Decisions
- Long-Lived Over Short-Lived Tokens: Short-lived tokens expire hourly and cannot be directly refreshed. Long-lived tokens (60-day window) reduce operational complexity and eliminate the need for frequent token rotation during routine operation.
- Token Storage in Lambda Environment: We store the token as a Lambda environment variable rather than in AWS Secrets Manager. Given that the token must be refreshed every 60 days and that the function is not highly security-sensitive (