Skip to content
Public Signal Labs

MCP technical reference

Public Signal provides 29 MCP tools, including 19 discovery tools, for collecting available Instagram data and working with saved research. Start every research request with instagram_get_guidance.

Credit overview

A standard data lookup or page fetch costs 0.25 credits. Some tools perform an initial account lookup, fetch several pages, or enrich each discovered profile. Those steps add together as shown below. Reading and organizing saved research is free.

A page is one fetched batch of search results, media, or interactions of varying size. Do not assume a particular page size.

A paid discovery call starts only while the balance is at least 1 credit; below that every input comes back not_started with error_code: "insufficient_credits" and an upgrade_url, and nothing is stored or charged. Once started, each provider request is checked against the spendable balance before it is sent, and each input's credits are charged when that input finishes, so the balance drops as inputs complete. When the balance can no longer pay for the next lookup or page, that input stops early and keeps everything it already collected: its result is partial, its coverage reports stop_reason: "credits_exhausted", and its entry in results carries error_code: "insufficient_credits" and an upgrade_url. A call that reaches its time limit likewise stops requesting and returns what it has, with stop_reason: "time_limit".

ToolBase credit calculation
instagram_get_guidanceCost: actual model cost, typically under 0.01 credits per call. Nothing is reserved; it starts only while the balance is at least 1 credit.
instagram_search_accountsCost: 0.25 credits per search page, plus 0.25 per returned account when enrich_profiles=true.
instagram_search_reelsCost: 0.25 credits per search page fetched.
instagram_get_hashtag_mediaCost: 0.25 credits per page fetched; every call, continuations included, fetches at least one page per selected stream.
instagram_get_related_profilesCost: 0.50 credits per seed, plus 0.25 per returned profile when enrich_profiles=true.
instagram_get_profileCost: 0.25 credits per profile, not-found included.
instagram_get_mediaCost: 0.25 credits per media, not-found included.
instagram_get_profile_mediaCost: 0.25 credits per page, plus 0.25 to resolve a handle or URL; numeric IDs skip it.
instagram_calculate_profile_metricsCost: 0 from fresh observations; otherwise 0.25 per profile lookup and 0.25 per feed page, at most 0.50.
instagram_get_tagged_mediaCost: 0.25 credits per page, plus 0.25 to resolve a handle or URL; numeric IDs skip it.
instagram_get_commentsCost: 0.25 credits per top-level page plus 0.25 per reply page; each comment with replies needs at least one.
instagram_get_likersCost: 0.25 credits per input, whatever results_limit.
instagram_get_followersCost: 0.25 credits per page plus 0.25 to resolve the handle; a resumed segment skips resolution.
instagram_get_followingCost: 0.25 credits per page plus 0.25 to resolve the handle; a resumed segment skips resolution.
instagram_search_locationsCost: 0.25 credits per input (one search, no paging).
instagram_get_locationCost: 0.25 credits per input, also when not found.
instagram_get_location_mediaCost: 0.25 credits per page; resuming mid-page buys that page again.
instagram_get_storiesCost: 0.50 credits per tray or known Story, plus 0.25 to resolve a handle or URL; a private tray costs 0.25.
instagram_get_highlightsCost: 0.50 credits per list page plus 0.25 to resolve a handle or URL, skipped for numeric IDs and resumes.
instagram_get_highlight_storiesCost: 0.25 credits per input.
instagram_workspace_queryCost: free, even at zero balance.
instagram_get_account_statusCost: free, even at zero balance.
instagram_workspace_writeCost: free, even at zero balance.
instagram_workspace_applyCost: the applied tool's price for every input at the given options.
instagram_workspace_decision_modelCost: actual model cost, typically 0.005–0.008 credits per row. Nothing is reserved; a row starts only while the balance is at least 1 credit, and it is charged its actual model cost afterwards.
instagram_workspace_llmCost: actual model cost, typically 0.02–0.04 credits per row, double when rewritten. Nothing is reserved; a row starts only while the balance is at least 1 credit, and it is charged its actual model cost afterwards.
instagram_run_codeCost: free; calls inside bill as their own tools - 0.25 credits per provider request, model sweeps at token cost.
instagram_watch_programCost: free; the program keeps spending while it runs.
instagram_abort_programCost: free; provider requests already sent stay charged.

Not-found and other billable provider errors still cost credits. A private Story tray (403) costs 0.25 credits for the tray, plus handle resolution when needed; a Highlight-list 403/404 costs 0.25 for that list request. Resuming a partially consumed hashtag, follower, following or location-media page buys that page again. Recalling comments, profile media or tagged media starts at the first page. Model-tool ranges are measured typical costs, not fixed prices.

Guidance

instagram_get_guidance

Get the prepared guide for one research request before starting on it.

TypeSafe's Jev 1.13 decision model (typesafe/jev-1.13) reads the request and picks one of Public Signal's prepared guides: a question one direct lookup answers (single_fetch: facts, metrics, or the newest posts or Stories of one or a few accounts, posts or places the caller names), the people interacting with named accounts (interactions), finding accounts that match a brief, including engagement or recent-activity gates and a per-account text such as an outreach draft (find_accounts), or general for anything else. A pick below 0.5 confidence returns general. The guide comes back whole, opening with a line that names it: the route, which steps run together in one instagram_run_code program and which are direct tool calls, and for programs a tested example program whose input block (search terms, gates, judgement questions, target, budget, per-account text) the caller fills in from the request. The guide does not restate the request; the caller takes its gates, deliverables and count from the request itself. The tool is stateless: it reads no saved research and needs no collection. usage reports the decision's token counts and duration; prompt_sha256 names the version of the guide catalog; billing_pending: true appears when the model charge has not settled yet, and charged_credits is then 0.

instagram_get_guidance(request) -> GuidanceResult

Parameters

  • request — required string, 1–8,000 characters; the caller's request, verbatim.

Example request

{"request": "Please find 5 German natural skincare brands with 10k-100k followers and a public contact email."}

Example result

{
  "guidance": "Guide: find_accounts (confidence 0.93)\n\n## Before you start\n...\n## Route\n...\n## Example program\n...",
  "model": "typesafe/jev-1.13",
  "usage": {"input_tokens": 950, "cached_input_tokens": 0, "output_tokens": 12, "reasoning_tokens": 0, "duration_seconds": 0.8},
  "credits": {"charged_credits": "0.005", "remaining_credits": "94.9", "as_of_utc": "2026-09-29T12:00:00+00:00"},
  "prompt_sha256": "f9d01d4ed11071781dcae4ebe964ec914744b7cd0b6d9e146c6663a054a1de2f"
}

Discovery tools

Every returned account and named interaction participant becomes an account row, including sparse search hits, media owners/coauthors, commenters, likers, followers, people followed, tagged and mentioned people. Interaction participants carry only id, username, full_name and avatar; their list privacy/verified flags stay on the activity row and never overwrite profile facts. Bio, counts and published contact come only from instagram_get_profile (0.25 credits per person): apply it with instagram_workspace_apply to the rows you need with follower_count IS NULL and no is_available. is_available = false means the account was looked up and not found, so a second lookup finds nothing. New rows start discovered; status changes only through an explicit instagram_workspace_write, never on reacquisition or identity merges.

Discovery results are compact receipts, not the acquired rows. Both are saved in the collection: instagram_workspace_query reads facts and operation coverage, and a call that passes include_rows: true gets each input's stored rows back with the receipt, when the whole call fits one query result. Receipts count changes to accounts, media, activity, places, stories, and highlights; the corresponding worksheets keep these evidence kinds separate. Every media acquisition also writes an account row for each media owner and coauthor. Request objects reject unknown fields. A batch whose inputs repeat is refused whole before paid acquisition; profile, location, Highlight and Story identifiers are normalized first (e.g. @Alice equals alice), while hashtags, search text and media URLs or shortcodes are compared as written, so pass each target in one form.

instagram_search_accounts

Find public accounts by ranked keyword matches. Without enrichment each hit holds identity only (handle, name, private/verified flags, avatar); enrich_profiles runs instagram_get_profile on every returned account for bio, counts, category, links and published contact, at 0.25 credits each, the same price as looking hits up later. Search results are saved in the collection; the response is a receipt.

instagram_search_accounts(requests, include_rows=false, collection_id=null) -> AcquisitionReceipt

Parameters

  • collection_id — optional string, default null; existing owned three-word collection ID (≤36 characters). Omit to create a collection.
  • include_rows — optional boolean, default false; return each input's stored rows when the whole call fits one query result (2,000 rows / 2 MB), otherwise query the collection. Inline rows omit server-owned bookkeeping and null promoted columns.
  • requests — required array of 1–50 distinct request objects.
  • requests[].query — required string, 1–100 characters; keyword search text.
  • requests[].max_pages — optional integer, default 1, range 1–10; the number of accounts per page varies.
  • requests[].enrich_profiles — optional boolean, default false; run instagram_get_profile on each hit (bio, counts, contact), 0.25 credits each.

Example request

{
  "requests": [{"query": "coffee roaster berlin", "max_pages": 1, "enrich_profiles": false}]
}

Example result

{
  "collection_id": "maple-otter-lantern",
  "status": "succeeded",
  "changes": {
    "accounts": {"added": 2, "updated": 0},
    "media": {"added": 0, "updated": 0},
    "activity": {"added": 0, "updated": 0},
    "places": {"added": 0, "updated": 0},
    "stories": {"added": 0, "updated": 0},
    "highlights": {"added": 0, "updated": 0}
  },
  "results": [{"index": 0, "status": "succeeded", "operation_id": "acorn-badger-candle"}],
  "credits": {"charged_credits": "0.25", "remaining_credits": "99.75", "as_of_utc": "2026-09-29T12:00:00+00:00"}
}

instagram_search_reels

Find public Reels by keyword and save their captions, owners, dates, and available engagement counts.

instagram_search_reels(requests, include_rows=false, collection_id=null) -> AcquisitionReceipt

Parameters

  • collection_id — optional string, default null; existing owned three-word collection ID (≤36 characters). Omit to create a collection.
  • include_rows — optional boolean, default false; return each input's stored rows when the whole call fits one query result (2,000 rows / 2 MB), otherwise query the collection. Inline rows omit server-owned bookkeeping and null promoted columns.
  • requests — required array of 1–50 distinct request objects.
  • requests[].query — required string, 1–100 characters; Reel search text.
  • requests[].max_pages — optional integer, default 1, range 1–10; maximum search pages to fetch.

Example request

{
  "collection_id": "maple-otter-lantern",
  "requests": [{"query": "latte art berlin", "max_pages": 1}]
}

Example result

{
  "collection_id": "maple-otter-lantern",
  "status": "succeeded",
  "changes": {
    "accounts": {"added": 2, "updated": 0},
    "media": {"added": 2, "updated": 0},
    "activity": {"added": 0, "updated": 0},
    "places": {"added": 0, "updated": 0},
    "stories": {"added": 0, "updated": 0},
    "highlights": {"added": 0, "updated": 0}
  },
  "results": [{"index": 0, "status": "succeeded", "operation_id": "birch-falcon-bridge"}],
  "credits": {"charged_credits": "0.25", "remaining_credits": "99.75", "as_of_utc": "2026-09-29T12:00:00+00:00"}
}

instagram_get_hashtag_media

Collect public posts and/or Reels for a hashtag into the saved research collection.

instagram_get_hashtag_media(requests, include_rows=false, collection_id=null) -> AcquisitionReceipt

Parameters

  • collection_id — optional string, default null; existing owned three-word collection ID (≤36 characters). Omit to create a collection.
  • include_rows — optional boolean, default false; return each input's stored rows when the whole call fits one query result (2,000 rows / 2 MB), otherwise query the collection. Inline rows omit server-owned bookkeeping and null promoted columns.
  • requests — required array of 1–50 distinct request objects.
  • requests[].hashtag — required string, 1–101 characters; hashtag with or without #.
  • requests[].results_limit — optional integer, default 25, range 1–10,000; combined saved post/Reel limit.
  • requests[].get_posts — optional boolean, default true; include posts. At least one media selector must be enabled.
  • requests[].get_reels — optional boolean, default true; include Reels.
  • requests[].continuation_token — optional string or null, default null; opaque token up to 524,288 characters, valid for 15 minutes. Reuse only with the same hashtag and media selectors.

Example request

{
  "collection_id": "maple-otter-lantern",
  "requests": [{"hashtag": "latteart", "results_limit": 2, "get_posts": true, "get_reels": true}]
}

Example result

{
  "collection_id": "maple-otter-lantern",
  "status": "succeeded",
  "changes": {
    "accounts": {"added": 2, "updated": 0},
    "media": {"added": 2, "updated": 0},
    "activity": {"added": 0, "updated": 0},
    "places": {"added": 0, "updated": 0},
    "stories": {"added": 0, "updated": 0},
    "highlights": {"added": 0, "updated": 0}
  },
  "results": [{"index": 0, "status": "succeeded", "operation_id": "cedar-fox-basket"}],
  "credits": {"charged_credits": "0.5", "remaining_credits": "99.5", "as_of_utc": "2026-09-29T12:00:00+00:00"}
}

Find accounts recommended alongside a seed profile. Without enrichment each suggestion holds identity only (handle, name, private/verified flags, avatar); enrich_profiles runs instagram_get_profile on every suggestion for bio, counts, category, links and published contact, at 0.25 credits each, the same price as looking them up later.

instagram_get_related_profiles(requests, include_rows=false, collection_id=null) -> AcquisitionReceipt

Parameters

  • collection_id — optional string, default null; existing owned three-word collection ID (≤36 characters). Omit to create a collection.
  • include_rows — optional boolean, default false; return each input's stored rows when the whole call fits one query result (2,000 rows / 2 MB), otherwise query the collection. Inline rows omit server-owned bookkeeping and null promoted columns.
  • requests — required array of 1–50 distinct request objects.
  • requests[].profile — required string, 1–200 characters; handle, @handle, or canonical profile URL. Numeric profile IDs are rejected.
  • requests[].max_results — optional integer, default 20, range 1–80; trim the recommendation list to this many profiles.
  • requests[].enrich_profiles — optional boolean, default false; run instagram_get_profile on each suggestion (bio, counts, contact), 0.25 credits each.

Example request

{
  "collection_id": "maple-otter-lantern",
  "requests": [{"profile": "example.coffee", "max_results": 2, "enrich_profiles": false}]
}

Example result

{
  "collection_id": "maple-otter-lantern",
  "status": "succeeded",
  "changes": {
    "accounts": {"added": 2, "updated": 0},
    "media": {"added": 0, "updated": 0},
    "activity": {"added": 0, "updated": 0},
    "places": {"added": 0, "updated": 0},
    "stories": {"added": 0, "updated": 0},
    "highlights": {"added": 0, "updated": 0}
  },
  "results": [{"index": 0, "status": "succeeded", "operation_id": "hazel-robin-compass"}],
  "credits": {"charged_credits": "0.5", "remaining_credits": "99.5", "as_of_utc": "2026-09-29T12:00:00+00:00"}
}

instagram_get_profile

Fetch the full public profile of known accounts: biography, follower/following/media counts, category, external URL and bio links, published email/phone/business address, and private/verified/business flags. Accounts found by search, suggestions or interactions hold identity only until looked up here; enrich_profiles on the search tools runs this same lookup. For more than 50 accounts, or rows chosen in SQL, use instagram_workspace_apply.

instagram_get_profile(profiles: string[], include_rows=false, collection_id=null) -> AcquisitionReceipt

Parameters

  • collection_id — optional string, default null; existing owned three-word collection ID (≤36 characters). Omit to create a collection.
  • include_rows — optional boolean, default false; return each input's stored rows when the whole call fits one query result (2,000 rows / 2 MB), otherwise query the collection. Inline rows omit server-owned bookkeeping and null promoted columns.
  • profiles — required array of 1–50 distinct strings; handles, canonical profile URLs, or numeric profile IDs. This tool uses profiles, not requests.

Example request

{
  "collection_id": "maple-otter-lantern",
  "profiles": ["example.coffee"],
  "include_rows": true
}

Example result

{
  "collection_id": "maple-otter-lantern",
  "status": "succeeded",
  "changes": {
    "accounts": {"added": 0, "updated": 1},
    "media": {"added": 0, "updated": 0},
    "activity": {"added": 0, "updated": 0},
    "places": {"added": 0, "updated": 0},
    "stories": {"added": 0, "updated": 0},
    "highlights": {"added": 0, "updated": 0}
  },
  "results": [
    {
      "index": 0,
      "status": "succeeded",
      "operation_id": "willow-panda-beacon",
      "rows": [
        {
          "item_id": "5f0c2e9a-6a1d-4c1e-9d1f-0b7f3c2a1e44",
          "kind": "account",
          "entity_key": "id:1234567",
          "status": "discovered",
          "reason": "Discovered; not yet reviewed against the brief.",
          "created_at": "2026-09-29T12:00:00.000000Z",
          "updated_at": "2026-09-29T12:00:00.000000Z",
          "id": "1234567",
          "account_id": "1234567",
          "username": "example.coffee",
          "full_name": "Example Coffee",
          "biography": "Roastery in Berlin.",
          "profile_url": "https://www.instagram.com/example.coffee/",
          "category": "Coffee shop",
          "follower_count": 18400,
          "is_private": false,
          "facts": {
            "schema_version": 2,
            "id": "1234567",
            "username": "example.coffee",
            "full_name": "Example Coffee",
            "follower_count": 18400,
            "category": "Coffee shop",
            "biography": "Roastery in Berlin."
          },
          "custom": {},
          "judgments": {},
          "generations": {}
        }
      ]
    }
  ],
  "credits": {"charged_credits": "0.25", "remaining_credits": "99.75", "as_of_utc": "2026-09-29T12:00:00+00:00"}
}

instagram_get_media

Fetch details for known public posts or Reels, including available captions, counts, coauthors, user tags, and location.

instagram_get_media(media, include_rows=false, collection_id=null) -> AcquisitionReceipt

Parameters

  • collection_id — optional string, default null; existing owned three-word collection ID (≤36 characters). Omit to create a collection.
  • include_rows — optional boolean, default false; return each input's stored rows when the whole call fits one query result (2,000 rows / 2 MB), otherwise query the collection. Inline rows omit server-owned bookkeeping and null promoted columns.
  • media — required array of 1–50 distinct strings, each 1–500 characters: post/Reel URLs (/p/, /reel/, /reels/), case-sensitive shortcodes, or numeric media IDs. Use one form per media; a URL and the shortcode of the same post are not recognized as duplicates.

Example request

{
  "collection_id": "maple-otter-lantern",
  "media": ["https://www.instagram.com/p/AbCdEf12345/"]
}

Example result

{
  "collection_id": "maple-otter-lantern",
  "status": "succeeded",
  "changes": {
    "accounts": {"added": 1, "updated": 0},
    "media": {"added": 1, "updated": 0},
    "activity": {"added": 0, "updated": 0},
    "places": {"added": 0, "updated": 0},
    "stories": {"added": 0, "updated": 0},
    "highlights": {"added": 0, "updated": 0}
  },
  "results": [{"index": 0, "status": "succeeded", "operation_id": "elm-otter-ribbon"}],
  "credits": {"charged_credits": "0.25", "remaining_credits": "99.75", "as_of_utc": "2026-09-29T12:00:00+00:00"}
}

instagram_get_profile_media

Collect a profile’s feed posts or Reels and save feed-coverage information.

instagram_get_profile_media(requests, include_rows=false, collection_id=null) -> AcquisitionReceipt

Parameters

  • collection_id — optional string, default null; existing owned three-word collection ID (≤36 characters). Omit to create a collection.
  • include_rows — optional boolean, default false; return each input's stored rows when the whole call fits one query result (2,000 rows / 2 MB), otherwise query the collection. Inline rows omit server-owned bookkeeping and null promoted columns.
  • requests — required array of 1–50 distinct request objects.
  • requests[].profile — required string, 1–200 characters; handle, @handle, canonical URL, or numeric profile ID.
  • requests[].media_type — optional string, default "all"; one of all, posts, or reels. posts filters Reels out of fetched feed pages.
  • requests[].results_limit — optional integer, default 25, range 1–1,000; maximum saved media rows.
  • requests[].max_pages — optional integer, default 3, range 1–100; maximum media pages to fetch.

Example request

{
  "collection_id": "maple-otter-lantern",
  "requests": [{"profile": "example.coffee", "media_type": "all", "results_limit": 2, "max_pages": 1}]
}

Example result

{
  "collection_id": "maple-otter-lantern",
  "status": "succeeded",
  "changes": {
    "accounts": {"added": 0, "updated": 1},
    "media": {"added": 2, "updated": 0},
    "activity": {"added": 0, "updated": 0},
    "places": {"added": 0, "updated": 0},
    "stories": {"added": 0, "updated": 0},
    "highlights": {"added": 0, "updated": 0}
  },
  "results": [{"index": 0, "status": "succeeded", "operation_id": "maple-raven-lantern"}],
  "credits": {"charged_credits": "0.5", "remaining_credits": "99.5", "as_of_utc": "2026-09-29T12:00:00+00:00"}
}

instagram_calculate_profile_metrics

Calculate engagement rate and mean likes, comments, and Reel plays from observations in this research, fetching only missing inputs (one profile lookup and/or one feed page). Engagement rate is 100 × the median likes of the latest 12 dated posts the profile owns or coauthors in the last two calendar months, divided by current followers; a null metric means unavailable, not zero. Metrics and sample information are saved on the account. With include_rows the receipt carries each measured profile's numbers; the sampled posts and the people tagged in them stay in the workspace.

instagram_calculate_profile_metrics(profiles, max_cache_age_seconds=3600, include_rows=false, collection_id=null) -> AcquisitionReceipt

Parameters

  • collection_id — optional string, default null; existing owned three-word collection ID (≤36 characters). Omit to create a collection.
  • include_rows — optional boolean, default false; return one metrics object per measured profile: item_id, username, account_id, follower_count, metrics_status, engagement_rate_pct, engagement_rate_pct_exact, average_likes, average_comments, average_reel_plays, sample_size, window_start, window_end and latest_post_taken_at. Without it, read the metrics from the workspace.
  • profiles — required array of 1–50 distinct strings, each 1–200 characters; handles, canonical profile URLs, or numeric profile IDs.
  • max_cache_age_seconds — optional integer, default 3600, minimum 0; maximum age of reusable observations in this research. Zero forces fresh lookups; it does not set a post-publication window.

Example request

{
  "collection_id": "maple-otter-lantern",
  "profiles": ["example.coffee"],
  "max_cache_age_seconds": 0,
  "include_rows": true
}

Example result

{
  "collection_id": "maple-otter-lantern",
  "status": "succeeded",
  "changes": {
    "accounts": {"added": 0, "updated": 1},
    "media": {"added": 12, "updated": 0},
    "activity": {"added": 0, "updated": 0},
    "places": {"added": 0, "updated": 0},
    "stories": {"added": 0, "updated": 0},
    "highlights": {"added": 0, "updated": 0}
  },
  "results": [
    {
      "index": 0,
      "status": "succeeded",
      "operation_id": "oak-heron-anchor",
      "rows": [
        {
          "item_id": "5f0c2e9a-6a1d-4c1e-9d1f-0b7f3c2a1e44",
          "username": "example.coffee",
          "account_id": "1234567",
          "follower_count": 18400,
          "metrics_status": "complete",
          "engagement_rate_pct": 2.5,
          "engagement_rate_pct_exact": "2.500",
          "average_likes": 425.0,
          "average_comments": 18.0,
          "average_reel_plays": 9100.0,
          "sample_size": 12,
          "window_start": "2026-07-29T12:00:00Z",
          "window_end": "2026-09-29T12:00:00Z",
          "latest_post_taken_at": "2026-09-27T17:04:11Z"
        }
      ]
    }
  ],
  "credits": {"charged_credits": "0.5", "remaining_credits": "99.5", "as_of_utc": "2026-09-29T12:00:00+00:00"}
}

instagram_get_tagged_media

Collect media from a profile’s Tagged tab (posts and Reels the profile is tagged in, usually owned by others; not a search for @mentions) and save tagged-coverage information.

instagram_get_tagged_media(requests, include_rows=false, collection_id=null) -> AcquisitionReceipt

Parameters

  • collection_id — optional string, default null; existing owned three-word collection ID (≤36 characters). Omit to create a collection.
  • include_rows — optional boolean, default false; return each input's stored rows when the whole call fits one query result (2,000 rows / 2 MB), otherwise query the collection. Inline rows omit server-owned bookkeeping and null promoted columns.
  • requests — required array of 1–50 distinct request objects.
  • requests[].profile — required string, 1–200 characters; handle, @handle, canonical URL, or numeric profile ID.
  • requests[].results_limit — optional integer, default 25, range 1–1,000; maximum saved tagged-media rows.
  • requests[].max_pages — optional integer, default 3, range 1–100; maximum Tagged-tab pages to fetch.

Example request

{
  "collection_id": "maple-otter-lantern",
  "requests": [{"profile": "example.coffee", "results_limit": 2, "max_pages": 1}]
}

Example result

{
  "collection_id": "maple-otter-lantern",
  "status": "succeeded",
  "changes": {
    "accounts": {"added": 2, "updated": 1},
    "media": {"added": 2, "updated": 0},
    "activity": {"added": 0, "updated": 0},
    "places": {"added": 0, "updated": 0},
    "stories": {"added": 0, "updated": 0},
    "highlights": {"added": 0, "updated": 0}
  },
  "results": [{"index": 0, "status": "succeeded", "operation_id": "pine-lynx-mirror"}],
  "credits": {"charged_credits": "0.5", "remaining_credits": "99.5", "as_of_utc": "2026-09-29T12:00:00+00:00"}
}

instagram_get_comments

Collect visible comments and optional replies on a post or Reel as saved activity records: text, time, likes on the comment, declared reply count, and the author's handle, name, verified flag and avatar. Authors' bio, follower counts and contact are not delivered; apply instagram_get_profile to them with instagram_workspace_apply (0.25 credits per person). A media's declared comment_count can exceed what Instagram serves; provider_has_more: false in the operation coverage means there is nothing more to fetch.

instagram_get_comments(requests, include_rows=false, collection_id=null) -> AcquisitionReceipt

Parameters

  • collection_id — optional string, default null; existing owned three-word collection ID (≤36 characters). Omit to create a collection.
  • include_rows — optional boolean, default false; return each input's stored rows when the whole call fits one query result (2,000 rows / 2 MB), otherwise query the collection. Inline rows omit server-owned bookkeeping and null promoted columns.
  • requests — required array of 1–50 distinct request objects.
  • requests[].media — required string, 1–500 characters; post/Reel URL, shortcode, or numeric media ID.
  • requests[].results_limit — optional integer, default 25, range 1–1,000; top-level comment limit.
  • requests[].include_replies — optional boolean, default false; fetch replies to the selected comments.
  • requests[].replies_limit — optional integer, default 25, range 0–1,000; total reply limit across threads. Zero skips reply fetching. When replies are enabled and this is positive, max_pages + results_limit + replies_limit must be ≤500.
  • requests[].max_pages — optional integer, default 3, range 1–100; top-level comment-page limit (about 15 comments per page, so the default yields about 45 and caps results_limit); not a reply-page or credit cap.

Example request

{
  "collection_id": "maple-otter-lantern",
  "requests": [
    {
      "media": "https://www.instagram.com/p/AbCdEf12345/",
      "results_limit": 2,
      "include_replies": false,
      "max_pages": 1
    }
  ]
}

Example result

{
  "collection_id": "maple-otter-lantern",
  "status": "succeeded",
  "changes": {
    "accounts": {"added": 2, "updated": 0},
    "media": {"added": 0, "updated": 0},
    "activity": {"added": 2, "updated": 0},
    "places": {"added": 0, "updated": 0},
    "stories": {"added": 0, "updated": 0},
    "highlights": {"added": 0, "updated": 0}
  },
  "results": [{"index": 0, "status": "succeeded", "operation_id": "fern-marten-spark"}],
  "credits": {"charged_credits": "0.25", "remaining_credits": "99.75", "as_of_utc": "2026-09-29T12:00:00+00:00"}
}

instagram_get_likers

Collect the available visible-liker list for a post or Reel as saved like activity: each liker's handle, name, private/verified flags and avatar. This is Instagram's visible subset from one fetch, not the full like count; total_like_count and available_liker_count in the operation coverage show what is missing. Like time, bio, counts and contact are not delivered; apply instagram_get_profile to the likers with instagram_workspace_apply (0.25 credits per person).

instagram_get_likers(requests, include_rows=false, collection_id=null) -> AcquisitionReceipt

Parameters

  • collection_id — optional string, default null; existing owned three-word collection ID (≤36 characters). Omit to create a collection.
  • include_rows — optional boolean, default false; return each input's stored rows when the whole call fits one query result (2,000 rows / 2 MB), otherwise query the collection. Inline rows omit server-owned bookkeeping and null promoted columns.
  • requests — required array of 1–50 distinct request objects.
  • requests[].source — required string, 1–300 characters; public post/Reel URL or case-sensitive shortcode. Numeric media IDs are not accepted.
  • requests[].results_limit — optional integer, default 25, range 1–3,000; limit the rows saved from the single fetched list.

Example request

{
  "collection_id": "maple-otter-lantern",
  "requests": [{"source": "https://www.instagram.com/p/AbCdEf12345/", "results_limit": 2}]
}

Example result

{
  "collection_id": "maple-otter-lantern",
  "status": "succeeded",
  "changes": {
    "accounts": {"added": 2, "updated": 0},
    "media": {"added": 0, "updated": 0},
    "activity": {"added": 2, "updated": 0},
    "places": {"added": 0, "updated": 0},
    "stories": {"added": 0, "updated": 0},
    "highlights": {"added": 0, "updated": 0}
  },
  "results": [{"index": 0, "status": "succeeded", "operation_id": "moss-wren-kettle"}],
  "credits": {"charged_credits": "0.25", "remaining_credits": "99.75", "as_of_utc": "2026-09-29T12:00:00+00:00"}
}

instagram_get_followers

Collect observed followers as saved relationship activity, following pagination when available: each follower's handle, name, private/verified flags and avatar. Follow date, bio, counts and contact are not delivered; apply instagram_get_profile to the followers with instagram_workspace_apply (0.25 credits per person).

instagram_get_followers(requests, include_rows=false, collection_id=null) -> AcquisitionReceipt

Parameters

  • collection_id — optional string, default null; existing owned three-word collection ID (≤36 characters). Omit to create a collection.
  • include_rows — optional boolean, default false; return each input's stored rows when the whole call fits one query result (2,000 rows / 2 MB), otherwise query the collection. Inline rows omit server-owned bookkeeping and null promoted columns.
  • requests — required array of 1–50 distinct request objects.
  • requests[].account — required string, 1–30 characters; plain handle (letters, digits, ., _) without @. URLs and numeric profile IDs are rejected.
  • requests[].results_limit — optional integer, default 25, range 1–20,000; maximum saved relationship rows.
  • requests[].continuation_token — optional string or null, default null; opaque token up to 4,096 characters: the next_continuation_token from the previous call's operation-row coverage for the same account, valid for 24 hours.

Example request

{
  "collection_id": "maple-otter-lantern",
  "requests": [{"account": "example.coffee", "results_limit": 2}]
}

Example result

{
  "collection_id": "maple-otter-lantern",
  "status": "succeeded",
  "changes": {
    "accounts": {"added": 2, "updated": 1},
    "media": {"added": 0, "updated": 0},
    "activity": {"added": 2, "updated": 0},
    "places": {"added": 0, "updated": 0},
    "stories": {"added": 0, "updated": 0},
    "highlights": {"added": 0, "updated": 0}
  },
  "results": [{"index": 0, "status": "succeeded", "operation_id": "ivy-beaver-sail"}],
  "credits": {"charged_credits": "0.5", "remaining_credits": "99.5", "as_of_utc": "2026-09-29T12:00:00+00:00"}
}

instagram_get_following

Collect observed accounts followed by a profile as saved relationship activity, following pagination when available: each followed account's handle, name, private/verified flags and avatar. Follow date, bio, counts and contact are not delivered; apply instagram_get_profile to them with instagram_workspace_apply (0.25 credits per person).

instagram_get_following(requests, include_rows=false, collection_id=null) -> AcquisitionReceipt

Parameters

  • collection_id — optional string, default null; existing owned three-word collection ID (≤36 characters). Omit to create a collection.
  • include_rows — optional boolean, default false; return each input's stored rows when the whole call fits one query result (2,000 rows / 2 MB), otherwise query the collection. Inline rows omit server-owned bookkeeping and null promoted columns.
  • requests — required array of 1–50 distinct request objects.
  • requests[].account — required string, 1–30 characters; plain handle (letters, digits, ., _) without @. URLs and numeric profile IDs are rejected.
  • requests[].results_limit — optional integer, default 25, range 1–20,000; maximum saved relationship rows.
  • requests[].continuation_token — optional string or null, default null; opaque token up to 4,096 characters: the next_continuation_token from the previous call's operation-row coverage for the same account, valid for 24 hours.

Example request

{
  "collection_id": "maple-otter-lantern",
  "requests": [{"account": "example.coffee", "results_limit": 2}]
}

Example result

{
  "collection_id": "maple-otter-lantern",
  "status": "succeeded",
  "changes": {
    "accounts": {"added": 2, "updated": 1},
    "media": {"added": 0, "updated": 0},
    "activity": {"added": 2, "updated": 0},
    "places": {"added": 0, "updated": 0},
    "stories": {"added": 0, "updated": 0},
    "highlights": {"added": 0, "updated": 0}
  },
  "results": [{"index": 0, "status": "succeeded", "operation_id": "reed-wolf-button"}],
  "credits": {"charged_credits": "0.5", "remaining_credits": "99.5", "as_of_utc": "2026-09-29T12:00:00+00:00"}
}

instagram_search_locations

Find candidate Instagram places by name and save returned place facts without buying separate detail lookups. A matching name is not proof of a matching city; check the acquired place facts before using its media as local evidence.

instagram_search_locations(requests, include_rows=false, collection_id=null) -> AcquisitionReceipt

Parameters

  • collection_id — optional string, default null; existing owned three-word collection ID (≤36 characters). Omit to create a collection.
  • include_rows — optional boolean, default false; return each input's stored rows when the whole call fits one query result (2,000 rows / 2 MB), otherwise query the collection. Inline rows omit server-owned bookkeeping and null promoted columns.
  • requests — required array of 1–50 distinct request objects.
  • requests[].query — required string, 1–200 characters; non-empty place-name search, not coordinates.
  • requests[].results_limit — optional integer, default 25, range 1–1,000; maximum saved candidate places.

Example request

{"requests": [{"query": "Austin coffee", "results_limit": 2}]}

Example result

{
  "collection_id": "maple-otter-lantern",
  "status": "succeeded",
  "changes": {
    "accounts": {"added": 0, "updated": 0},
    "media": {"added": 0, "updated": 0},
    "activity": {"added": 0, "updated": 0},
    "places": {"added": 2, "updated": 0},
    "stories": {"added": 0, "updated": 0},
    "highlights": {"added": 0, "updated": 0}
  },
  "results": [{"index": 0, "status": "succeeded", "operation_id": "coral-penguin-bell"}],
  "credits": {"charged_credits": "0.25", "remaining_credits": "99.75", "as_of_utc": "2026-10-05T12:00:00+00:00"}
}

instagram_get_location

Look up returned facts for one known Instagram location ID or location URL. This saves a Place row; it does not fetch location media or synthesize a map or image.

instagram_get_location(requests, include_rows=false, collection_id=null) -> AcquisitionReceipt

Parameters

  • collection_id — optional string, default null; existing owned three-word collection ID (≤36 characters). Omit to create a collection.
  • include_rows — optional boolean, default false; return each input's stored rows when the whole call fits one query result (2,000 rows / 2 MB), otherwise query the collection. Inline rows omit server-owned bookkeeping and null promoted columns.
  • requests — required array of 1–50 distinct request objects.
  • requests[].location — required string, 1–512 characters; numeric location ID or Instagram /explore/locations/<id>/ URL.

Example request

{"collection_id": "maple-otter-lantern", "requests": [{"location": "123456789"}]}

Example result

{
  "collection_id": "maple-otter-lantern",
  "status": "succeeded",
  "changes": {
    "accounts": {"added": 0, "updated": 0},
    "media": {"added": 0, "updated": 0},
    "activity": {"added": 0, "updated": 0},
    "places": {"added": 0, "updated": 1},
    "stories": {"added": 0, "updated": 0},
    "highlights": {"added": 0, "updated": 0}
  },
  "results": [{"index": 0, "status": "succeeded", "operation_id": "amber-turtle-key"}],
  "credits": {"charged_credits": "0.25", "remaining_credits": "99.5", "as_of_utc": "2026-10-05T12:00:00+00:00"}
}

instagram_get_location_media

Collect posts and Reels from one place's recent or top stream, without automatically buying place details or full owner profiles. Page coverage includes pages_completed and, when available, next_continuation_token.

instagram_get_location_media(requests, include_rows=false, collection_id=null) -> AcquisitionReceipt

Parameters

  • collection_id — optional string, default null; existing owned three-word collection ID (≤36 characters). Omit to create a collection.
  • include_rows — optional boolean, default false; return each input's stored rows when the whole call fits one query result (2,000 rows / 2 MB), otherwise query the collection. Inline rows omit server-owned bookkeeping and null promoted columns.
  • requests — required array of 1–50 distinct request objects.
  • requests[].location — required string, 1–512 characters; numeric location ID or Instagram location URL.
  • requests[].stream — optional string, default "recent"; "recent" or "top", one stream per request.
  • requests[].results_limit — optional integer, default 25, range 1–1,000; maximum saved media rows.
  • requests[].max_pages — optional integer, default 1, range 1–10; page-fetch bound for this call.
  • requests[].continuation_token — optional string or null, default null; opaque token up to 360,000 characters, valid for 15 minutes. Reuse only with the same normalized location and stream.

Example request

{"collection_id": "maple-otter-lantern", "requests": [{"location": "123456789", "stream": "recent", "results_limit": 2, "max_pages": 1}]}

Example result

{
  "collection_id": "maple-otter-lantern",
  "status": "succeeded",
  "changes": {
    "accounts": {"added": 1, "updated": 0},
    "media": {"added": 2, "updated": 0},
    "activity": {"added": 0, "updated": 0},
    "places": {"added": 0, "updated": 1},
    "stories": {"added": 0, "updated": 0},
    "highlights": {"added": 0, "updated": 0}
  },
  "results": [{"index": 0, "status": "succeeded", "operation_id": "pearl-dolphin-cloud"}],
  "credits": {"charged_credits": "0.25", "remaining_credits": "99.25", "as_of_utc": "2026-10-05T12:00:00+00:00"}
}

instagram_get_stories

Snapshot one profile's active Story tray or look up one known Story. An empty or unavailable tray is not proof of inactivity: coverage records outcome and expired_count. Facts and evidence remain saved after Story expiry, but media URLs are temporary and cached images do not establish that a Story is still active.

instagram_get_stories(requests, include_rows=false, collection_id=null) -> AcquisitionReceipt

Parameters

  • collection_id — optional string, default null; existing owned three-word collection ID (≤36 characters). Omit to create a collection.
  • include_rows — optional boolean, default false; return each input's stored rows when the whole call fits one query result (2,000 rows / 2 MB), otherwise query the collection. Inline rows omit server-owned bookkeeping and null promoted columns.
  • requests — required array of 1–50 distinct request objects; each must select exactly one of profile or story.
  • requests[].profile — optional string or null, default null, 1–200 characters; handle, canonical profile URL, or numeric profile ID for an active tray.
  • requests[].story — optional string or null, default null, 1–2,048 characters; known numeric Story ID, <story_id>_<owner_id>, or Instagram Story URL.
  • requests[].results_limit — optional integer, default 25, range 1–100; maximum saved Stories. No automatic profile, place, or media enrichment.

Example request

{"collection_id": "maple-otter-lantern", "requests": [{"profile": "example.coffee", "results_limit": 2}]}

Example result

{
  "collection_id": "maple-otter-lantern",
  "status": "succeeded",
  "changes": {
    "accounts": {"added": 0, "updated": 1},
    "media": {"added": 0, "updated": 0},
    "activity": {"added": 0, "updated": 0},
    "places": {"added": 0, "updated": 0},
    "stories": {"added": 2, "updated": 0},
    "highlights": {"added": 0, "updated": 0}
  },
  "results": [{"index": 0, "status": "succeeded", "operation_id": "violet-sparrow-drum"}],
  "credits": {"charged_credits": "0.75", "remaining_credits": "98.5", "as_of_utc": "2026-10-05T12:00:00+00:00"}
}

instagram_get_highlights

List one profile's Highlight collections with acquired titles, covers, and declared counts. Listing never expands saved Stories; select a collection before calling instagram_get_highlight_stories. An unavailable list, including a not-found response, is not proof that the profile has no Highlights.

instagram_get_highlights(requests, include_rows=false, collection_id=null) -> AcquisitionReceipt

Parameters

  • collection_id — optional string, default null; existing owned three-word collection ID (≤36 characters). Omit to create a collection.
  • include_rows — optional boolean, default false; return each input's stored rows when the whole call fits one query result (2,000 rows / 2 MB), otherwise query the collection. Inline rows omit server-owned bookkeeping and null promoted columns.
  • requests — required array of 1–50 distinct request objects.
  • requests[].profile — required string, 1–200 characters; handle, canonical profile URL, or numeric profile ID.
  • requests[].max_pages — optional integer, default 1, range 1–10; list-page bound per segment. It may change on continuation.
  • requests[].continuation_token — optional string or null, default null; opaque h1. token up to 3,000,000 characters, valid for 15 minutes. Reuse only with the same normalized profile; this tool lists metadata, without Story expansion.

Coverage records pages_completed and provider_has_more. Retrieve facts.coverage.<execution_id>.next_continuation_token from the saved operation row using instagram_workspace_query; it is not copied into account facts or automatically resumed. Otherwise coverage provides continuation_unavailable_reason; provider_has_more alone does not promise a usable token.

Example request

{"collection_id": "maple-otter-lantern", "requests": [{"profile": "example.coffee", "max_pages": 1}]}

Example result

{
  "collection_id": "maple-otter-lantern",
  "status": "succeeded",
  "changes": {
    "accounts": {"added": 0, "updated": 1},
    "media": {"added": 0, "updated": 0},
    "activity": {"added": 0, "updated": 0},
    "places": {"added": 0, "updated": 0},
    "stories": {"added": 0, "updated": 0},
    "highlights": {"added": 2, "updated": 0}
  },
  "results": [{"index": 0, "status": "succeeded", "operation_id": "clover-rabbit-feather"}],
  "credits": {"charged_credits": "0.75", "remaining_credits": "97.75", "as_of_utc": "2026-10-05T12:00:00+00:00"}
}

instagram_get_highlight_stories

Acquire the saved Stories in one selected Highlight, retaining the collection metadata and Story memberships. Coverage records declared_item_count and expansion_status; the declared count is not a guarantee of that many delivered Stories. Saved Story facts and evidence remain in research, while image URLs and disposable cached previews may become unavailable.

instagram_get_highlight_stories(requests, include_rows=false, collection_id=null) -> AcquisitionReceipt

Parameters

  • collection_id — optional string, default null; existing owned three-word collection ID (≤36 characters). Omit to create a collection.
  • include_rows — optional boolean, default false; return each input's stored rows when the whole call fits one query result (2,000 rows / 2 MB), otherwise query the collection. Inline rows omit server-owned bookkeeping and null promoted columns.
  • requests — required array of 1–50 distinct request objects.
  • requests[].highlight — required string, 1–512 characters; numeric Highlight ID, highlight:<id>, or Instagram /stories/highlights/<id>/ URL. No automatic owner, place, or media enrichment.

Example request

{"collection_id": "maple-otter-lantern", "requests": [{"highlight": "highlight:17890000000000000"}]}

Example result

{
  "collection_id": "maple-otter-lantern",
  "status": "succeeded",
  "changes": {
    "accounts": {"added": 0, "updated": 1},
    "media": {"added": 0, "updated": 0},
    "activity": {"added": 0, "updated": 0},
    "places": {"added": 0, "updated": 0},
    "stories": {"added": 3, "updated": 0},
    "highlights": {"added": 0, "updated": 1}
  },
  "results": [{"index": 0, "status": "succeeded", "operation_id": "olive-koala-cup"}],
  "credits": {"charged_credits": "0.25", "remaining_credits": "97.5", "as_of_utc": "2026-10-05T12:00:00+00:00"}
}

Saved research and account tools

instagram_workspace_query

Read, filter, and aggregate saved research with owner- and collection-scoped PostgreSQL queries.

After a failed query or the shared 15-second budget, every later query is not_started.

instagram_workspace_query(collection_id: string, queries) -> WorkspaceQueryResult

Parameters

  • collection_id — required string; existing owned three-word collection ID (≤36 characters).
  • queries — required array of 1–50 query objects.
  • queries[].sql — required string, 1–20,000 characters; one read-only SELECT against research_items. Writes, locks, schema-qualified tables, and unsafe functions are rejected. Each query returns at most 2,000 rows (more are cut off with truncated: true) and at most 2,000,000 result bytes (a larger result fails the query, so later queries in the batch are not_started), with up to 5 seconds per statement and 15 seconds for the batch. Rows come back as objects keyed by output column name, so two output columns with the same name fail the query. numeric values are JSON numbers (integers when whole, null for NaN/Infinity) and timestamps are ISO-8601 UTC text.
  • queries[].parameters — optional array, default []; positional values for $1, $2, … placeholders.

Example request

{
  "collection_id": "maple-otter-lantern",
  "queries": [
    {
      "sql": "SELECT username, follower_count, public_email FROM research_items WHERE kind = 'account' AND follower_count >= $1 ORDER BY follower_count DESC, username LIMIT 20",
      "parameters": [1000]
    }
  ]
}

Example result

{
  "collection_id": "maple-otter-lantern",
  "status": "succeeded",
  "results": [
    {
      "index": 0,
      "status": "succeeded",
      "rows": [{"username": "example.coffee", "follower_count": 12500, "public_email": null}],
      "row_count": 1,
      "truncated": false,
      "command": "SELECT"
    }
  ],
  "credits": {"charged_credits": "0", "remaining_credits": "100", "as_of_utc": "2026-09-29T12:00:00+00:00"}
}

instagram_get_account_status

Check your current spendable credit balance and obtain an upgrade link when the balance is low.

instagram_get_account_status() -> AccountStatusResult

Parameters

  • No parameters; send an empty object {}.

Example request

{}

Example result

{
  "credits": {"charged_credits": "0", "remaining_credits": "100", "as_of_utc": "2026-09-29T12:00:00+00:00"},
  "upgrade_url": null
}

instagram_workspace_write

Atomically write saved account decisions and reasons, and customer-visible custom fields on any saved row.

A SQL NULL patch cell leaves that column unchanged. A non-null status still requires a reason in the same row; custom merges at the top level, and a null value inside it is stored, not deleted. There is no row limit; a repeated item_id, a reason over 16 KB or a custom over 32 KB refuses the write and nothing is written. One write runs for at most 60 seconds.

instagram_workspace_write(collection_id: string, query, parameters=[]) -> WorkspaceWriteResult

Parameters

  • collection_id — required string; existing owned three-word collection ID (≤36 characters).
  • query — required nonempty string; a read-only SELECT with item_id first, then columns aliased exactly status, reason, or custom. custom works on account, media, activity, place, story, and highlight rows; status and reason on accounts only. Status is discovered, under_review, qualified, or disqualified; an explicit status needs a reason in the same row. custom is a JSON object whose top-level keys merge, with supplied nested values replacing their subtrees.
  • parameters — optional array, default []; positional SQL values.

Example request

{
  "collection_id": "maple-otter-lantern",
  "query": "SELECT item_id, 'under_review' AS status, 'Needs manual review of recent content.' AS reason FROM research_items WHERE kind = 'account' AND username = $1",
  "parameters": ["example.coffee"]
}

Publishing generated fields is an ordinary write:

SELECT item_id,
       (SELECT jsonb_object_agg(f.key, f.value->'value')
        FROM jsonb_each(generations->'profile_summary'->'fields') AS f) AS custom
FROM research_items WHERE kind = 'account' AND generations ? 'profile_summary'

Example result

{
  "collection_id": "maple-otter-lantern",
  "matched": 1,
  "updated": 1,
  "status_counts": {"under_review": 1},
  "credits": {"charged_credits": "0", "remaining_credits": "100", "as_of_utc": "2026-09-29T12:00:00+00:00"}
}

instagram_workspace_apply

Run one acquisition tool once for every row of a saved-research query.

Each column is named exactly like one of the applied tool's input fields: the target (profile, account, media, source, location, story, highlight, query, hashtag) and any per-input option (results_limit, media_type, max_pages, continuation_token, …). instagram_get_profile and instagram_calculate_profile_metrics take a profile column, instagram_get_media a media column. A NULL cell means the tool default. Nothing is inferred from the row, so any stored value can feed any tool; a numeric account ID skips the 0.25-credit handle resolution of the tools that take one (instagram_get_profile_media, instagram_get_tagged_media, instagram_get_stories, instagram_get_highlights), while instagram_get_followers and instagram_get_following accept only plain handles and resolve them except on resumed segments (instagram_get_profile costs 0.25 per profile either way). Every row is checked by the tool's own input rules first: an unknown column, an invalid value, or a duplicate input (after normalization, @Alice equals alice) refuses the whole call before anything is charged. There is no row limit; inputs run in batches of 50, one after another. Empty selections return status: "empty" without a charge; otherwise status is succeeded when every input completed, partial when some input yielded data, or failed. issues lists up to 50 failed or partial inputs, and when the balance refuses a batch no further batch starts and upgrade_url is returned.

instagram_workspace_apply(collection_id: string, tool, query, parameters=[], include_rows=false, max_cache_age_seconds=null, max_credits=null) -> WorkspaceApplyResult

Parameters

  • collection_id — required string; existing owned three-word collection ID (≤36 characters).
  • tool — required string; any acquisition tool, from instagram_search_accounts to instagram_get_highlight_stories.
  • query — required nonempty string; a read-only SELECT whose rows are the tool's inputs.
  • parameters — optional array, default []; positional SQL values.
  • include_rows — optional boolean, default false; return the stored rows when they fit one query result (2,000 rows / 2 MB).
  • max_cache_age_seconds — optional integer, default null (the tool's own default), minimum 0; accepted only with tool: "instagram_calculate_profile_metrics", and any other tool refuses the call.
  • max_credits — optional positive number; no further batch starts once this call has been charged that much (a running batch can go over by its own cost).

Example request

{
  "collection_id": "maple-otter-lantern",
  "tool": "instagram_get_profile_media",
  "query": "SELECT coalesce(account_id, username) AS profile, 12 AS results_limit FROM research_items WHERE kind = 'account' AND status = 'qualified'",
  "max_credits": 20
}

Example result

{
  "collection_id": "maple-otter-lantern",
  "status": "partial",
  "changes": {
    "accounts": {"added": 0, "updated": 3},
    "media": {"added": 34, "updated": 2},
    "activity": {"added": 0, "updated": 0},
    "places": {"added": 0, "updated": 0},
    "stories": {"added": 0, "updated": 0},
    "highlights": {"added": 0, "updated": 0}
  },
  "inputs": 3,
  "not_started": 0,
  "issues": [{"input": "example.private", "status": "failed", "operation_id": "walnut-swan-chest", "error": "The profile is private."}],
  "credits": {"charged_credits": "1.5", "remaining_credits": "98.5", "as_of_utc": "2026-09-29T12:00:00+00:00"}
}

instagram_workspace_decision_model

Judge saved rows with TypeSafe's Jev 1.13 decision model (typesafe/jev-1.13) and save the typed answers under judgments -> label -> answers.

Jev is a decision model, not a chat model: for each row it reads one JSON state, {brief, <kind>: the selected columns}, plus the questions, and returns option keys with probabilities. It writes no text and no explanations, and sees nothing but that state. It is the cheap way to screen many rows: about 0.005–0.008 credits per row, 16 rows at a time, each answer with a probability that SQL can threshold. The SELECT returns item_id first and aliased evidence columns, at most 8 KB per row; any kind but operation rows can be judged. There is no row limit; the selecting read is bounded at 8 MiB, and a duplicate item_id refuses the call before any model call. An empty match is a zero-row result. Answers never change status, reason or custom. A repeated label replaces the whole stored entry on every row that got at least one answer (answered or partial); rows with no usable answer keep their earlier answers. Rows are stored and model charges settled as they finish, so a stopped sweep keeps completed work and resumes by selecting the rows still without the label.

instagram_workspace_decision_model(collection_id: string, label, query, brief, questions, parameters=[], include_rows=false, max_credits=null) -> WorkspaceDecisionModelResult

Parameters

  • collection_id — required string; existing owned three-word collection ID (≤36 characters).
  • label — required string, 3–40 characters, matching ^[A-Za-z][A-Za-z0-9_]*$; key for the saved judgment sweep.
  • query — required nonempty string; a read-only SELECT with item_id first, followed by aliased evidence columns.
  • brief — required string, 1–4,000 characters; evaluation context and requirements.
  • questions — required object with 1–6 named question specifications; each name uses only letters, digits, and underscores.
  • questions.<name>.type — required string; noul for a boolean-style judgment with probability, choice for named options, or score for ordered levels.
  • questions.<name>.instructions — required string, 1–600 characters; what to evaluate.
  • questions.<name>.criteria — default {}; choice requires 2–8 option-name-to-meaning entries; score requires 2–8 ordered level-name-to-meaning entries, lowest first; noul optionally accepts meanings keyed by true/false; every key and meaning must be non-blank.
  • parameters — optional array, default []; positional SQL values.
  • include_rows — optional boolean, default false; return each row's stored answers when they fit one query result (2,000 rows / 2 MB).
  • max_credits — optional number; no further row starts once this call has been charged that much (rows already running finish).

Example request

{
  "collection_id": "maple-otter-lantern",
  "label": "coffee_fit",
  "query": "SELECT item_id, biography FROM research_items WHERE kind = 'account' AND username = $1",
  "parameters": ["example.coffee"],
  "brief": "Identify accounts that focus on coffee.",
  "questions": {"coffee": {"type": "noul", "instructions": "Does the biography indicate a focus on coffee?"}},
  "include_rows": true
}

Example result

{
  "collection_id": "maple-otter-lantern",
  "label": "coffee_fit",
  "model": "typesafe/jev-1.13",
  "matched": 1,
  "answered": 1,
  "partial": 0,
  "failed": 0,
  "not_started": 0,
  "questions": {"coffee": {"true": 1}},
  "issues": [],
  "rows": [{"item_id": "c2d6c0d4-5d1e-4c55-9d43-6f1f0b8a7e21", "status": "answered", "answers": {"coffee": {"value": true, "noul": 0.93}}}],
  "credits": {"charged_credits": "0.006", "remaining_credits": "99.994", "as_of_utc": "2026-09-29T12:00:00+00:00"}
}

instagram_workspace_llm

Write structured content for saved rows with Z.ai's GLM 5.3 Flash language model (z-ai/glm-5.3-flash) and store it under generations -> label -> fields. This creates content; it does not send messages or perform outreach.

Each row is one independent request: a fixed system prompt (use only facts in the state, null when unsupported, finished text without placeholders, no shared template) and one user message holding {brief, <kind>: the selected columns}. The reply is forced into a strict JSON schema built from the declared fields, at temperature 0.6 and low reasoning effort, with at most 2,048 output tokens. The model has no tools, no web access and no memory of other rows, but it can draw on its training, so review the output before publishing. Select item_id first and aliased evidence columns, at most 8 KB per row; there is no row limit, the selecting read is bounded at 8 MiB, and a duplicate item_id refuses the call before any model call. Invalid output triggers at most one additional billed request rewriting the entire row, then rejected values are dropped. Any {…} or […] span counts as a placeholder, as do TODO/TBD and similar unfinished text. Oversize text, a trailing ellipsis, invalid numeric values and malformed JSON can trigger this rewrite; null required fields only mark the row partial and do not trigger retry. Completed rows and settled model spend are retained on a stop. Output stays in generations until published with instagram_workspace_write. The result reports fields.<name> as filled/null counts (plus median_chars for text), and similarity.<name> as median_pairwise, max_pairwise, and near_duplicate_rows for text fields with at least two values; median_pairwise near 1 means one template. Check the returned rows and similarity before publishing.

instagram_workspace_llm(collection_id: string, label, query, brief, fields, parameters=[], include_rows=false, max_credits=null) -> WorkspaceLlmResult

Parameters

  • collection_id — required string; existing owned three-word collection ID (≤36 characters).
  • label — required string, 3–40 characters, matching ^[A-Za-z][A-Za-z0-9_]*$; reusing a label replaces output only on rows that produce output.
  • query — required nonempty string; a read-only SELECT with item_id first and readable aliases for evidence columns.
  • brief — required string, 1–4,000 characters; context, voice, and requirements.
  • fields — required object with 1–6 named field specifications; each name uses only letters, digits, and underscores.
  • fields.<name>.type — required string; text, number, integer, or list.
  • fields.<name>.instructions — required string, 1–600 characters; what the field should contain.
  • fields.<name>.required — optional boolean, default true; a null value marks the row partial, without retry. Every field can return null when evidence is insufficient.
  • fields.<name>.max_chars — default 400 for text and 200 per list item; integer range 1–2,000.
  • fields.<name>.minimum, fields.<name>.maximum — optional numeric bounds for number or integer.
  • fields.<name>.max_items — optional integer for lists, default 5, range 1–10.
  • parameters — optional array, default []; positional SQL values.
  • include_rows — optional boolean, default false; return each row's stored fields when they fit one query result (2,000 rows / 2 MB).
  • max_credits — optional number; no further row starts once this call has been charged that much (rows already running finish).

Example request

{
  "collection_id": "maple-otter-lantern",
  "label": "profile_summary",
  "query": "SELECT item_id, biography FROM research_items WHERE kind = 'account' AND username = $1",
  "parameters": ["example.coffee"],
  "brief": "Write a short factual summary using only the biography.",
  "fields": {
    "summary": {"type": "text", "instructions": "Summarize the account in one short sentence.", "max_chars": 80}
  }
}

Example result

{
  "collection_id": "maple-otter-lantern",
  "label": "profile_summary",
  "model": "z-ai/glm-5.3-flash",
  "matched": 1,
  "generated": 1,
  "partial": 0,
  "failed": 0,
  "not_started": 0,
  "fields": {"summary": {"filled": 1, "null": 0, "median_chars": 22}},
  "similarity": {},
  "issues": [],
  "credits": {"charged_credits": "0.03", "remaining_credits": "99.97", "as_of_utc": "2026-09-29T12:00:00+00:00"}
}

Program tools

instagram_run_code

Run restricted Python against the saved collection, returning the last expression or a run ID to watch. The call waits up to 120 seconds, then returns the result or a running run_id; programs run at most 5 minutes, with at most 2 active per account. Program reports include settled acquisition and model-sweep spend in credits.charged_credits, the current balance, and the latest 25 events oldest first; skipped_events counts older events. Acquisitions report status, charge and the non-zero changes; writes report counts (model sweeps also report settled spend); sql and instagram_get_account_status calls leave no event; event data is kept up to 4 KB, then becomes cut-off text. Compare event sequence numbers because watches repeat events. instagram_run_code and instagram_watch_program each hold one of the account's 3 concurrent slots shared with instagram_workspace_query and instagram_workspace_write for as long as they wait; a call that cannot get one within 20 seconds fails with "This account already has 3 research queries, programs or watches in flight". If no sandbox worker frees up within 15 seconds, the run ends failed with "All sandbox workers are busy; retry in a few seconds."

Every research tool except instagram_get_guidance and the three program tools is an async function of its exact MCP name with the same arguments except collection_id; no short aliases exist. The helpers are await sql(query, parameters=None) -> list[dict] (free SELECT; parameters is a list with one entry per placeholder, and a query error raises ValueError), await checkpoint(label, data=None) -> float (records an event, label kept to 200 characters, and returns the remaining credit balance), and to_json(value) -> str (json.dumps(value, default=str)). Inside programs instagram_get_account_status() also returns collection_id, and credit amounts are decimal strings. Programs may import json, re, math, datetime, itertools, collections, functools and asyncio (gather and run only). Generator expressions work; unsupported Python includes yield, del, match, datetime.now(), asyncio.sleep/Semaphore, % formatting, class inheritance and custom exceptions, and json.dumps has no default= (use to_json). There is no file, network or environment access; errors return a traceback naming the failing line. Variables persist best-effort only within hosted conversations; external callers get a fresh session each run. Below 1 credit, the next paid call aborts the whole program. Acquisitions have no per-run input cap: every input of every gathered call is queued at once and provider requests are paced service-wide, so gather independent calls with asyncio.gather rather than awaiting them one by one.

instagram_run_code(collection_id: string, code, max_credits=null) -> ScratchpadResult

Parameters

  • collection_id — required string; existing owned three-word collection ID (≤36 characters).
  • code — required string, 1–64,000 characters; Python program using documented workspace helpers. The last expression (or last assigned name) is returned, intact up to 64 KB; larger values become cut-off text, not retrievable elsewhere. print() output is separate, kept up to 8 KB. truncated: true means the result, the print output, or both were cut.
  • max_credits — optional integer, minimum 1; checked before each paid acquisition function call against completed acquisition receipts. At the threshold it raises a catchable ValueError, not a whole-program stop. In-flight or gathered calls can overshoot; model sweeps are counted in run spend but excluded from this threshold.

Example request

{
  "collection_id": "maple-otter-lantern",
  "code": "await sql(\"SELECT status, count(*) AS accounts FROM research_items WHERE kind = 'account' GROUP BY status ORDER BY status\")"
}

Example result

{
  "run_id": "silver-waterfall-poppy",
  "collection_id": "maple-otter-lantern",
  "status": "succeeded",
  "events": [],
  "skipped_events": 0,
  "result": [{"status": "discovered", "accounts": 2}],
  "output": "",
  "truncated": false,
  "credits": {"charged_credits": "0", "remaining_credits": "100", "as_of_utc": "2026-09-29T12:00:00+00:00"}
}

instagram_watch_program

Observe an existing program’s progress or completion without starting its work again. The call waits up to 120 seconds, returning sooner if the run finishes, then reports the run's latest 25 events, including events an earlier call already returned; each event carries its sequence number.

instagram_watch_program(run_id) -> ScratchpadResult

Parameters

  • run_id — required string, 1–64 characters; owned run ID returned by instagram_run_code. No collection argument.

Example request

{"run_id": "silver-waterfall-poppy"}

Example result

{
  "run_id": "silver-waterfall-poppy",
  "collection_id": "maple-otter-lantern",
  "status": "succeeded",
  "events": [
    {"sequence": 1, "kind": "call", "label": "instagram_get_profile", "data": {"status": "succeeded", "charged_credits": "0.5", "changes": {"accounts": {"added": 2}}}},
    {"sequence": 2, "kind": "checkpoint", "label": "profiles", "data": {"looked_up": 2}}
  ],
  "skipped_events": 0,
  "result": [{"status": "discovered", "accounts": 2}],
  "output": "",
  "truncated": false,
  "credits": {"charged_credits": "0.5", "remaining_credits": "99.5", "as_of_utc": "2026-09-29T12:00:00+00:00"}
}

instagram_abort_program

Stop an owned program immediately. Completed writes stay committed; an in-flight acquisition is cancelled, provider requests already sent stay charged, and that input's rows are not stored.

instagram_abort_program(run_id, reason) -> ScratchpadResult

Parameters

  • run_id — required string, 1–64 characters; owned run ID to stop. No collection argument.
  • reason — required string, 1–400 characters; reason recorded with the run.

Example request

{"run_id": "brook-emerald-panda", "reason": "Stopped by user."}

Example result

{
  "run_id": "brook-emerald-panda",
  "collection_id": "maple-otter-lantern",
  "status": "aborted",
  "events": [{"sequence": 1, "kind": "checkpoint", "label": "started", "data": {"candidates": 40}}],
  "skipped_events": 0,
  "result": null,
  "output": "",
  "truncated": false,
  "error": "Stopped by user.",
  "credits": {"charged_credits": "0", "remaining_credits": "100", "as_of_utc": "2026-09-29T12:00:00+00:00"}
}