Open-source Instagram publisher that drives instagram.com with Playwright and your own imported cookies. Prepare content in a folder, configure your account once, publish.
Status: Alpha. Personal-account use only. Using browser automation violates Instagram's ToS — use a scratch account, conservative pacing, and a residential IP that matches where you got the cookies.
Most Instagram scheduling tools require a Business/Creator account converted to Meta's Graph API. That rules out personal accounts entirely, and even for business accounts you need App Review + Business Verification before distributing a tool. auto-instagram takes the opposite approach: automate the website you already use, with the session you already have.
Supported post types (web UI constraints, April 2026):
- Feed image (single)
- Carousel (2–20 items, images or videos)
- Reels (≤ 90s vertical video)
Not supported: Stories (instagram.com doesn't support creating Stories — no automation can fix this without switching backends).
- Python 3.11+
- macOS or Linux (tested on macOS)
- A real Chrome/Chromium you can log in from on the same network you'll run the bot on
- (Recommended) a residential proxy if you plan to run this on a different machine from where you logged in
One command with pipx (recommended):
pipx install auto-instagram
auto-ig init --account demoOr with uv:
uv tool install auto-instagram
auto-ig init --account demoauto-ig init installs the patched Chrome channel Patchright needs and scaffolds a working directory in .:
./config/demo.yaml # account config from the shipped template
./content/example-post/ # sample post descriptor
./sessions/ # session files (gitignored)
./.gitignore # appended with auto-instagram entries
It is safe to re-run — existing files are preserved.
git clone https://github.com/xtea/auto-instagram
cd auto-instagram
uv sync
uv run auto-ig init --account demoEdit config/<account>.yaml. The important fields:
handle— your IG username (display only)user_agent,viewport,locale,timezone— match the browser you'll log in from. Drift between these and the cookie's origin is the #1 cause ofchallenge_required.pacing.max_posts_per_day— start at 1–3. Hammering a personal account gets it flagged.
Two paths; pick whichever you prefer.
auto-ig login --account demoA Chrome window opens. Log in by hand (handle 2FA yourself). When the home feed appears, the session is saved to sessions/demo.json.
If you already have a logged-in IG tab in Chrome:
-
Install Cookie-Editor (browser extension).
-
Open
instagram.com, click the extension, "Export" → "Export as JSON" → save toig-cookies.json. -
Run:
auto-ig import-cookies ./ig-cookies.json --account demo
The tool rejects the import if sessionid, csrftoken, or ds_user_id are missing.
auto-ig doctor --account demoShould print OK: <handle> session is valid.
content/
└── my-post/
├── post.yaml
└── media/
├── 1.jpg
└── 2.jpg
type: feed # feed | carousel | reel
caption: |
Your caption text. Up to 2200 chars and 30 hashtags.
#opensource
media:
- ./media/1.jpg # paths are relative to this file
schedule: 2026-04-25T14:00:00Z # optional; UTC or with offsetValidation runs before any browser work:
| Type | Rule |
|---|---|
feed |
exactly 1 image/video |
carousel |
2–20 items, images and/or videos |
reel |
exactly 1 video, .mp4/.mov/.m4v |
| caption | ≤ 2200 chars, ≤ 30 hashtags |
auto-ig publish content/my-post --account demo --dry-run # safe first run
auto-ig publish content/my-post --account demo # actually shares--dry-run walks the full upload flow and stops before clicking Share — useful when patching selectors.
Drop posts with schedule: set in the future, then run auto-ig queue from cron / launchd / systemd-timer:
*/5 * * * * cd /path/to/auto-instagram && auto-ig queue --account demo >> sessions/queue.log 2>&1The queue stores state in sessions/queue.db (SQLite) with statuses: queued | running | succeeded | failed | paused. Use auto-ig list to inspect.
- Launch Chrome via Patchright (Chromium with CDP/webdriver leaks patched at the binary level). Vanilla
playwrightis detected by Instagram's fingerprinting stack in 2026 — don't use it. - Load
sessions/<account>.jsonas the Playwrightstorage_state. - Navigate to
instagram.com, confirm the home-feed nav is visible (signal of authenticated state). - Click New post → (for Reels, select the Reel submenu) →
setInputFilesinto the hidden<input type=file>. - Click through Next → Next, fill caption, click Share.
- Wait for the "Your post has been shared" confirmation and extract the
/p/<shortcode>/if available.
All selectors are in src/auto_instagram/publisher/selectors.py — when IG changes the UI, that is the file to patch.
The Publisher protocol in publisher/base.py is deliberately minimal so you can drop in:
InstagrapiPublisher(wrapinstagrapifor more-robust personal-account posting, including Stories)GraphApiPublisher(Meta official API, Business/Creator accounts only)
Built-in guardrails, tunable in config/<account>.yaml:
max_posts_per_daydaily cap (enforced by the queue)min/max_step_delay_secondsrandomized delays between UI actionspre_run_idle_seconds_*scroll/dwell before the first click
On challenge_required / redirect to /accounts/login/ or /challenge/, the runner pauses the job and records the reason. Re-authenticate with auto-ig login and retry.
- No Stories — not supported by instagram.com.
- Selectors rot. IG ships UI changes every few weeks. Expect periodic patches to
selectors.py. - 2FA mid-run. If IG challenges mid-publish, the tool pauses; manual re-login is required.
- Shared IP. Using cookies captured from residence A while running the bot on residence B's IP is the single most reliable way to get challenged.
- ToS risk. Browser-driven automation of personal IG accounts is against Instagram's terms. Ban risk is real. Use a scratch account.
- Web UI / dashboard (CLI + YAML only)
- Long-running daemon (cron-friendly invocation instead)
- Engagement automation (likes, follows, DMs)
MIT.