Skip to main content
Glama

igagent

An agentic Instagram content & media harvester for AI agents — no login, no API keys, no browser. MCP wrapper included.

Python Tests License LLM

igagent is the Instagram sibling of xthread-agent (X/Twitter threads) and ytagent (YouTube). Same doctrine: a deterministic, slot-based, stdlib-only state machine that a cloud agent can call with one URL and read back one JSON contract.

python igagent.py "https://www.instagram.com/p/<CODE>/"
# → verified media on disk + post_manifest.json

Why this exists

Cloud-based AI agents (GitHub Actions runners, sandboxed VMs, MCP hosts) cannot log into Instagram, and Instagram keeps proving that a login is not even the interesting boundary — the interesting boundary is which door a machine is allowed through with no session at all. Public embed pages are exactly such a door: Instagram serves them to any referrer, and for link unfurlers it renders the actual media straight into the HTML. igagent is built entirely on that observation.

What it gives you:

  1. One input, one artifact. A post URL (or share link, or bare shortcode) in; a verified media directory plus post_manifest.json out.

  2. Honest negatives. A deleted post is not an exception — it is status: "empty" with structured errors and a slot-by-slot provenance trace.

  3. Slots, not brands. The three decode surfaces are interchangeable implementations of one contract. When Instagram kills one, you replace the slot — the pipeline never restructures.

  4. Verified delivery. Files exist only after passing the CDN allowlist, a Content-Length check, and a magic-byte identity check. A tool that reports a file is vouching for its bytes.

  5. Truth layers over heuristics. Carousel membership comes only from the payload (edge_sidecar_to_children) and is flagged carousel.known — never inferred from page layout. The shortcode itself decodes to the numeric media id by pure math, cross-checked against Instagram's ig_cache_key.

  6. Politeness as a hard constraint. Bounded retries, 0.6 s decode sleeps, response caps, one post per invocation. The public surfaces this tool depends on are free; restraint is the rent.

Related MCP server: reelminner

Architecture

            ┌──────────────────────────────────────────────────────┐
            │                    DISCOVERY                        │
            │  normalize p/reel/reels/tv URLs · instagr.am &      │
            │  ddinstagram mirrors · share links (1 hop) · bare   │
            │  shortcodes · shortcode ↔ media-id base64 math      │
            └───────────────────────────┬──────────────────────────┘
                                        ▼
            ┌──────────────────────────────────────────────────────┐
            │                     DECODE  (slots)                 │
            │  1. embed_json  — legacy rich payload               │
            │     (caption, metrics, sidecar children, author)    │
            │  2. embed_html  — unfurler SSR page                 │
            │     (media img/video, author, followers, cache key) │
            │  3. og_meta     — OpenGraph tags                    │
            │     (title/caption, og:image, og:video:secure_url)  │
            │  provenance trace [{"slot","outcome"}] on every run │
            └───────────────────────────┬──────────────────────────┘
                                        ▼
            ┌──────────────────────────────────────────────────────┐
            │                     DELIVER                        │
            │  https + *.cdninstagram.com / *.fbcdn.net only      │
            │  stream → .part → Content-Length ✓ → magic bytes ✓  │
            │  → os.replace (atomic) → files + post_manifest.json │
            └──────────────────────────────────────────────────────┘

Each tier fails closed: a slot that finds nothing hands control to the next slot and the attempt is recorded; only "everything failed" becomes a run-level error.

Quickstart

# full harvest (decode + verified downloads)
python igagent.py "https://www.instagram.com/p/<CODE>/" --out ./ig_media

# metadata only
python igagent.py "<CODE>" --no-download

# the machine contract: exactly one JSON object on stdout
python igagent.py "<input>" --json --quiet

For AI agents

python igagent.py agent-instructions 2>/dev/null || true   # not needed —
# the operating manual IS agent.md: read that file and nothing else.

The three-command contract:

command

returns

exit

igagent.py "<input>" --json --quiet

summary JSON on stdout, full envelope at manifest_path

0 ok/partial · 1 empty · 2 invalid

igagent.py "<input>" --no-download --json --quiet

same, no files on disk

same

igagent.py --version

igagent 1.0.0

0

CLI reference

igagent.py <input> [--out DIR] [--no-download] [--json] [--quiet] [--version]

<input>         instagram.com /p|reel|reels|tv/<code>/ · mirror hosts ·
                share links (instagram.com/share/…) · bare shortcodes
--out DIR       output directory (default: ig_media)
--no-download   decode only; manifest still written
--json          one JSON summary object on stdout (stderr silenced)
--quiet         silence human logs on stderr

Accepted input shapes: https://www.instagram.com/p/<CODE>/, instagram.com/reel/<CODE>?utm_source=…, m./l./ddinstagram/instagr.am mirrors, https://www.instagram.com/share/<token>/, and bare shortcodes (5–32 chars of A-Za-z0-9_- that decode to a positive media id).

JSON output (for AI agents)

{
  "ok": true,
  "status": "ok",
  "shortcode": "<CODE>",
  "media_id": "1949525278281554174",
  "canonical_url": "https://www.instagram.com/p/<CODE>/",
  "post_type": "image",
  "extraction_source": "embed_html",
  "images": 1, "videos": 0,
  "downloaded": 1, "failed_downloads": 0,
  "out_dir": "ig_media",
  "manifest_path": "ig_media/post_manifest.json",
  "errors": [],
  "duration_sec": 2.2
}

The full envelope (post_manifest.json) adds the post object: caption, created_at, author (username, followers, verified, … — explicit nulls when a slot does not expose them), media.images[] / media.videos[] with url / file / downloaded / downloadable / reason, carousel{known, child_count}, errors[] with stable codes, and metadata.decode_slots_tried — the provenance trace. The draft-07 schema is bundled at schema/post-result.schema.json.

For MCP hosts

python mcp_server.py speaks newline-delimited JSON-RPC 2.0 on stdio — stdlib only, no mcp package. Register it in Claude Desktop / Zed:

tool

what it does

extract_post

full harvest → verified files + envelope

lookup_post

metadata-only decode (no downloads)

read_manifest

return an existing post_manifest.json (refuses anything else)

get_schema

the draft-07 envelope schema

Error policy: an honest empty result is not an MCP error; a bad tool argument, timeout, or crash is.

Documentation

file

role

agent.md

the operating manual for AI agents — read this file and nothing else

agents.md

perfection-based role prompts for the five pipeline roles

PROJECT_CONTEXT.md

maintainer manifesto: why, load-bearing walls, fragile parts, debts

docs/endpoint-matrix.md

living endpoint status table + maintenance protocol

schema/post-result.schema.json

the output contract, machine-checkable

RELEASE_NOTES.md

version history

Constraints (non-negotiable)

  1. No login, no cookies, no OAuth, no browser. Public content only; everything fails closed.

  2. No GUI, no interactive prompts. 100% non-interactive CLI.

  3. No LLM at runtime. Deterministic state machine — the "agent" is designed for AI agents, not made of one.

  4. stdlib only. Single file, zero pip dependencies, Python 3.9+.

  5. Logs on stderr, data on stdout. Always pipe-safe.

  6. Files stay under the output directory. CDN allowlist, ID validation, atomic writes, magic-byte verification.

Requirements

Python 3.9+ and outbound HTTPS. Nothing else — no pip, no ffmpeg, no browser, no env vars, no config files.

Installation

# as a tool (after PyPI publish)
pip install igagent
igagent "<url>" --json

# from source
git clone https://github.com/Bilal140202/igagent.git
python igagent/igagent.py "<url>"
# or
python -m igagent "<url>"

Testing

python -m unittest discover -s tests -p "test_*.py"
# 69 tests, ~1s, zero network — synthetic fixtures only, never real IDs

Verified behavior (as shipped)

claim

evidence

embed_html slot harvests a live public image post from a datacenter IP

live run: status=ok, 1 image downloaded (33 KB, JPEG magic), media_id from ig_cache_key

shortcode ↔ media-id math is exact

round-trip property tests (50 random ids)

dead posts produce honest empties

live run: status=empty, E_DECODE_FAILED, exit 1

magic-byte gate rejects HTML masquerading as media

offline tests

lookalike CDN hosts (evil-cdninstagram.com, cdninstagram.com.evil.io) refused

offline tests

MCP handshake + 4-tool registry

offline stdio probe

Re-verify against your own vantage point and update docs/endpoint-matrix.md — that is the protocol.

Limitations (the honest section)

  • Reel/video URLs are frequently not exposed by the embed surface. The video entry then reports downloadable: false, reason: "no_video_url_exposed" — this is the truth, not a bug. Stories, live, and private accounts are out of scope entirely.

  • Carousel depth is slot-dependent. The commonly-served unfurler view shows only the primary item; carousel.known=false says so honestly. Full sidecars require the legacy embed_json payload.

  • Datacenter IPs get walls. Instagram's structured APIs (graphql, media info) are walled from cloud ranges and deliberately not slots; og_meta often needs a residential IP.

  • Instagram changes surfaces without notice. The slot architecture and the endpoint matrix exist precisely because everything upstream of this tool is borrowed ground.

igagent accesses only publicly served documents over unauthenticated HTTP(S), with politeness sleeps and hard caps, and it never circumvents a paywall, a login, or a private post. Respect creators: media and captions remain the property of their authors; downstream use is your responsibility. Do not use this tool at volumes that constitute abuse.

FAQ

Why is there no login option? Because the calling agent is a cloud VM by definition — no session, no cookies. The whole design is "what can a sessionless machine legitimately get?" — and the answer is documented, versioned, and fail-closed.

Why is status often partial instead of ok? partial means the post was decoded but something failed (e.g. a video URL was never exposed so nothing could be downloaded). The media entries carry the per-item reason. errors[] is the full story.

A decode stopped working — is igagent broken? Check docs/endpoint-matrix.md first. Surfaces flip; the matrix is the impersonal record of flips, and slots are how they get absorbed.

Does it work for stories or private accounts? No, by design — both require authentication, which violates constraint 1.

License

MIT — see LICENSE.

Acknowledgments

  • xthread-agent — the architecture, the docs-as-contract system, the MCP wrapper, and the phrase "restraint is the rent".

  • ytagent — "never trust a method's self-report", the verification doctrine this project merged into its delivery tier.

  • The mirror/unfurler ecosystem — the doors that were already open.

Available Tools

4 tools
extract_postA

Harvest a public Instagram post/carousel/reel: given a post URL, share link or bare shortcode, decodes the public embed surface and downloads every exposed photo and video (magic-byte verified) to out_dir. Returns the summary plus the full post_manifest envelope (author, caption, media URLs, local files, errors, decode-slot provenance). No login, no API keys. Large carousels can take a minute.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYesInstagram post/reel URL, share link, or bare shortcode
out_dirNooutput directory; a fresh temp dir when omitted
download_mediaNofalse = manifest only, no files on disk

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full load and does well: it discloses side effects (files written to out_dir, magic-byte verified), auth requirements (none), and a latency warning ('Large carousels can take a minute'). It does not state failure/timeout behavior, but covers the major behavioral traits an agent needs.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the verb and resource, then adds return/manifest and constraint details in a single dense paragraph. Every clause is informative, though the parenthetical list of manifest contents is slightly heavy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, yet the description enumerates the return envelope (author, caption, media URLs, local files, errors, decode-slot provenance) and covers auth, latency, and disk effects. For a 3-parameter tool this is complete enough for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents url, out_dir, and download_media. The description reinforces accepted URL forms and out_dir behavior but adds little parameter meaning beyond the structured fields, matching the baseline 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Specific verb (Harvest/download) plus resource (public Instagram post/carousel/reel), with scope made explicit ('public', 'given a post URL, share link or bare shortcode'). It clearly differentiates itself from siblings like lookup_post (metadata) and read_manifest (inspection) by describing end-to-end media download.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear context for use ('given a post URL... downloads every exposed photo and video') and constraints ('public', 'No login, no API keys'). It does not explicitly name when to prefer a sibling such as lookup_post over this tool, so it stops short of full when/when-not guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_schemaA

Return the JSON Schema (draft-07) describing the post_manifest envelope (schema_version 1.0) — use it to validate or explore the output contract.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does disclose the return format (JSON Schema draft-07) and the target version (post_manifest envelope, schema_version 1.0). It omits auth or size characteristics, but for a parameterless static-schema read this is solid disclosure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single front-loaded sentence with zero waste: verb, resource, format, version, and purpose each appear once. Nothing is padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a no-arg tool whose output IS the schema, the description adequately explains what comes back even without an output schema. It could note the response size or that it is static, but nothing essential is missing for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the schema-to-description burden is nil; the baseline of 4 applies. Nothing in the description misrepresents the parameter surface.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: it returns the JSON Schema (draft-07) for the post_manifest envelope. It is clearly distinct from read_manifest (which returns data rather than the schema), though it does not explicitly name siblings to route the agent.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives clear usage context — 'use it to validate or explore the output contract' — which tells the agent why and when to call it. There are no explicit exclusions or named alternatives, but the intent is unambiguous.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

lookup_postA

Fast metadata-only lookup of a public Instagram post: same decode pipeline as extract_post but with --no-download — author, caption, timestamps, media URLs, carousel shape, without writing media files. Use this when you only need to read the post.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYesInstagram post/reel URL, share link, or bare shortcode
out_dirNooutput directory for the manifest; temp dir when omitted

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does well: it discloses the metadata-only behavior, that no media files are written, that it applies to public posts, and that it shares the decode pipeline with extract_post. It omits failure modes (private/deleted posts) and any rate-limit or auth expectations, which keeps it from a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, zero filler: the core behavior leads, the routing guidance follows. The dense em-dash clause is still readable and every phrase (field list, --no-download, public) earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema and no annotations, the description compensates by enumerating returned entities (author, caption, timestamps, media URLs, carousel shape) and the file-writing side effect. Remaining gap is error/edge-case behavior for non-public posts, which is minor but real.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% — url format and out_dir/manifest semantics are already documented in the schema, including share links and bare shortcodes. The description reinforces the manifest output but adds no format or constraint detail beyond the structured fields.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific verb and resource ('metadata-only lookup of a public Instagram post') and explicitly positions itself against the sibling extract_post ('same decode pipeline as extract_post but with --no-download'). It even enumerates what metadata is returned, so an agent can distinguish it from extract_post without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives a clear selection condition ('Use this when you only need to read the post') and implicitly routes download needs to extract_post via the --no-download contrast. It stops short of an explicit when-not or a named alternative invocation, but the context is unambiguous.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

read_manifestA

Read an existing post_manifest.json produced by a previous extract/lookup and return the full envelope. Refuses any file not named post_manifest.json.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYesabsolute path to a post_manifest.json

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the burden and does disclose concrete behavior: it refuses any file not named post_manifest.json and returns the full envelope. It does not state permission requirements or failure modes beyond the name check, so it isn't fully transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, nothing wasted; the purpose and the hard constraint are both front-loaded and easy to scan.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter read tool with no output schema, the description covers purpose, provenance, and the filename restriction. The phrase 'return the full envelope' is only loosely informative about the return value, which is acceptable but not maximally clear.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter is already fully documented in the schema ('absolute path to a post_manifest.json'), so any description of it duplicates structured data. The 'refuses any file not named post_manifest.json' line adds a validation nuance, but the schema coverage is 100%, which sets the baseline at 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (read) and resource (an existing post_manifest.json), and ties provenance to prior tooling ('produced by a previous extract/lookup'), which cleanly separates it from extract_post and lookup_post.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Clearly frames the use case as re-reading output from a prior extract/lookup, plus a hard constraint (refuses files not named post_manifest.json). It lacks an explicit 'when not to use' or a named alternative, so it stops short of 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 4 tool updatesv1.0.0
    • First observedextract_post
    • First observedget_schema
    • First observedlookup_post
    • First observedread_manifest

TDQS

A4.2/5.0

Scored across 4 tools

Disambiguation4/5

extract_post and lookup_post share the same decode pipeline and differ only in whether media is downloaded, which is the main potential confusion, but their descriptions clearly state the download vs metadata-only distinction. read_manifest and get_schema are cleanly distinct from each other and from the extraction tools.

Naming Consistency5/5

All four tools follow a consistent verb_noun snake_case pattern (read_manifest, get_schema, extract_post, lookup_post). No mixed conventions or vague verbs.

Tool Count4/5

Four tools is a tight, well-scoped set for an Instagram post extraction utility. It is on the lean side but each tool earns its place, with no redundancy.

Completeness4/5

The surface covers the full round-trip: discover the contract (get_schema), fetch metadata (lookup_post), fetch media (extract_post), and re-read prior output (read_manifest). No obvious dead ends, though there is no batch/multi-post operation for repeated use.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to scrape Instagram Reels and public profile data (metadata, engagement, owner info) without the official API, via tools for scraping, status checks, cookie import, stopping, and exporting results.
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Enables MCP clients like Claude and Cursor to query public Instagram data read-only — profiles, posts, reels, stories, highlights, comments, likers, followers, hashtags, places, audio and search — over local stdio without writing files or storing anything.
    28
    72 PyPI
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to retrieve and analyze Instagram posts and profiles, including captions, media, video frames, transcripts, and engagement metrics, through MCP tools.
    MIT