Install
openclaw skills install @mebusw/instagram-publishUse when publishing images to Instagram via the Graph API from a Creator or Business account. Triggers when the user wants to automate Instagram posting, push an image with caption to their account, set up Instagram API access for the first time, or troubleshoot Instagram Graph API errors like "Media download has failed" or "Unsupported post request". Assumes one-time Meta App setup is complete and an Access Token is available.
openclaw skills install @mebusw/instagram-publishPublish images to Instagram through Meta's Graph API. Part 1 is one-time manual setup (browser clicks); Part 2 is the repeatable automated publish (this skill does it for you).
# 1. Put credentials in .env (one time)
cp .env.example .env
# edit .env with ACCESS_TOKEN (and optionally IG_USER_ID)
# 2a. Single image
python3 scripts/publish.py \
--image-urls "https://example.com/photo.jpg" \
--caption "Hello from Instagram API"
# 2b. Carousel (2-10 images, swipable in feed)
python3 scripts/publish.py \
--image-urls "https://example.com/1.jpg" "https://example.com/2.jpg" "https://example.com/3.jpg" \
--caption "Swipe → slide 1 / slide 2 / slide 3"
The script prints the new post ID on success. Done.
These steps cannot be automated — they require human logins, email verification, and Meta's UI. Do them once, save the resulting ACCESS_TOKEN, and you never need to touch Meta's dashboard again unless permissions change.
Visit https://www.instagram.com/accounts/professional_account_settings/ and follow:
Settings
↓
Account Type
↓
Switch to Professional Account
↓
Creator
Visit https://developers.facebook.com/ and sign in with your Facebook account, then:
Get Started
↓
Verify Email
↓
Accept Terms
Visit https://developers.facebook.com/apps/:
Create App
↓
Use cases: Manage messaging & content on Instagram
↓
Do NOT connect a Business portfolio
⚠️ Skipping the Business portfolio avoids the "insufficient developer permissions" error later.
In App settings → Basic:
Use cases → Customize
↓
Left sidebar: API setup with Instagram login
↓
Add required messaging permissions
↓
Add account → authorize your Instagram account
↓
Generate token
↓
Check "I understand" → copy the token
The token starts with IGAA.... Save it to .env (next section).
| Field | Example | Lives in |
|---|---|---|
ACCESS_TOKEN | IGAAxxxxx... | .env |
IG_USER_ID | 2700xxxxxx | .env (optional — script can auto-fetch) |
USERNAME | xxxxx | reference only |
⚠️ Never commit
.envto git. The token is the only thing that authenticates posts on your behalf.
The Graph API calls below are bundled in scripts/publish.py. You run one command; the script runs the whole flow. The path is chosen automatically from the number of URLs:
1 URL → single-image mode (3 calls):
[1] Validate token → GET /me?fields=id,username
↓
[2] Create container → POST /{ig_user_id}/media (image_url + caption)
↓
[3] Publish → POST /{ig_user_id}/media_publish (creation_id)
2–10 URLs → Carousel mode (5+ calls):
[1] Validate token → GET /me?fields=id,username
↓
[2a] Create item → POST /{ig_user_id}/media (image_url + is_carousel_item=true) × N
↓
[2b] Create carousel → POST /{ig_user_id}/media (media_type=CAROUSEL + children=id1,id2,...)
↓
[3] Publish → POST /{ig_user_id}/media_publish (creation_id)
.envcp .env.example .env
.env contents:
ACCESS_TOKEN=IGAAxxxxxxxxxxxxxxxxxxxx
IG_USER_ID=2700xxxxxxxx # optional — auto-detected if missing
The script reads .env from the skill directory. If you run it from elsewhere, pass --env /path/to/.env.
可以调用可用的文生图模型或生图 SKILL来生图,通常会返回一个公网 CDN 临时 URL。无需下载图片后再上传到图床,直接用这个图片 URL 传入以下脚本即可用于发布 ins 帖子。
python3 scripts/publish.py \
--image-urls "https://example.com/photo.jpg" \
--caption "Hello from Instagram API"
Optional flags:
| Flag | Default | Notes |
|---|---|---|
--image-urls URL [URL ...] | required | One or more public image URLs. 1 URL = single post, 2-10 URLs = Carousel. Each URL must be reachable by Meta's servers. Usually, use the the returned url from image generation model. if user provides a local file, you should invoke relevant skill to upload it to OSS/COS platform and get a public url before publishing |
--caption TEXT | empty | Instagram's caption, with hashtags and mentions |
--env PATH | ./.env | Path to a .env file |
--api-version | v24.0 | Graph API version |
--dry-run | off | Validate token and build the container, but skip the final publish |
Successful single-image output:
✓ Token valid — user: xxxxx (id: 2700xxxxxx)
→ Single-image mode
✓ Container created: xxxxx
✓ Published: xxxxx
https://www.instagram.com/p/xxxxxxxxx
Pass 2–10 URLs to --image-urls and the script will switch to Carousel mode automatically. The flow is:
For each image URL:
POST /{ig_user_id}/media image_url=... is_carousel_item=true
→ returns N creation_ids
Then:
POST /{ig_user_id}/media media_type=CAROUSEL children=id1,id2,... caption=...
→ returns one carousel creation_id
Then:
POST /{ig_user_id}/media_publish creation_id=<carousel id>
→ returns the published post id
Example:
python3 scripts/publish.py \
--image-urls "https://example.com/1.jpg" "https://example.com/2.jpg" "https://example.com/3.jpg" \
--caption "这是 OpenClaw 自动发布的多图帖子 #carousel"
Successful Carousel output:
✓ Token valid — user: xxxxx (id: 2700xxxxxx)
→ Carousel mode (3 images)
✓ Carousel item 1/3 container: 111111111111
✓ Carousel item 2/3 container: 222222222222
✓ Carousel item 3/3 container: 333333333333
✓ Carousel container created: 999999999999
✓ Published: 1789xxxxxxxx
https://www.instagram.com/p/xxxxxxxxx
Constraints:
media_publish returns "Media ID is not available", re-run with --dry-run first or wait 30–60s between create_carousel_container and publish_container.If you only need a "fake carousel" and don't want to use the Carousel API, stitch images vertically with ImageMagick and publish as a single image:
magick img1.jpg img2.jpg -append -resize 1080x /tmp/combined.jpg
# 裁切到精确 4:5
magick /tmp/combined.jpg -gravity Center -crop 1080x1350+0+0 +repage /tmp/final.jpg
Use this when:
https://drive.google.com/file/d/xxxxxx/view?usp=sharing ,要提取文件ID并转换为 https://drive.google.com/uc?export=view&id=xxxxxxhttps://raw.githubusercontent.com/<username>/<repo>/<branch>/<path>/<image>.jpg形式,例如:https://github.com/mebusw/instagram-assets/blob/main/images/post-001.jpg要转为https://raw.githubusercontent.com/mebusw/instagram-assets/main/images/post-001.jpg{"error": {"message": "Media download has failed"}}
Meta's servers cannot reach your image_url. Fix:
localhost, 127.0.0.1, and private network IPs原因是 container 刚创建就立即 publish,解决方案是等待 30–60 秒。
{"error": {"message": "Unsupported post request"}}
Almost always a token-permission issue. Fix:
instagram_content_publish and instagram_basic are grantedACCESS_TOKEN in .envLong-lived tokens still expire (default ~60 days). If /me returns an auth error, regenerate the token in the App dashboard and update .env.
instagram-publish/
├── SKILL.md # this file
├── .env.example # template for ACCESS_TOKEN and IG_USER_ID
├── .gitignore # keeps .env out of git
└── scripts/
└── publish.py # the three-step publisher