Instagram Publish
Publish 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).
When to use
- User wants to post an image to Instagram programmatically
- User has a public image URL and wants to attach a caption and publish
- User just registered a Meta App and needs the rest of the wiring done
- User is hitting "Media download has failed" or "Unsupported post request"
When NOT to use
- Posting Reels, Stories, or Carousels (this skill handles single-image posts only)
- Local files (image must be a public URL — see error #1 below)
- Account is still a personal account (must be Creator or Business first)
Quick Start (after Part 1 setup is done)
# 1. Put credentials in .env (one time)
cp .env.example .env
# edit .env with ACCESS_TOKEN (and optionally IG_USER_ID)
# 2. Publish
python3 scripts/publish.py \
--image-url "https://example.com/photo.jpg" \
--caption "Hello from Instagram API"
The script prints the new post ID on success. Done.
Part 1: One-time manual setup (browser only)
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.
Step 1 — Switch Instagram to a Creator account
Visit https://www.instagram.com/accounts/professional_account_settings/ and follow:
Settings
↓
Account Type
↓
Switch to Professional Account
↓
Creator
Step 2 — Create a Meta Developer account
Visit https://developers.facebook.com/ and sign in with your Facebook account, then:
Get Started
↓
Verify Email
↓
Accept Terms
Step 3 — Create an App
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.
Step 4 — Finish App settings
In App settings → Basic:
- Upload an app icon
- Fill in Privacy Policy URL and Data Deletion URL
- Pick an app category
- (Optional) In Use cases → Customize, configure account linking at https://accountscenter.instagram.com/connected_experiences/
- Click Publish to make the app live
Step 5 — Generate the Access Token
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).
Credentials you should have after Part 1
| 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.
Part 2: Automated publishing (the skill does this)
The three Graph API calls below are bundled in scripts/publish.py. You run one command; the script runs all three.
[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)
Setting up .env
cp .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.
Publishing an image with public reachable url
python3 scripts/publish.py \
--image-url "https://example.com/photo.jpg" \
--caption "Hello from Instagram API"
Optional flags:
| Flag | Default | Notes |
|---|---|---|
--image-url URL |
required | Must be publicly reachable by Meta's servers, 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 output:
✓ Token valid — user: xxxxx (id: 2700xxxxxx)
✓ Container created: xxxxx
✓ Published: xxxxx
https://www.instagram.com/p/xxxxxxxxx
Note:
- 单图:确认图片存在且格式为 JPG/PNG。
- 多图拼接:publish.py 仅支持单图,两张图要么拼成一张,要么分两次发两条 post。IG 推荐 4:5(1080×1350)竖图,比例超过 1:2.67(高/宽)时 IG 会自动裁切, 可以用 ImageMagick 纵向拼接:
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
Common errors
"Media download has failed"
{"error": {"message": "Media download has failed"}}
Meta's servers cannot reach your image_url. Fix:
- Host the image somewhere public: Alibaba Cloud OSS, Tencent Cloud COS, AWS S3, GitHub Raw, any CDN
- Open the URL in a fresh browser while logged out — if you can't see it, Meta can't either
- Avoid
localhost,127.0.0.1, and private network IPs
"Media ID is not available"
原因是 container 刚创建就立即 publish,解决方案是等待 30–60 秒。
"Unsupported post request"
{"error": {"message": "Unsupported post request"}}
Almost always a token-permission issue. Fix:
- Back to Use cases → Customize in the App dashboard
- Confirm
instagram_content_publishandinstagram_basicare granted - Re-generate the token and replace
ACCESS_TOKENin.env
Token expired
Long-lived tokens still expire (default ~60 days). If /me returns an auth error, regenerate the token in the App dashboard and update .env.
Files in this skill
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