hasdata-instagram-mcp 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ ---
2
+ name: Something in the README is wrong
3
+ about: A tool table, a response sample or a documented behaviour does not match reality
4
+ labels: documentation
5
+ ---
6
+
7
+ **Where in the README**
8
+
9
+ Section or heading.
10
+
11
+ **What it says**
12
+
13
+ Quote the line.
14
+
15
+ **The call you made**
16
+
17
+ Tool name and arguments, or the equivalent REST URL with your key removed.
18
+
19
+ **What came back**
20
+
21
+ Trimmed response, with anything private removed.
@@ -0,0 +1,31 @@
1
+ # The tool contract is checked on a schedule as well as on push, because the upstream tool list
2
+ # can change without a single commit in this repository.
3
+ name: tool contract
4
+
5
+ on:
6
+ push:
7
+ branches: [main]
8
+ pull_request:
9
+ schedule:
10
+ - cron: '0 6 * * 1'
11
+ workflow_dispatch:
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ jobs:
17
+ contract:
18
+ runs-on: ubuntu-latest
19
+ timeout-minutes: 5
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - uses: actions/setup-node@v4
23
+ with:
24
+ node-version: '22'
25
+ # Forks cannot read repository secrets. The suite skips its live checks when the key is
26
+ # absent, so a pull request from a fork stays green instead of failing for a reason the
27
+ # contributor cannot fix.
28
+ - name: Assert the tool list still matches the README
29
+ env:
30
+ HASDATA_API_KEY: ${{ secrets.HASDATA_API_KEY }}
31
+ run: npm test
@@ -0,0 +1,65 @@
1
+ # Publishes the npm and PyPI wrapper packages on a version tag, using OIDC
2
+ # trusted publishing. No NPM_TOKEN or PYPI_TOKEN is stored anywhere: GitHub
3
+ # mints a short-lived OIDC token per run, and npmjs.org / pypi.org accept it
4
+ # because this repo + workflow are configured as trusted publishers.
5
+ #
6
+ # One-time setup, done once per package on the registries (not in this repo):
7
+ # npmjs.org -> package settings -> Trusted Publisher -> GitHub Actions,
8
+ # repo HasData/instagram-mcp, workflow publish.yml
9
+ # pypi.org -> the hasdata org -> Publishing -> add a trusted publisher
10
+ # (pending publisher works before the first release),
11
+ # repo HasData/instagram-mcp, workflow publish.yml
12
+ #
13
+ # The MCP registry entry (com.hasdata/instagram) is NOT published here. It uses
14
+ # domain auth, which would need the namespace-wide Ed25519 key as a secret in
15
+ # every repo. That key stays off CI; the registry entry is published by hand
16
+ # when server.json changes, after the package versions below are live.
17
+ #
18
+ # Release: bump nothing by hand. Tag the commit `vX.Y.Z` and push the tag; the
19
+ # tag is the single source of the version and is written into both manifests.
20
+
21
+ name: publish
22
+
23
+ on:
24
+ push:
25
+ tags: ['v*.*.*']
26
+
27
+ permissions:
28
+ contents: read
29
+ id-token: write
30
+
31
+ jobs:
32
+ npm:
33
+ runs-on: ubuntu-latest
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+ - uses: actions/setup-node@v4
37
+ with:
38
+ node-version: '22'
39
+ registry-url: 'https://registry.npmjs.org'
40
+ - name: Set version from the tag
41
+ run: npm version "${GITHUB_REF_NAME#v}" --no-git-tag-version --allow-same-version
42
+ - name: Publish to npm (OIDC, no token)
43
+ run: npm publish --access public
44
+
45
+ pypi:
46
+ runs-on: ubuntu-latest
47
+ steps:
48
+ - uses: actions/checkout@v4
49
+ - uses: actions/setup-python@v5
50
+ with:
51
+ python-version: '3.12'
52
+ - name: Set version from the tag
53
+ run: |
54
+ python - "${GITHUB_REF_NAME#v}" <<'PY'
55
+ import re, sys
56
+ v = sys.argv[1]
57
+ p = "pyproject.toml"
58
+ t = open(p, encoding="utf-8").read()
59
+ t = re.sub(r'(?m)^version = ".*"$', f'version = "{v}"', t, count=1)
60
+ open(p, "w", encoding="utf-8", newline="\n").write(t)
61
+ PY
62
+ - name: Build the wheel and sdist
63
+ run: pipx run build
64
+ - name: Publish to PyPI (OIDC, no token)
65
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,11 @@
1
+ node_modules/
2
+ package-lock.json
3
+ dist/
4
+ build/
5
+ *.egg-info/
6
+ __pycache__/
7
+ *.pyc
8
+ .env
9
+ .env.*
10
+ .DS_Store
11
+ *.log
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 HasData
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,449 @@
1
+ Metadata-Version: 2.5
2
+ Name: hasdata-instagram-mcp
3
+ Version: 1.0.0
4
+ Summary: MCP server for public Instagram data through HasData's hosted API. Reads public data only.
5
+ Project-URL: Homepage, https://hasdata.com/apis/instagram-profile-api
6
+ Project-URL: Repository, https://github.com/HasData/instagram-mcp
7
+ License: MIT
8
+ License-File: LICENSE
9
+ Keywords: hasdata,instagram,mcp,model-context-protocol,posts,profile
10
+ Requires-Python: >=3.10
11
+ Requires-Dist: mcp-proxy>=0.12.0
12
+ Requires-Dist: mcp<2,>=1.17
13
+ Description-Content-Type: text/markdown
14
+
15
+ # Instagram MCP Server
16
+
17
+ <!-- mcp-name: com.hasdata/instagram -->
18
+
19
+ A hosted Model Context Protocol (MCP) server that gives Claude, Cursor, Windsurf and any other MCP client two read-only Instagram tools. Look up a public profile by handle, and walk its public post feed, as structured JSON.
20
+
21
+ It reads public data about accounts. It does not act as an account. There is nothing to connect and no account of yours involved anywhere in the flow.
22
+
23
+ ```
24
+ https://mcp.hasdata.com/api/mcp?apis=instagram
25
+ ```
26
+
27
+ [![Glama score](https://glama.ai/mcp/servers/HasData/instagram-mcp/badges/score.svg)](https://glama.ai/mcp/servers/HasData/instagram-mcp)
28
+ [![tool contract](https://github.com/HasData/instagram-mcp/actions/workflows/contract.yml/badge.svg)](https://github.com/HasData/instagram-mcp/actions/workflows/contract.yml)
29
+ [![MCP](https://img.shields.io/badge/MCP-remote%20%7C%20streamable%20HTTP-6366f1?style=flat-square)](https://modelcontextprotocol.io)
30
+ [![Tools](https://img.shields.io/badge/tools-2-10b981?style=flat-square)](#tools)
31
+ [![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](LICENSE)
32
+
33
+ ## Contents
34
+
35
+ - [What you need](#what-you-need)
36
+ - [Quick start](#quick-start)
37
+ - [Example prompts](#example-prompts)
38
+ - [Tools](#tools)
39
+ - [Errors and failure paths](#errors-and-failure-paths)
40
+ - [Pricing, free tier and limits](#pricing-free-tier-and-limits)
41
+ - [Tool selection](#tool-selection)
42
+ - [How it compares](#how-it-compares)
43
+ - [FAQ](#faq)
44
+ - [HasData links](#hasdata-links)
45
+ - [Development](#development)
46
+ - [Contributing](#contributing)
47
+ - [License](#license)
48
+
49
+ ## What you need
50
+
51
+ An MCP client that speaks streamable HTTP with custom headers. A HasData API key from the [dashboard](https://app.hasdata.com/sign-up?utm_source=github&utm_medium=syndication&utm_campaign=instagram-mcp), free to create with no card, and the trial covers 100 calls. Nothing else. This is a remote server. There is no package to install, no container to run and no local process that has to stay up.
52
+
53
+ ## Quick start
54
+
55
+ | | |
56
+ | :--- | :--- |
57
+ | URL | `https://mcp.hasdata.com/api/mcp?apis=instagram` |
58
+ | Transport | HTTP, streamable |
59
+ | Auth header | `x-api-key: HASDATA_API_KEY` |
60
+
61
+ The server URL is the same for every client. We run it hands-on in Claude Code and Claude Desktop. The other blocks follow each client's own documented format for a remote server.
62
+
63
+ Clients with OAuth support can add the same URL as a connector and sign in without putting a key in a config file.
64
+
65
+ <details>
66
+ <summary><b>Claude Code</b></summary>
67
+
68
+ ```bash
69
+ claude mcp add --transport http instagram "https://mcp.hasdata.com/api/mcp?apis=instagram" \
70
+ --header "x-api-key: HASDATA_API_KEY"
71
+ ```
72
+
73
+ </details>
74
+
75
+ <details>
76
+ <summary><b>Claude Desktop</b></summary>
77
+
78
+ Claude Desktop loads only local (stdio) servers from its config file, so a remote server is reached through the `mcp-remote` bridge. Node has to be on the machine.
79
+
80
+ `claude_desktop_config.json`:
81
+
82
+ ```json
83
+ {
84
+ "mcpServers": {
85
+ "instagram": {
86
+ "command": "npx",
87
+ "args": [
88
+ "-y",
89
+ "mcp-remote",
90
+ "https://mcp.hasdata.com/api/mcp?apis=instagram",
91
+ "--header",
92
+ "x-api-key:HASDATA_API_KEY"
93
+ ]
94
+ }
95
+ }
96
+ }
97
+ ```
98
+
99
+ The `x-api-key:` value carries no space after the colon. Claude Desktop passes the argument without a shell, and a space splits the header. A client with OAuth support can instead add the URL as a custom connector and skip the bridge.
100
+
101
+ </details>
102
+
103
+ <details>
104
+ <summary><b>Cursor</b></summary>
105
+
106
+ `.cursor/mcp.json`:
107
+
108
+ ```json
109
+ {
110
+ "mcpServers": {
111
+ "instagram": {
112
+ "url": "https://mcp.hasdata.com/api/mcp?apis=instagram",
113
+ "headers": { "x-api-key": "HASDATA_API_KEY" }
114
+ }
115
+ }
116
+ }
117
+ ```
118
+
119
+ </details>
120
+
121
+ <details>
122
+ <summary><b>Windsurf</b></summary>
123
+
124
+ `~/.codeium/windsurf/mcp_config.json`:
125
+
126
+ ```json
127
+ {
128
+ "mcpServers": {
129
+ "instagram": {
130
+ "serverUrl": "https://mcp.hasdata.com/api/mcp?apis=instagram",
131
+ "headers": { "x-api-key": "HASDATA_API_KEY" }
132
+ }
133
+ }
134
+ }
135
+ ```
136
+
137
+ </details>
138
+
139
+ <details>
140
+ <summary><b>Cline</b></summary>
141
+
142
+ ```json
143
+ {
144
+ "mcpServers": {
145
+ "instagram": {
146
+ "url": "https://mcp.hasdata.com/api/mcp?apis=instagram",
147
+ "type": "streamableHttp",
148
+ "headers": { "x-api-key": "HASDATA_API_KEY" },
149
+ "disabled": false
150
+ }
151
+ }
152
+ }
153
+ ```
154
+
155
+ </details>
156
+
157
+ <details>
158
+ <summary><b>VS Code</b></summary>
159
+
160
+ `.vscode/mcp.json`:
161
+
162
+ ```json
163
+ {
164
+ "servers": {
165
+ "instagram": {
166
+ "type": "http",
167
+ "url": "https://mcp.hasdata.com/api/mcp?apis=instagram",
168
+ "headers": { "x-api-key": "HASDATA_API_KEY" }
169
+ }
170
+ }
171
+ }
172
+ ```
173
+
174
+ </details>
175
+
176
+ <details>
177
+ <summary><b>Gemini CLI</b></summary>
178
+
179
+ `~/.gemini/settings.json`:
180
+
181
+ ```json
182
+ {
183
+ "mcpServers": {
184
+ "instagram": {
185
+ "httpUrl": "https://mcp.hasdata.com/api/mcp?apis=instagram",
186
+ "headers": { "x-api-key": "HASDATA_API_KEY" }
187
+ }
188
+ }
189
+ }
190
+ ```
191
+
192
+ </details>
193
+
194
+ ## Example prompts
195
+
196
+ Each of these is one tool call unless the count says otherwise.
197
+
198
+ > Pull the profile for `@nasa` and tell me the follower count, the category and every link in the bio.
199
+
200
+ *One call, 10 credits. For a public account the profile response already carries the twelve most recent posts, so a follow-up about recent activity needs no second call.*
201
+
202
+ > Compare `@nasa`, `@natgeo` and `@bbcearth` on followers, posts published and whether each is a business account.
203
+
204
+ *Three calls, 30 credits. One per handle.*
205
+
206
+ > Walk the last fifty posts from `@nasa` and list every hashtag with how often it appears.
207
+
208
+ *Five calls, 50 credits. Twelve posts arrive per call, and fifty takes five pages.*
209
+
210
+ > For the last twelve posts on `@natgeo`, give me likes, comments and the accounts mentioned in each caption.
211
+
212
+ *One call, 10 credits. Engagement counts and mentions come parsed in the post objects.*
213
+
214
+ Two things make these work. Hashtags and mentions arrive as arrays parsed out of the caption, and an agent counts them instead of running a regex over prose. And a profile lookup returns the recent feed in the same response. That is why so many research questions land in a single call.
215
+
216
+ ## Tools
217
+
218
+ Two tools, both read-only, both keyed on a public account handle. Samples below are trimmed from real calls, and the numbers in them move as accounts post. Read them as shapes. Each tool name links to its endpoint reference.
219
+
220
+ The samples are the payload, not the whole response. A `tools/call` result carries one text block, and that text is itself JSON holding `url`, `status`, `text` and `json`, with the scraped data under `json`. From a raw JSON-RPC response the path is `result.content[0].text`, parsed, then `.json`. A chat client unwraps that for you and code talking to the endpoint directly does not.
221
+
222
+ ### Get an Instagram profile
223
+
224
+ [`hasdata_instagram_profile_getInstagramProfile`](https://docs.hasdata.com/apis/instagram/profile?utm_source=github&utm_medium=syndication&utm_campaign=instagram-mcp)
225
+
226
+ One public profile by handle.
227
+
228
+ | Parameter | Type | Required | Notes |
229
+ | :--- | :--- | :--- | :--- |
230
+ | `handle` | string | yes | Username without the `@`, as it appears in the profile URL |
231
+
232
+ Returns `id`, `username`, `fullName`, `biography`, `businessCategory`, `verified`, `isBusinessAccount` and `isProfessionalAccount`, the counters `followersCount`, `followsCount`, `postsCount`, `highlightsCount` and `igtvVideoCount`, both `profilePicUrl` and `profilePicUrlHD`, and the arrays `latestPosts`, `latestIgtvVideos` and `relatedProfiles`.
233
+
234
+ The core identity fields and the follower and following counts come back for every public account. The fields beyond that depend on what the account itself exposes, so read the optional ones with a default.
235
+
236
+ > Links live in two fields that are not the same thing. `bioLinks` is the array of every link in the bio. `externalUrls` is a single string despite the plural name, and it holds the primary link, sometimes with a trailing slash the array version lacks. Read `bioLinks` when you want them all.
237
+
238
+ > `latestPosts` and `latestIgtvVideos` do not carry identical fields. Video entries add `taggedUsers`, and the post objects here omit the `productType` that the posts tool includes. Code that walks both arrays through one parser has to treat the extra keys as optional.
239
+
240
+ ```json
241
+ {
242
+ "id": "528817151",
243
+ "username": "nasa",
244
+ "fullName": "NASA",
245
+ "biography": "Making the seemingly impossible, possible. ✨",
246
+ "businessCategory": "Government Agencies",
247
+ "bioLinks": [
248
+ "https://www.nasa.gov",
249
+ "https://science.nasa.gov/mission/roman-space-telescope/",
250
+ "http://intern.nasa.gov"
251
+ ],
252
+ "externalUrls": "https://www.nasa.gov/",
253
+ "followersCount": 104397669,
254
+ "followsCount": 92,
255
+ "postsCount": 4887,
256
+ "verified": true,
257
+ "isBusinessAccount": true,
258
+ "latestPosts": [ "…twelve most recent posts, same shape as the posts tool…" ],
259
+ "relatedProfiles": [
260
+ { "id": "…", "username": "…", "fullName": "…", "profilePicUrl": "…" }
261
+ ]
262
+ }
263
+ ```
264
+
265
+ `relatedProfiles` is Instagram's own suggestion list for the account and runs to a few dozen entries. It is a cheap way to widen a competitor set without guessing handles.
266
+
267
+ ### Get Instagram posts
268
+
269
+ [`hasdata_instagram_posts_getInstagramPosts`](https://docs.hasdata.com/apis/instagram/posts?utm_source=github&utm_medium=syndication&utm_campaign=instagram-mcp)
270
+
271
+ The public post feed for one handle, page by page.
272
+
273
+ | Parameter | Type | Required | Notes |
274
+ | :--- | :--- | :--- | :--- |
275
+ | `handle` | string | yes | Username without the `@` |
276
+ | `limit` | number | | Approximate ceiling on posts in one response. Twelve is the real maximum, and larger values do not fetch more |
277
+ | `nextPageToken` | string | | The `pagination.nextPageToken` from the previous response |
278
+
279
+ > `limit` is a rough cap rather than an exact count. Twelve posts is one Instagram page and the hard ceiling for a single call, and `limit: 50` returns twelve. Below the ceiling the count lands near the number you asked for without always matching it, and how near depends on the account. Measured on `@nasa`, a limit of 2 returned 4 posts, 6 returned 6, 11 returned 10 and 13 returned 12. Treat it as "no more than roughly this many" and read the array length rather than assuming it.
280
+
281
+ > The response repeats the account's identity fields alongside the posts. `username`, `id`, `fullName`, `verified` and both avatar URLs arrive on every page. Handy for labelling rows, and worth knowing before you make a separate profile call to get them.
282
+
283
+ Each post carries `id`, `shortcode`, `caption`, `type`, `productType`, `hashtags`, `mentions`, `likesCount`, `commentsCount`, `timestamp`, `url`, `displayUrl`, `images`, `dimensionsWidth`, `dimensionsHeight`, `ownerId` and `ownerUsername`.
284
+
285
+ ```json
286
+ {
287
+ "username": "nasa",
288
+ "id": "528817151",
289
+ "fullName": "NASA",
290
+ "verified": true,
291
+ "latestPosts": [
292
+ {
293
+ "id": "3967213292204992434",
294
+ "shortcode": "DcOX3hWFiey",
295
+ "caption": "With your powers combined…\n\nThis colorful picture of the cosmos is the product of teamwork between our @NASAHubble, @NASAWebb, and @NASAChandraXray telescopes. […] \n\n#NASA #Universe #Nebula",
296
+ "type": "Image",
297
+ "hashtags": ["#NASA", "#Universe", "#Nebula"],
298
+ "mentions": ["@NASAHubble", "@NASAWebb", "@NASAChandraXray"],
299
+ "likesCount": 78412,
300
+ "commentsCount": 402,
301
+ "timestamp": "2026-08-18T16:02:11.000Z",
302
+ "url": "https://www.instagram.com/p/DcOX3hWFiey/"
303
+ }
304
+ ],
305
+ "pagination": {
306
+ "morePostsAvailable": true,
307
+ "nextPageToken": "3968050822236429248_528817151",
308
+ "hasdataLink": "https://api.hasdata.com/scrape/instagram/posts?handle=nasa&nextPageToken=3968050822236429248_528817151"
309
+ }
310
+ }
311
+ ```
312
+
313
+ Hashtags and mentions keep their `#` and `@` prefixes, which matters if you are joining them against a list you built yourself. `morePostsAvailable` is the flag to branch on when paging, and `hasdataLink` is the same next page expressed as a REST URL, useful when you want to reproduce an agent's call by hand.
314
+
315
+ ## Errors and failure paths
316
+
317
+ Your client almost never sees an HTTP error code from a tool call. The MCP layer answers 200 and puts the failure inside the result, with `isError` set to `true` and the reason as text. The agent reads a message where you might expect a status line.
318
+
319
+ **A wrong key surfaces as tool output, not as a failed connection.** Listing tools accepts any non-empty key, and the client completes its handshake and shows green. The first tool call then comes back with `isError: true` and the text `HasData API error: 401 Unauthorized`. Watch for that string, because nothing earlier in the flow reports the problem.
320
+
321
+ **A missing key is the one real HTTP error.** Authorization runs before any tool, and the connection itself fails with 401.
322
+
323
+ **An argument that breaks the schema is rejected before it becomes a request.** The server answers with `isError: true` and the text `MCP error -32602: Input validation error`, naming the field. Nothing is fetched and nothing is charged.
324
+
325
+ **A handle that does not resolve is a clean error, not empty data.** It returns `isError: true` with `HasData API error: 400 Bad Request` and `requestMetadata.status` set to `error`. This is the good case, because the failure is unambiguous. Test the flag rather than the array length.
326
+
327
+ **An account whose data is not public returns no post feed.** The tools cover public accounts, and there is nothing to read on one that is not. Treat a missing `latestPosts` as out of scope and not as an empty feed.
328
+
329
+ Results that carry data also carry a `requestMetadata.id` worth quoting in support, plus `html` and `json` links to the stored artifact of that exact call.
330
+
331
+ ## Pricing, free tier and limits
332
+
333
+ Every Instagram tool costs **10 credits per successful call**. Response size does not change the price. A profile with twelve posts attached costs the same as one with none.
334
+
335
+ The free trial is **1,000 credits over 30 days with no card**, or 100 Instagram calls. After that an active account keeps getting 100 credits topped up each day whenever its balance drops below 100, so a low-volume agent runs on the free tier indefinitely.
336
+
337
+ Paid plans start at **$49 a month** for 200,000 credits, or 20,000 calls. The unit price falls with volume, from **$2.45 per 1,000 calls** on the entry plan to **$0.99** on Business, **$0.83** on Growth and **$0.75** on the largest [high-volume plans](https://hasdata.com/prices?utm_source=github&utm_medium=syndication&utm_campaign=instagram-mcp).
338
+
339
+ Your plan also sets concurrency. The free trial allows 1 request at a time, Startup 15, Business 30, Growth 50, and the high-volume plans run from 200 to 1,500. Handle the overflow case defensively in anything unattended, because an agent that fans out across handles will reach the ceiling before you do.
340
+
341
+ Paging costs a call each time. A prompt that walks a hundred posts across two accounts is eighteen calls and 180 credits. The trial goes further on profile comparisons than on deep feed crawls.
342
+
343
+ ## Tool selection
344
+
345
+ `?apis=instagram` exposes exactly these two tools. The parameter takes a list, and `?apis=instagram,tiktok,youtube` gives your agent three social platforms at once. Drop the parameter and you get everything HasData exposes, which is currently 57 tools.
346
+
347
+ A narrow list is usually the better default. A model choosing between two tools picks correctly more often than one choosing between fifty-seven, and the tool descriptions themselves cost context on every turn.
348
+
349
+ Cross-platform comparison is the usual reason to widen the list. Ask the same question of an Instagram handle and a TikTok handle and it is one prompt once both are exposed.
350
+
351
+ ## How it compares
352
+
353
+ Almost every Instagram MCP server does something different from this one, and that makes the choice unusually clear.
354
+
355
+ The popular ones operate an account. Some wrap the Instagram Graph API to publish posts, read comments and manage the accounts you administer. Others handle direct messages. The engagement-analysis servers ask for `INSTAGRAM_USERNAME` and `INSTAGRAM_PASSWORD` in an env block, per their own setup instructions, because they sign in and browse as you. All of those are the right tool when the job is to run an account you control.
356
+
357
+ This server never signs in as anyone, which is a different job. Every question it answers is about a handle you do not own, and the call is identical whichever handle that is.
358
+
359
+ | | Account-operating server | This server |
360
+ | :--- | :--- | :--- |
361
+ | What it acts as | Your account, via a token or a session | Nothing, it reads public data |
362
+ | What you configure | Credentials or a Graph API app, per account | One API key, once |
363
+ | Which handles it covers | The accounts you administer | Any public handle |
364
+ | Publishing and messaging | Yes, that is the point | Not offered |
365
+ | Output | Scoped to the account you run | JSON for any public handle, hashtags and mentions parsed |
366
+ | What you run | A Python or Node process locally | A URL and a header |
367
+ | Cost | Free | 10 credits a call |
368
+
369
+ Two rows decide it. If you need to post, comment or reply, this server cannot help you at all. If you need the same fields across a hundred handles you have no relationship with, a server built around your own credentials cannot help you either.
370
+
371
+ The deciding axis is scope, not polish. A server built around your own login can only reach the accounts you administer, however good its output is. This one answers the same question for any public handle, and the fields come back as parsed arrays that cost nothing to aggregate.
372
+
373
+ **What this server does not do.** No comments, no stories, no reels beyond what the feed reports, no direct messages, no hashtag or location search, and nothing that writes. It reads two things well.
374
+
375
+ ## FAQ
376
+
377
+ ### What is an Instagram MCP server?
378
+
379
+ A server that exposes Instagram data as tools an AI client can call. The client sends a tool call over the Model Context Protocol, the server fetches the data and returns structured JSON, and the model works with the result and never sees a page of HTML. This one exposes two read-only tools and runs remotely. The client connects to a URL and starts no local process.
380
+
381
+ ### Is there an official Instagram MCP server?
382
+
383
+ Meta publishes no general-purpose one. There is an official MCP for Meta advertising, and it covers ad accounts and campaigns, not profile and post data. Everything else in this space is built by somebody else.
384
+
385
+ ### What data is in scope?
386
+
387
+ Public profile fields and the public post feed, for public accounts, by handle. A private account still returns its header, the follower and following counts and a `private: true` flag, but no biography and no posts, since there is no public feed to read. You are responsible for how you use the results, including compliance with Instagram's terms and with the law that applies to you.
388
+
389
+ ### Do I need to host or run anything?
390
+
391
+ No. This is a remote MCP server on streamable HTTP. Nothing to install, no Python environment, no process to restart.
392
+
393
+ ### Is the data live or cached?
394
+
395
+ Live. Each call fetches at request time and carries its own `requestMetadata.id`. Two identical calls are two separate fetches and not a replay of a stored copy. Counters like followers and likes track the account and move as it moves.
396
+
397
+ ### How many posts can I get?
398
+
399
+ Twelve per call, one Instagram page, and further pages come from `pagination.nextPageToken`. For a public account the profile lookup includes the same twelve at no extra cost, so short feed questions often need no posts call at all.
400
+
401
+ ### What happens when Instagram changes its markup?
402
+
403
+ Nothing on your side. We track the changes and keep the response schema stable, and field names and types stay put. A field with no value is absent from the item instead of present and null, and that is why optional fields should be read with a default.
404
+
405
+ ### Can I use one server for several platforms?
406
+
407
+ Yes. The `apis` parameter takes a list, and `?apis=instagram,tiktok,youtube` gives your agent three platforms at once.
408
+
409
+ ### Which clients work?
410
+
411
+ Any MCP client that supports streamable HTTP with custom headers. The configs above are tested. Clients with OAuth support can add the URL as a connector instead.
412
+
413
+ ## HasData links
414
+
415
+ | | |
416
+ | :--- | :--- |
417
+ | Product pages and request builder | [Instagram Profile API](https://hasdata.com/apis/instagram-profile-api?utm_source=github&utm_medium=syndication&utm_campaign=instagram-mcp) and [Instagram Posts API](https://hasdata.com/apis/instagram-posts-api?utm_source=github&utm_medium=syndication&utm_campaign=instagram-mcp) |
418
+ | Server documentation | [MCP server docs](https://docs.hasdata.com/mcp-server?utm_source=github&utm_medium=syndication&utm_campaign=instagram-mcp) |
419
+ | All 57 tools in one server | [HasData/hasdata-mcp](https://github.com/HasData/hasdata-mcp) |
420
+ | Client walkthroughs | [MCP clients and integrations](https://hasdata.com/integrations/mcp?utm_source=github&utm_medium=syndication&utm_campaign=instagram-mcp) |
421
+ | The other platforms we parse | [53 more scraper APIs](https://hasdata.com/apis/?utm_source=github&utm_medium=syndication&utm_campaign=instagram-mcp) |
422
+ | Plans and credit costs | [Plans and credit costs](https://hasdata.com/prices?utm_source=github&utm_medium=syndication&utm_campaign=instagram-mcp) |
423
+ | Keys and usage | [HasData dashboard](https://app.hasdata.com?utm_source=github&utm_medium=syndication&utm_campaign=instagram-mcp) |
424
+
425
+ ## Development
426
+
427
+ This repository is configuration and documentation for a remote server. There is no build step and nothing to containerize.
428
+
429
+ It does carry a contract test. The README promises two tools with specific parameters, and the upstream tool list can change without a commit here, and that would leave this file quietly lying to you. The test asserts the promise and runs weekly in CI as well as on every push.
430
+
431
+ ```bash
432
+ HASDATA_API_KEY=your_key_here npm test
433
+ ```
434
+
435
+ On PowerShell:
436
+
437
+ ```powershell
438
+ $env:HASDATA_API_KEY = "your_key_here"; npm test
439
+ ```
440
+
441
+ The last check makes a real call and costs 10 credits, which is the price of a canary that can fail for the right reason. Listing tools succeeds with any non-empty key, and a test that only lists tools stays green with a revoked one.
442
+
443
+ ## Contributing
444
+
445
+ Corrections to the tool tables and the response samples are the most useful contribution, because those are the parts that drift. Include the call you made and the response you got. Pull requests from forks run the suite without a key, and the live checks skip instead of going red.
446
+
447
+ ## License
448
+
449
+ MIT. See [LICENSE](LICENSE).