Contract
{
"type": "object",
"required": [
"hashtags"
],
"properties": {
"hashtags": {
"type": "array",
"items": {
"type": "string",
"pattern": "^\\s*#?[^!?.,:;\\-+=*&%$#@/~^|<>()[\\]{}\"'`]+$"
},
"description": "Type in or paste the hashtag with or without <code>#</code> symbol.<br><br> ⚠️ Each hashtag is treated as <strong>one single search term</strong>. If you put several words in one line (e.g. <code>travel photography food</code>), they'll be merged into a single hashtag (<code>#travelphotographyfood</code>). To search for terms separately, add each one as its own entry using the <strong>+ Add</strong> button — and use keyword search if you need multi-word phrases. <br><br>🔎 <strong>Searching by keyword?</strong> Add your keywords in this same field, then enable <strong>Scrape with a keyword instead of hashtag</strong> toggle below. You can add as many keywords as you like in one field, space-separated."
},
"resultsType": {
"enum": [
"posts",
"reels",
"stories"
],
"type": "string",
"default": "posts",
"enumTitles": [
"Scrape posts",
"Scrape reels",
" "
],
"description": "Choose whether to scrape posts or reels for the selected hashtags or keywords."
},
"resultsLimit": {
"type": "integer",
"default": 20,
"minimum": 1,
"description": "Set the maximum number of results (reels or posts) you want to scrape. If you set this to <code>5</code>, you will scrape 5 posts for each hashtag or keyword you provide. With this setup, if you decide to add <code>7</code> different hashtags or keywords, you will scrape <code>35</code> posts altogether.<br><br>⚠️ A single call only returns the first page of results for each hashtag or keyword; raising this value beyond that first page's size will not return more posts."
},
"keywordSearch": {
"type": "boolean",
"description": "Enable this option if you want to get posts or reels by a specific keyword instead of a hashtag. Add your keywords to the <strong>Hashtags or keywords</strong> field above. Unlike with hashtags, <b>multiple words are supported as keywords</b>, just add spaces between them. The dataset returned will be slightly different than the one produced by a hashtag search."
}
},
"additionalProperties": false
}{
"type": "object",
"required": [
"results"
],
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "The post's own Instagram media id."
},
"type": {
"type": "string",
"description": "Observed 'Image' and 'Sidecar' (multi-photo/video carousel)."
},
"error": {
"type": "string",
"description": "Present, instead of the post fields above, on the one item returned for a hashtag/keyword with no matches. Observed value: 'no_items'."
},
"caption": {
"type": "string",
"description": "The post's caption text; an empty string when the post has none."
},
"hashtags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Hashtags parsed from the caption; an empty array when none."
},
"mentions": {
"type": "array",
"items": {
"type": "string"
},
"description": "Other Instagram accounts' usernames tagged/mentioned in the caption text (e.g. '@natgeoindia'); an empty array when none."
},
"shortCode": {
"type": "string",
"description": "The post's short code, e.g. 'Ddd90kLRMaR'. Combine with 'https://www.instagram.com/p/' to build the post's public permalink."
},
"timestamp": {
"type": "string",
"description": "ISO 8601 publish time."
},
"childPosts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string"
},
"caption": {
"type": "string"
},
"hashtags": {
"type": "array",
"items": {
"type": "string"
}
},
"mentions": {
"type": "array",
"items": {
"type": "string"
}
},
"timestamp": {
"type": "string"
},
"productType": {
"type": "string",
"description": "Observed 'carousel_item' for a child photo/video."
},
"commentsCount": {
"type": "integer",
"minimum": 0
},
"dimensionsWidth": {
"type": "integer",
"minimum": 0
},
"dimensionsHeight": {
"type": "integer",
"minimum": 0
}
},
"additionalProperties": false
},
"description": "For a 'Sidecar' (multi-photo/video carousel) post, one entry per child photo/video; an empty array for every other post type -- confirmed against a real 2-photo Sidecar post in the natgeo-hashtag fixture."
},
"likesCount": {
"type": "integer",
"minimum": 0
},
"locationId": {
"type": "string",
"description": "The tagged location's own id. Present only when the post is location-tagged -- ABSENT entirely (not null) otherwise; confirmed absent on 2 of 5 posts and present on the other 3 in the real natgeo-hashtag fixture."
},
"productType": {
"type": "string",
"description": "Instagram's own internal post-format label, e.g. 'feed'."
},
"locationName": {
"type": "string",
"description": "The tagged location's display name; same absence behaviour as locationId."
},
"commentsCount": {
"type": "integer",
"minimum": 0
},
"ownerFullName": {
"type": "string",
"description": "The posting account's own display name."
},
"ownerUsername": {
"type": "string",
"description": "The posting account's own username."
},
"dimensionsWidth": {
"type": "integer",
"minimum": 0
},
"paidPartnership": {
"type": "boolean",
"description": "Whether the post is flagged as a paid partnership/brand collaboration."
},
"dimensionsHeight": {
"type": "integer",
"minimum": 0
},
"errorDescription": {
"type": "string",
"description": "Human-readable pair to `error`, e.g. 'Empty or private data for provided input'."
}
},
"additionalProperties": false
},
"description": "One entry per matching post, up to resultsLimit per hashtag/keyword -- or, for a hashtag/keyword with no matches, one item carrying only `error`/`errorDescription`."
}
},
"description": "Matching posts, wrapped in an object because a run's output is always a JSON object -- the provider's own response is a bare array. Up to resultsLimit entries per hashtag/keyword supplied. A hashtag with no matching posts returns one item carrying only `error`/`errorDescription`, no post fields.",
"additionalProperties": false
}Pricing
Every real charge, itemised. A model that quietly omits one is a slow financial leak, so nothing here is rolled up, and a charge that only applies to some inputs says so rather than being added in.
Prices in this catalog are the provider's own list price, not your bill: Omnial MCP charges provider cost plus a platform markup on top, so what you are charged is higher than the figure shown. For the exact amount a specific call will cost, run omnial_execute with dry_run: true; that number includes the markup and is what we hold while the call runs. It is a quote, not a cap on the charge.
| Charge | Rate |
|---|---|
Per call Flat, regardless of what comes back | $0.00470925 |
- Cost basis
- Not recorded
This tool's catalog entry does not record how its final bill is determined, so we will not tell you whether its cost is fixed before the call or reported by the provider afterwards. Either way what is held is a quote rather than a cap: you are charged what the call actually costs, bounded at 2x the quote.
- Updated
- Sep 23, 2026
