verification
use it
# add to stunt.yaml
services:
instagram:
adapter: embedded:instagram-style
# then
$ stunt upThe embedded: source extracts this adapter from the binary — no git clone, fully offline.
how to mock the Instagram Graph API locally
Install stunt, paste the service block above into stunt.yaml, run stunt up — then point your client at localhost instead of Instagram Graph API. It works offline, keeps state across requests and restarts, and every response is synthetic: the route shapes, status codes, pagination, and webhook delivery follow the real Instagram Graph API v21.0 surface — the data is fake, the behavior is real.
backing stores
tokenscodescontainersmedia
api surface (10)
generated from the adapter manifest — exactly what boots, nothing more
10 routes
GET/oauth/authorize
POST/oauth/access_token
GET/v21.0/me
GET/v21.0/refresh_access_token
GET/v21.0/{container_id}
GET/v21.0/{media_id}/comments
GET/v21.0/{media_id}/insights
POST/v21.0/{ig_user_id}/media_publish
POST/v21.0/{ig_user_id}/media
GET/v21.0/{ig_user_id}/media
VM-verified behaviors (44)
- ✓authorize without redirect_uri, state or client_id is invalid_request
- ✓authorize redirects back with a fresh code and the state echoed
- ✓a redirect_uri that already carries a query is joined with &
- ✓the exchange demands grant_type=authorization_code
- ✓an unknown code is 400 invalid_grant
- ✓a good exchange mints a token bound to a fresh IG user
- ✓the code is single-use: a replay is invalid_grant
- ✓client mismatches are 400 invalid_client
- ✓a mismatched attempt must not burn the code
- ✓a second flow mints a distinct user
- ✓a missing bearer is 401 in the Graph error envelope
- ✓wrong schemes and unknown bearers answer the same 190
- ✓every API route enforces the same gate
- ✓a bearer dies at its clock-derived 60-day expiry
- ✓me returns the OAuth-bound profile
- ✓media_count starts at 0 and tracks published media
- ✓a second user's profile counts only its own media
- ✓a container without image_url or video_url is a code-100 400
- ✓create assigns a c_* container id
- ✓a fresh container polls IN_PROGRESS and publish is gated
- ✓the status derives FINISHED after the ~3s window
- ✓publishing a FINISHED container assigns an m_* media id
- ✓simulate_fail ends in ERROR and never publishes
- ✓unknown container ids answer the catch-all 404
- ✓a video container publishes VIDEO media carrying the video url
- ✓the media edge lists newest first (reverse chronological)
- ✓?fields= projects each row and drops unknown fields
- ✓limit pages through after cursors with a next that keeps the query
- ✓a malformed after cursor is a code-100 400
- ✓the list is scoped to the path user
- ✓all four manifest metrics return in canonical order
- ✓values are deterministic across reads
- ✓?metric= filters to the requested names
- ✓an unknown metric name empties the data set (deviation, as-is)
- ✓insights for an unknown media id are the catch-all 404
- ✓comments authorize via the access_token query param
- ✓the bearer header authorizes the identical read
- ✓a bad query token is the 190 envelope
- ✓since hides comments at-or-before the cutoff
- ✓comments on an unknown media id are a code-100 OAuthException 404
- ✓refresh demands a token (query param or bearer)
- ✓an unknown token is rejected with 190
- ✓refresh mints a fresh 60-day token; the old one keeps working
- ✓the refreshed token outlives the original's expiry
Not implemented (4)
- No account-level insights (GET /{ig_user_id}/insights), per-media only
- No stories publishing (media_type=STORIES) or story insights
- No hashtag search (ig_hashtag_search, top_media, recent_media)
- No comment replies, delete, hide, or moderation endpoints
Differs from the real API (5)
- Container processing fixed at ~3s IN_PROGRESS before FINISHED or ERROR
- simulate_fail=true create flag forces container ERROR; real API has no such switch
- Cursors are forward-only plain offsets; no before cursor or paging.previous
- An unknown insights metric returns 200 with empty data; real Graph answers error code 100
- media_publish does not check container ownership across users
want more of the Instagram Graph API surface? adapters are YAML + Starlark — the authoring guide covers adding routes, fixtures, and state machines. Open an issue for anything big first, or just file a PR — merged routes ship in the next release.