# Using an AI agent (/docs/ai-agents) Point Claude, ChatGPT, Cursor or any coding agent at the InsightSocial API with one paste-in prompt and the free endpoint catalogue, so it reads the real paths, parameters and prices instead of guessing. You do not have to read the rest of these docs before you start. Paste the prompt below into your agent and it will read the free endpoint catalogue, check your key and make the first calls for you. Everything happens over plain HTTPS: one base URL, one header, one JSON envelope, so there is nothing you have to install. If your agent supports MCP or Agent Skills, one command sets up both instead: `npx -y insightsocial init`. See [MCP server, CLI and agent skills](/docs/mcp). For workflows in n8n, see [n8n](/docs/n8n). ## Why the catalogue matters [#why-the-catalogue-matters] `GET https://api.insightsocial.app/v1/endpoints` is public and free, and it needs no key. It lists every endpoint with its description, every parameter (name, type, whether it is required, allowed values, an example) and its price in credits. An agent that reads it before planning a call never invents a path or a parameter, and it can tell you what a job will cost before it spends anything. ```bash title="cURL" # Every endpoint on one platform curl "https://api.insightsocial.app/v1/endpoints?platform=tiktok" ``` ```json title="Response (one entry, trimmed)" { "path": "/v1/tiktok/profile", "method": "GET", "platform": "tiktok", "description": "Returns a TikTok account's public profile: display name, bio, follower count ...", "credits": 20, "paginates": false, "cache_ttl_seconds": 86400, "params": [ { "name": "handle", "required": false, "in": "query", "type": "string", "example": "charlidamelio", "one_of_group": "handle|user_id" }, { "name": "user_id", "required": false, "in": "query", "type": "string", "one_of_group": "handle|user_id" } ], "available": true } ``` A few fields worth knowing: | Field | Meaning | | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | `credits` | A number for a fixed price, or `{ "min", "max" }` for a metered endpoint. A metered call holds `max` when it starts and is charged what it actually used. | | `params[].one_of_group` | Parameters in the same group are alternatives: send at least one of them. | | `paginates` | `true` when the endpoint returns a cursor for the next page. | | `cache_ttl_seconds` | Kept for compatibility. It does not change the price: every repeat is charged. | | `available` | Only call endpoints where this is `true`. | `?platform=` takes one platform id, such as `instagram`, `tiktok`, `bluesky` or `weibo` (the full list is under [UNKNOWN\_PLATFORM](/docs/errors#unknown_platform)). Leave it off to get all 25 platforms at once. If your tooling generates clients or agent tools from OpenAPI, the same surface is published as an OpenAPI 3.1 spec at `GET https://api.insightsocial.app/v1/openapi.json`, also free and keyless. ## Give it the docs as Markdown [#give-it-the-docs-as-markdown] The quickest start is one link: `https://www.insightsocial.app/docs/api-reference.md` lists every endpoint with its path, a one-line description and its required parameters, and links to each endpoint's full page. Give it to your assistant together with your key and say what data you want. Every page of these docs is also plain Markdown: add `.md` to its URL, for example `https://www.insightsocial.app/docs/pagination.md`. Two more files cover the whole site: * `https://www.insightsocial.app/docs/llms.txt` lists every page, one line each. * `https://www.insightsocial.app/docs/llms-full.txt` is every page in one file. Paste either into a chat, or let an agent that can fetch URLs read them itself. ## Copy the prompt [#copy-the-prompt] Put your key in the environment first, so it never appears in the chat: ```bash title="Terminal" export INSIGHTSOCIAL_API_KEY=isk_live_... ``` Then paste this into your agent: ```text title="Prompt" Set up the InsightSocial API in this project and make a first successful call. Facts you can rely on: - Base URL: https://api.insightsocial.app/v1 - Auth: send the header `x-api-key: $INSIGHTSOCIAL_API_KEY` on every request. Read the key from the environment. Never print it, put it in a URL, or commit it. Authorization: Bearer is refused. - All data endpoints are GET with query parameters. - Success responses look like: { success: true, platform, endpoint, schema_version: "2", data, pagination?, unavailable, credits_used, credits_remaining, request_id, cached, idempotent_replay, charge_reason, free_call }. List responses put rows in data.items (each { post }, { author } or { comment }) and add pagination { next_cursor, has_more }. unavailable lists fields (paths into data) this response could not fill. - Ids are strings, times are ISO 8601 UTC. null means unknown, never zero. - Responses carry data only: no engagement rate, labels or relevance scores. Compute what you need from the raw counts. Parameters that ask for analysis (label, relevance, relevant_to, judgments, fit, brand, ...) are refused with 400 UNSUPPORTED_PARAMETER and error.param. - Error responses look like: { success: false, error: { type, message, param? }, request_id, credits_used, credits_remaining }. - Prices are per endpoint, in credits. Failed calls, empty results and dry_run=1 calls are never charged. Repeating a call is charged again, so store responses you need twice. Before writing any call, read the reference instead of guessing paths or parameters: https://www.insightsocial.app/docs/api-reference.md For prices and allowed values, read the catalogue. It is free and needs no key: GET https://api.insightsocial.app/v1/endpoints GET https://api.insightsocial.app/v1/endpoints?platform= Use only endpoints where available is true, only parameters listed for that endpoint, and tell me the price (credits, or min-max for metered) before any call over 50 credits. Then, in order: 1. Verify the key with GET https://api.insightsocial.app/v1/credits (free) and print credits_remaining. 2. Call GET https://api.insightsocial.app/v1/tiktok/profile?handle=khaby.lame and print data.author.followers and credits_used. 3. Read the same kind of account on instagram/profile (handle=natgeo), youtube/channel (handle=mkbhd) and twitter/profile (handle=nasa). Print one line per platform. 4. Pick one list endpoint from the catalogue whose paginates is true, and read two pages by sending pagination.next_cursor back as ?cursor= while has_more is true. Send cursor values back unchanged, with the same other parameters as page 1. Rules: - Check `success` before using `data`. Branch on error.type, never on the message text. - Retry only on HTTP 429, HTTP 503, IDEMPOTENCY_IN_PROGRESS and INTERNAL_ERROR. On a 400, read error.param and fix or remove that parameter before trying again. Wait for the Retry-After header when present, then back off with jitter. Never retry other 4xx errors. - On INSUFFICIENT_CREDITS, stop and tell me. Do not retry. - Stay under 60 requests per minute and 10 requests in flight. - Stop and ask me before spending more than 500 credits in total. ``` The prompt pins down the three things an agent must not guess (the base URL, the header and the response shape), sends it to the catalogue before it writes a call, and gives it four concrete tasks that end with accounts read on four platforms. The whole exercise costs roughly 60 to 100 credits, and on a new account the 10 free calls cover most of it. ## What the agent will do first [#what-the-agent-will-do-first] A good agent run starts the same way every time, and you can ask for these steps by name: **Read the catalogue.** `GET /v1/endpoints?platform=` to find the endpoint and its parameters. Free, no key. **Check the key.** `GET /v1/credits` confirms the key works and returns your balance, the monthly allowance, pack credits and your remaining free calls. Free. **Run a single paid request,** then swap the platform segment of the path and run it again to see how another network answers. ```bash title="cURL" curl "https://api.insightsocial.app/v1/credits" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Wiring the API into your own agent [#wiring-the-api-into-your-own-agent] When you build the agent yourself, one generic HTTP tool is enough: the model picks the path and parameters from the catalogue, and your tool makes the call. Keep the key in your code, not in the model's context. ```python title="Python" import os import requests KEY = os.environ["INSIGHTSOCIAL_API_KEY"] BASE = "https://api.insightsocial.app/v1" # Give the model this catalogue once, as reference text or a second tool. CATALOGUE = requests.get(f"{BASE}/endpoints", timeout=30).json() def insightsocial_get(path: str, params: dict) -> dict: """Tool: call one InsightSocial endpoint, e.g. path='tiktok/profile'.""" res = requests.get(f"{BASE}/{path.removeprefix('/v1/').lstrip('/')}", params=params, headers={"x-api-key": KEY}, timeout=120) body = res.json() if not body["success"]: # Hand the model a short instruction, not a raw error envelope. retry = res.headers.get("Retry-After") return {"error": body["error"]["type"], "message": body["error"]["message"], "retry_after_seconds": retry} return {"data": body["data"], "pagination": body.get("pagination"), "credits_used": body["credits_used"], "credits_remaining": body["credits_remaining"]} ``` Describe the tool to the model with two parameters, `path` (string, for example `tiktok/profile`) and `params` (object of query parameters). This works with Claude tool use, OpenAI function calling or any agent framework, because it is only an HTTP request. Three habits keep an agent loop cheap and correct: * **Return a short error, not the envelope.** A model given a raw `success: false` body tends to reason around it. Hand it the `error.type` and let your code decide whether to retry. * **Watch the budget.** Every response carries `credits_used` and `credits_remaining`. Stop the loop on a budget you set, and cap the number of tool steps. * **Price before you call.** For metered endpoints the catalogue's `max` is what the call holds up front. `dry_run=1` returns our quote in `data.dry_run` (`credits_min`, `credits_max`) with `charge_reason: "dry_run"`, and is never charged. ## Troubleshooting [#troubleshooting] **The agent gets `MISSING_API_KEY`.** It is almost certainly putting the key in `Authorization: Bearer`. That header is ignored: the API looks for the key in `x-api-key` and nowhere else, so a Bearer-only request arrives with no key at all. `INVALID_API_KEY` is different: the `x-api-key` header is present but its value is wrong, such as a token that does not start with `isk_` or a key that does not exist. **It invents parameters.** Unknown parameters are passed along and may be rejected with a `400`. Errors are never charged, but they waste a round trip. Tell the agent to read `GET /v1/endpoints?platform=` first and use only the listed `params`. **It gets `UNSUPPORTED_PARAMETER`.** The agent sent a parameter that asks for analysis, such as `relevance`, `relevant_to` or `label`, often copied from older examples. `error.param` names it. Have the agent drop it and filter or score the rows itself; see [Schema 2](/docs/schema-2). **It gets `CURSOR_INVALID` on page 2.** It changed a parameter between pages, sent the cursor under another name, or reused a cursor from the previous schema. A cursor works only as `cursor`, with the first page's parameters, for 24 hours. See [Pagination](/docs/pagination#bad-or-stale-cursors). **It hits `RATE_LIMITED` or `CONCURRENCY_LIMIT`.** Each key allows 60 requests per minute and 10 in flight. Have the agent honour `Retry-After` and run fewer calls in parallel. See [Rate limits](/docs/rate-limits). **It gets `INSUFFICIENT_CREDITS` on a call that should be cheap.** A metered call holds its ceiling before it runs, so the balance has to cover `max`, not the final charge. Top up at [insightsocial.app/pricing](https://www.insightsocial.app/pricing). API credits share one balance with your InsightSocial export credits, and they are non-refundable. Calls that fail or return nothing are simply never charged. **It keeps paying for the same data.** Every repeat of a call is charged. Have the agent keep the responses it already has instead of calling again, and make sure it is not adding `fresh=1` or `Cache-Control: no-cache`, which skip the 2-credit shared cache and force a full-price fetch. ## Next steps [#next-steps] One command to connect Claude Code, Cursor, Codex or Gemini CLI. The same first calls, step by step, by hand. Creating, naming and revoking keys. Every error type and whether to retry. End-to-end builds an agent can adapt. --- # Authentication (/docs/authentication) Create, send, rotate and revoke InsightSocial API keys with the x-api-key header. Every request carries your key in the `x-api-key` header. The key is all you need to authenticate. You do not sign in, trade it for a token or keep a session alive. ```bash title="cURL" curl "https://api.insightsocial.app/v1/instagram/profile?handle=natgeo" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## What a key looks like [#what-a-key-looks-like] A key is `isk_live_` followed by 43 URL-safe characters (32 random bytes). You may also come across keys starting `isk_test_`. They are accepted the same way and draw on the same credits. The full key is shown in the dialog that opens right after you create it. It is stored encrypted, so you can show or copy it again later from the dashboard. Keep it in your secret store, not in source code. ## Create a key [#create-a-key] 1. Sign in at [insightsocial.app](https://www.insightsocial.app). 2. Open [API keys](https://www.insightsocial.app/portal/api/keys) in the dashboard. The first time you open the API section, a first key is created for you. 3. To add another, click create and give it a name that says where it runs, such as `production` or `local-dev`. A name is required and can be up to 64 characters. 4. Copy the key from the dialog. ## Send the key [#send-the-key] ```bash title="cURL" curl "https://api.insightsocial.app/v1/instagram/profile?handle=natgeo" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os import requests res = requests.get( "https://api.insightsocial.app/v1/instagram/profile", params={"handle": "natgeo"}, headers={"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}, ) ``` ```ts title="Node.js" const res = await fetch( "https://api.insightsocial.app/v1/instagram/profile?handle=natgeo", { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! } }, ); ``` The API does not read the `Authorization` header. A key sent as `Authorization: Bearer ...` is treated as no key at all and returns `MISSING_API_KEY`. Never put the key in a query string. Anything in a URL can be recorded by browsers, proxies and the sites you link to. ## Authentication errors [#authentication-errors] Authentication runs before anything else, so these failures never cost credits and do not count against your rate limit. `credits_remaining` is `null` on all of them. | `error.type` | Status | What happened | Fix | | --------------------- | ------ | ----------------------------------------------------------------------------------------- | ------------------------------------------------- | | `MISSING_API_KEY` | 401 | No `x-api-key` header reached the API | Add the header, and check that no proxy strips it | | `INVALID_API_KEY` | 401 | The value is not an InsightSocial key (for example a session token), or no key matches it | Copy the key again from the dashboard | | `API_KEY_REVOKED` | 401 | The key was revoked | Create a new key | | `SERVICE_UNAVAILABLE` | 503 | We could not verify the key right now | Retry shortly | ```json title="Response" { "success": false, "error": { "type": "API_KEY_REVOKED", "message": "This API key has been revoked. Create a new one at https://www.insightsocial.app/portal/api/keys" }, "request_id": "req_1a2b3c4d5e6f", "credits_used": 0, "credits_remaining": null } ``` The 401s are not worth retrying: the same key fails the same way until you change it. Running out of credits is a different problem and returns `402 INSUFFICIENT_CREDITS`; see [Errors](/docs/errors). ## Key management [#key-management] | Rule | Value | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Keys per account | Up to 25 | | Name | Required, up to 64 characters | | Scope | Every key can call every `/v1` endpoint | | Balance | All keys draw on your one account balance | | Storage | Verified against a hash; the key itself is kept encrypted so you can show or copy it again from the dashboard. Keys created before 2026-09-23 cannot be shown again | | Revocation | Takes effect on the next request, which returns `401 API_KEY_REVOKED` | | Rate limits | Counted per key. See [Rate limits](/docs/rate-limits) | ### Rotate a key [#rotate-a-key] Issue the new key, roll it out, check that requests now use it, and only then revoke the old one from the dashboard. Revoking first causes downtime, because the old key stops working on its very next request. If a key has leaked, revoke it at once and accept the downtime. Anyone holding a live key can spend your credits. ## Security guidance [#security-guidance] * **Never ship a key in client-side code.** Anyone can unpack a web or mobile app and pull the key out, and you pay for every call made with it. Put a server route in front of the API. * **Use one key per environment.** That way you can revoke a test key without breaking live traffic. * **Store keys in a secret manager** or encrypted environment variables, never in git. * **Revoke keys you no longer use**, including ones issued to contractors or short-lived jobs. ## Next steps [#next-steps] Your first authenticated call in about a minute. Every error type and whether to retry it. What calls cost and how the shared balance works. Per-key request and concurrency limits. --- # Caching (/docs/caching) How cached answers are flagged and priced, and how to force a fresh fetch. One thing can make a call cheaper than its listed price: a recent answer to the same request may already sit in our shared cache, which costs 2 credits instead of the full price. It needs no setup and is reported on every response. Repeating a call is charged like the first time. A repeat can return newer data, so it is a new call. ## Reading the signals [#reading-the-signals] | Field | Meaning | | --------------- | ---------------------------------------------------------------------------------------------- | | `cached` | `true` when the answer came from our shared cache instead of a new fetch | | `charge_reason` | Why you were charged what you were: `miss`, `shared_cache`, `replay`, `no_result` or `dry_run` | | `credits_used` | What this call cost. Also in the `X-Credits-Used` header | Rely on the body fields. There is no `X-Cache` header. ## What each case costs [#what-each-case-costs] | Case | `cached` | `charge_reason` | Credits | | ---------------------------- | -------- | --------------- | -------------------------------------------- | | New fetch | `false` | `miss` | The endpoint's price (metered: what it used) | | Served from the shared cache | `true` | `shared_cache` | 2 | | Nothing found | `false` | `no_result` | 0 | The shared cache is not tied to your account. Public data is the same for everyone, so a request someone else made moments ago can answer yours. That is why a shared-cache answer is cheap but not free. If you need the same data more than once, store the first response on your side instead of calling again. ## Force a fresh fetch [#force-a-fresh-fetch] When you need data as it is right now, send `Cache-Control: no-cache`, or add `fresh=1` to the query string. Either skips the shared cache, so the call is always charged at the endpoint's normal price. ```bash title="cURL" curl "https://api.insightsocial.app/v1/tiktok/profile?handle=khaby.lame" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" \ -H "Cache-Control: no-cache" ``` ```python title="Python" import os import requests res = requests.get( "https://api.insightsocial.app/v1/tiktok/profile", params={"handle": "khaby.lame"}, headers={ "x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"], "Cache-Control": "no-cache", }, ) body = res.json() print(body["cached"], body["charge_reason"], body["credits_used"]) ``` ```ts title="Node.js" const res = await fetch( "https://api.insightsocial.app/v1/tiktok/profile?handle=khaby.lame", { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY!, "Cache-Control": "no-cache", }, }, ); const body = await res.json(); console.log(body.cached, body.charge_reason, body.credits_used); ``` The same request with the query-string form: ```bash title="cURL" curl "https://api.insightsocial.app/v1/tiktok/profile?handle=khaby.lame&fresh=1" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` `fresh=1` (or `fresh=true`) is read by us and not passed on as an endpoint parameter. Either form affects only the request it is on. ## When should I force a fresh fetch? [#when-should-i-force-a-fresh-fetch] Leave it off for most work. A shared-cache answer is far cheaper than a new fetch. Force it only when stale data would be wrong, for example: * a reconciliation or audit that must reflect the platform at the moment of the call; * reading a post or profile straight after it changed; * comparing a cached answer against a live one while debugging. ## Next steps [#next-steps] Every charge reason, free calls, and where your balance comes from. Prices for every endpoint. Retries, idempotency and logging before launch. --- # Changelog (/docs/changelog) Every change to the InsightSocial API that affects what you send, what comes back, or what a call costs. Newest first. Changes that affect your integration are listed here with the date they took effect. Anything that changes what a call costs is marked **Billing**. ## October 14, 2026 [#october-14-2026] **Date filters on 112 more endpoints.** Lists of posts and comments across the platforms now take `until` (on or before) and, where they did not have it already, `since` (on or after), as `YYYY-MM-DD` or an ISO 8601 timestamp. On lists that are not in date order they filter each page and the walk continues, and a page is charged even when every row on it was filtered out. See [Pagination](/docs/pagination#date-filters-on-other-lists). **`GET /v1/instagram/post/likers` sets `data.truncated` to `true`** when fewer likers come back than the post has likes. It was `null`. **`GET /v1/linkedin/search/hashtag` is switched off.** Its data source has returned no posts for any hashtag since at least October 3, so every call came back as a free, empty result that read as "nobody posts under this tag". Calls answer `503 ENDPOINT_UNAVAILABLE` with `Retry-After: 3600` and are never charged, and the endpoint is out of the catalogue until it works again. `GET /v1/linkedin/search/posts` with `query=#hiring` returns recent posts that carry the tag. **`GET /v1/instagram/post/likers` says how many likers it returns.** Its description said about the first 100. In our tests a post with a few hundred likes came back whole, and bigger posts returned about 1,000 to 2,000 likers in one call however many likes they had; an older post returned fewer. The call itself did not change. See [Get the accounts that liked an Instagram post](/docs/how-to/instagram-post-likers). ## October 5, 2026 [#october-5-2026] **Billing: every endpoint costs half what it did.** A standard call is 10 credits (was 20), and a 100-credit call is now 50. Metered endpoints halve at both ends of their range, and so does what a metered call holds. An answer served from the shared cache costs 2 credits (was 5). The free plan's 500 credits a month now cover 50 standard calls instead of 25. The 10 free calls a new account gets now cover calls priced at up to 100 credits, which is the same set of endpoints as before. Failed calls, empty results, `dry_run=1` and `Idempotency-Key` replays are still free. Credits you already hold are worth twice as many calls. Every current price is in [Endpoint pricing](/docs/endpoint-pricing). Changelog entries below this one quote the prices of their own date. ## October 4, 2026 [#october-4-2026] **New: a hosted MCP server at `https://api.insightsocial.app/mcp`.** Claude (desktop, claude.ai, Cowork), ChatGPT and any MCP client that supports sign-in connect by URL: add a connector, sign in to InsightSocial and click Allow, with no key to paste. Each app gets its own key, listed under Connected apps on [API keys](https://www.insightsocial.app/portal/api/keys), where you can disconnect it. Clients that cannot sign in send an API key as a Bearer token. The hosted server has the same tools as the local one plus `quote_endpoint` and `send_feedback`, and `call_endpoint` is marked as spending credits so clients ask first. See [MCP](/docs/mcp). **The CLI signs in through your browser.** `npx -y insightsocial init` (or `insightsocial login`) opens a sign-in page and prints a short code: type it on the page and click Allow, and the CLI prints which account it signed into. `login --api-key` still takes a key you already have. **`GET /v1/instagram/music/trending` is switched off.** Its data source failed most calls, often after a minute's wait. Calls answer `503 ENDPOINT_UNAVAILABLE` with `Retry-After: 3600` and are never charged, and the endpoint is out of the catalogue until it works again. `GET /v1/instagram/search/music` still finds tracks by name. **Breaking: a LinkedIn person has one id everywhere, the member URN.** `author.id` on a LinkedIn person is now the URN (`ACoAA…`) on every endpoint that has one: profiles, search, similar, reactors, and the authors of posts. It used to be the vanity slug on profiles and a numeric member id on posts and reactors, so the same person had different ids. The slug is in `username` (it was sometimes the URN there), the numeric id in `ext.member_id` on profiles and `ext.author_member_id` on posts and comments. Comment authors have no URN at the source, so their `author.id` is `null` and listed in `unavailable`; join on `username`. On `/v1/linkedin/search/people/by-url`, where the source gives no URN, `id` stays the slug. Companies keep their numeric id. **Breaking: `GET /v1/tiktok/profile/region` returns a record, not a profile.** The source sent the handle as the profile's `id`. `data` is now `{ region, username, display_name, followers, … , url }` with no `id` and no `author` wrapper, and the endpoint is marked beta. **LinkedIn companies have one id everywhere.** `/v1/linkedin/search/companies` and `/v1/linkedin/company/also-viewed` now return the numeric company id in `author.id` (for example `"1035"`), the same id `/v1/linkedin/company` returns and `/v1/linkedin/company/posts` takes as `company_id`. The slug (`"microsoft"`) stays in `author.username`. **X replies to a retweet no longer include the original tweet as a reply.** A row whose `id` equals its `post_id` was the post itself; it is dropped, so the page holds replies only. **`GET /v1/instagram/user/embed` is switched off.** Its data source answers not-found for every profile. Calls answer `503 ENDPOINT_UNAVAILABLE` with `Retry-After: 3600` and are never charged, and the endpoint is out of the catalogue until it works again. **`Retry-After` on `UPSTREAM_ERROR` and `UPSTREAM_INVALID` is now at least 30 seconds** (was 5), longer when the platform asks for more. By the time you see one we have already retried, so 5 seconds met the same outage. **Reddit comment trees: `data.truncated` is `null` instead of `false`** when we cannot tell whether Reddit left branches out, and `true` when the tree reaches Reddit's depth limit. A thread was served 329 of its 602 comments while saying `false`. **Billing: a metered call holds what it can cost, not the top of the range.** The hold before a call is now worked out from the parameters you send. `GET /v1/threads/user/posts` without `limit` holds 20 credits (was 3,300); `/v1/instagram/search/reels` without `include` holds 20 (was 1,300). What a call is charged did not change. `dry_run=1` quotes the same hold. **Billing: `GET /v1/facebook/adlibrary/company/ads` is metered, 100–700 credits.** It takes a new `include=audience`, which adds EU reach and age, gender and location targeting to each ad that ran in the EU, for up to 20 credits per ad. Without `include` a page costs 100 credits, as before. **The previous response shape is no longer served.** `InsightSocial-Version: legacy` is ignored and `/v1/openapi-legacy.json` is gone. No key was still using it. **Breaking: every key now gets schema 2.** One response shape per entity on every platform; analysis fields (`computed`, `labels`, `relevance`) are gone, cursors are `v2c.…`, and analysis parameters answer `400 UNSUPPORTED_PARAMETER`. Everything that changed is on [Schema 2](/docs/schema-2). **Billing: lower ceilings under schema 2.** A metered call no longer holds credits for labels, which schema 2 does not produce, so the ceiling held at the start of a call dropped on 24 endpoints. What a call is charged did not change: it was already what the call used. * Now one price, 20 credits a call: `GET /v1/tiktok/profile/videos`, `/v1/tiktok/video/comment/replies`, `/v1/tiktok/search/hashtag`, `/v1/instagram/post/comment/replies`, `/v1/youtube/video/comments`, `/v1/youtube/video/comment/replies`, `/v1/twitter/user/tweets`, `/v1/twitter/tweet/replies`, `/v1/facebook/post/comments`, `/v1/facebook/post/comment/replies` and `/v1/reddit/subreddit`. * Now one price, 100 credits a call: `GET /v1/reddit/post/comments`. * Still metered, with a lower top: for example `GET /v1/instagram/search/reels` is 20–1,300 (was 20–1,380), `/v1/tiktok/post/comments` 20–60 and `/v1/twitter/search/tweets` 20–100. `GET /v1/tiktok/search/users` goes the other way, to 20–2,620, because its old ceiling was below what a call can use. Every current price is in [Endpoint pricing](/docs/endpoint-pricing). ## October 1, 2026 [#october-1-2026] **Billing: two price ranges changed.** | Endpoint | Before | Now | | -------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------ | | `GET /v1/instagram/search/reels` | 20–180 | 20–1,380. `include=creator` adds 40 credits per creator looked up, held up front and refunded for creators not looked up | | `GET /v1/instagram/similar` | 100 | 100–4,000. A call with no `limit`, or `limit` of 50 or less, still costs 100 | Both are metered: the top of the range is held when the call starts and you are charged what it used. See [Endpoint pricing](/docs/endpoint-pricing). Both ranges have changed since. `GET /v1/instagram/search/reels` is 20–1,300 under schema 2 (see October 4). `GET /v1/instagram/similar` is 100–1,700: `include=profile` fills the top 20 accounts unless you pass `limit` (1 to 80). **New endpoint.** `GET /v1/tiktok/similar` returns accounts similar to a TikTok creator, 100 credits a call. ## September 26, 2026 [#september-26-2026] **Billing: repeat calls are now charged.** Repeating a call you had already paid for used to be free for a while afterwards. A repeat is now charged like the first call, because it can return newer data. Store responses you need again on your side. Retries with the same `Idempotency-Key` still cost 0 and come back with `charge_reason: "replay"`. `owned` is no longer a `charge_reason`; you will still see it on Activity Log rows from before this date. See [Credits](/docs/credits#repeating-a-call). **Required inputs on three Instagram endpoints.** The reference listed no required parameters for these, but they cannot answer without one: | Endpoint | Required | | ------------------------------------ | ---------------------------------- | | `GET /v1/instagram/basic-profile` | `userId` | | `GET /v1/instagram/highlight/detail` | `id` | | `GET /v1/instagram/comment` | one of `comment_url` or `post_url` | **The whole reference on one page.** [`/docs/api-reference.md`](https://www.insightsocial.app/docs/api-reference.md) lists every endpoint with its path, a one-line description and its required parameters. Give it to an AI assistant along with your key. See [AI agents](/docs/ai-agents). ## September 24, 2026 [#september-24-2026] **Not-found errors no longer read like outages.** A 404 sometimes ended with an extra sentence saying the endpoint was temporarily unavailable. It now says only that nothing exists at that identifier, and that you were not charged. ## September 23, 2026 [#september-23-2026] **The API is live** at `https://api.insightsocial.app/v1`. Public data from Instagram, TikTok, Facebook, LinkedIn, Twitter/X, Threads, YouTube, Reddit and Pinterest, authenticated with one `x-api-key` header and paid from the same credit balance as InsightSocial exports. Every account gets 10 free calls. Start with the [Quickstart](/docs/quickstart), or run a call without code in the [Explorer](https://www.insightsocial.app/portal/api/explorer). **Pagination on every list endpoint.** List responses return a `pagination` block (`next_cursor`, `has_more`, `page_size`) next to `data`. See [Pagination](/docs/pagination). --- # Which endpoint should I use? (/docs/choosing-endpoints) Side-by-side decision tables for the endpoints that look alike, with the credit price of each so you pick the one that returns what you need for the least. Several endpoints overlap. Two of them may return the same account, one cheaply and one with extras, or the same comments as one page or as a whole thread. Picking the wrong one either costs more than it should or leaves you making a second call. Each section below takes one of those "these look the same" questions and puts the options next to each other with their prices. Every price here comes from the live catalogue at `GET https://api.insightsocial.app/v1/endpoints` (free, no key needed). A few pricing rules to keep in mind while you read: * **Fixed endpoints** show one number, and that is the charge for a successful call. * **Metered endpoints** show a range. The top of the range is held when the call starts, and when it finishes you are charged what the call actually used, never more than the ceiling. Plan your balance around the top of the range. * **Nothing found costs nothing.** Failed calls, empty results and `dry_run=1` calls are never charged. API credits are non-refundable, so this is how "you only pay for data" works: an unanswered call is simply never billed. * **Repeats are charged.** Calling the same endpoint again with the same parameters is a new call, because the data may have changed. A response served from the shared cache costs 2 credits. Store what you need to reuse. See [Credits](/docs/credits) for the full rules and [Endpoint pricing](/docs/endpoint-pricing) for every price in one table. ## Start here [#start-here] | What you are trying to do | Section | | --------------------------------------------------------- | -------------------------------------------------------------------------------- | | Account numbers alone, or together with its latest posts | [Profile, or profile plus posts?](#profile-or-profile-plus-posts) | | Pull Instagram data at the right price | [Instagram: 10 credits or 50?](#instagram-10-credits-or-50) | | Page through posts or reels, with or without share counts | [A plain list, or the `/full` list?](#a-plain-list-or-the-full-list) | | Fill a Facebook list's missing fields in the same call | [Facebook: fill the rows, or fetch them?](#facebook-fill-the-rows-or-fetch-them) | | Fetch only what is new since your last run | [Only what is new](#only-what-is-new) | | Find posts or accounts by keyword | [Which search?](#which-search) | | Get what was said in a video | [Getting a transcript](#getting-a-transcript) | | Read the comments on a post | [Which comment endpoint?](#which-comment-endpoint) | | Track hiring and job postings | [Hiring and job postings](#hiring-and-job-postings) | | See what a brand runs as paid advertising | [Ad libraries](#ad-libraries) | ## Profile, or profile plus posts? [#profile-or-profile-plus-posts] `profile` returns the account and nothing else. `profile/full` returns the same account and its latest page of posts in one call, so you skip the follow-up list request. `profile/full` is a **beta** endpoint, but its two main parts are typed: `data.profile` is a [profile](/docs/schema/author), and each `data.posts[].post` is a [post](/docs/schema/post), the same shapes `profile` and the posts list return. Anything else under `data` keeps the source's own structure under our naming rules (snake\_case keys, string ids, ISO times where the source gives a timestamp) and may change. Under [schema 2](/docs/schema-2) it no longer carries computed metrics such as engagement rate or posting cadence; compute those from the posts it returns. | Your goal | Endpoint | Credits | | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | Just the account | `/v1/tiktok/profile`, `/v1/instagram/profile`, `/v1/twitter/profile`, `/v1/facebook/profile`, `/v1/youtube/channel` | 10 | | Just the account, LinkedIn member | `/v1/linkedin/profile` | 50 | | Account + recent posts (beta) | `/v1/tiktok/profile/full`, `/v1/instagram/profile/full`, `/v1/twitter/profile/full`, `/v1/facebook/profile/full`, `/v1/youtube/profile/full` | 50 | | LinkedIn company + latest 10 posts (beta) | `/v1/linkedin/profile/full` | 50 | If you only need follower counts, use plain `profile`. When you fetch a profile and then always ask for its latest posts, `profile/full` replaces those two calls with one. On TikTok, for example, `profile` plus one page of `profile/videos` costs 20 credits, while `profile/full` is a flat 50 for the same profile and posts. Prefer the pair when cost matters; prefer `profile/full` when one round trip matters more. How many posts one `profile/full` call reads depends on the platform's page size: 10 on TikTok, 12 on Instagram, up to 30 on YouTube, about 10 to 20 on X, 3 on Facebook and 10 on a LinkedIn company. To read further back, send `pagination.next_cursor` back to `profile/full` as `cursor`: each further page is another 50-credit call. Paging the platform's posts list (for example `/v1/tiktok/profile/videos`) is usually cheaper. `/v1/linkedin/profile/full` takes a company page URL and replaces a `/v1/linkedin/company` plus `/v1/linkedin/company/posts` pair (100 credits) with one 50-credit call. For a person, use `/v1/linkedin/profile`. The fields that get filled vary by platform. Check the platform's page under [Platforms](/docs/platforms/tiktok) before you rely on one. ## Instagram: 10 credits or 50? [#instagram-10-credits-or-50] Instagram endpoints come in two price bands. The 10-credit endpoints cover what a public profile page shows. The 50-credit (advanced) endpoints return what the public page does not: share counts, follower and following lists, stories, tagged posts, locations and comment lists. | Your goal | Endpoint | Credits | | -------------------------------------- | ------------------------------------------------------------ | -------------- | | Profile, one post | `/v1/instagram/profile`, `/v1/instagram/post` | 10 | | A page of posts or reels | `/v1/instagram/profile/posts`, `/v1/instagram/profile/reels` | 10–130, 10–140 | | Name and avatar from a numeric user id | `/v1/instagram/basic-profile` | 10 | | A post's share count | `/v1/instagram/post/stats` | 50–90 | | Followers or following | `/v1/instagram/followers`, `/v1/instagram/following` | 50–100 | | Accounts similar to one you know | `/v1/instagram/similar` | 50–850 | | Stories live right now | `/v1/instagram/stories` | 50 | | Comments on a post | `/v1/instagram/post/comments` | 50–150 | | Posts under a hashtag | `/v1/instagram/search/hashtag` | 50 | `/v1/instagram/post` (10 credits) returns everything about a post, including views on video, except the share count. Only reach for `/v1/instagram/post/stats` (50–90) when you specifically need shares. ## A plain list, or the `/full` list? [#a-plain-list-or-the-full-list] For Instagram posts and reels, and for Facebook reels, there is a plain list and an enriched `/full` list. The plain list is one fast page. The `/full` list attaches views, likes, comments and a per-item share count where one can be found. The `/full` lists are **beta**: they keep the source's own structure rather than the typed post shape. | Your goal | Endpoint | Credits | | ----------------------------------------------------------- | ---------------------------------- | ------- | | One page of a user's reels | `/v1/instagram/profile/reels` | 10–140 | | Reels with per-reel share counts | `/v1/instagram/profile/reels/full` | 50–250 | | One page of a user's posts | `/v1/instagram/profile/posts` | 10–130 | | Posts with per-post share counts | `/v1/instagram/profile/posts/full` | 50–250 | | Facebook reels with exact views, likes, comments and shares | `/v1/facebook/profile/reels/full` | 50–250 | Paging a long back catalogue when you do not need share counts is far cheaper on the plain list. Use `/full` when every item needs its engagement and you want it in one call. ### What `limit` means depends on the endpoint [#what-limit-means-depends-on-the-endpoint] `limit` does three different jobs on this API. Read the endpoint's parameter description before you rely on one. * **Collect until N.** On `/v1/instagram/profile/reels/full`, `/v1/instagram/profile/posts/full` and `/v1/facebook/profile/reels/full` (1 to 50), and `/v1/threads/search` (1 to 100), `limit` tells the endpoint to keep walking pages on its own until it has that many items. Each page walked is billed, which is what moves these calls up their range. Leave `limit` off and you get one page. * **Bigger single page.** On `/v1/tiktok/search`, `limit` returns one bulk page of up to 120 videos (rounded up to a multiple of 30). On `/v1/linkedin/search/posts` it returns up to 200 posts in one call. * **Keep the top N.** On `/v1/tiktok/search/users`, `/v1/pinterest/search`, `/v1/reddit/subreddits/search` and `/v1/instagram/similar`, `limit` trims the page after the search has run. It is not a page size, and the next cursor still moves past the whole page. Separately, twelve list endpoints take `max_pages` (1 to 5), which walks that many pages in one call. Each page is billed as one call, and the hold is multiplied by the page count before the call starts. ## Facebook: fill the rows, or fetch them? [#facebook-fill-the-rows-or-fetch-them] Four Facebook list endpoints take an optional `include` parameter that fills in every row on the page from that row's own detail record, in the same call. Without it, the call costs the bottom of its range. With it, you pay for the rows that were actually filled, which is why the price is a range. | Your goal | Call | Credits | | --------------------------------------------------------------------------- | ----------------------------------------------- | -------- | | A page's recent posts: text, reactions, comments, publish time | `/v1/facebook/profile/posts` | 10 plain | | The same posts with share counts, plus exact reel views and duration | `/v1/facebook/profile/posts?include=engagement` | up to 40 | | A page's photos with author, likes, comments, shares and publish date | `/v1/facebook/profile/photos?include=details` | 10–90 | | A page's own events with description, address, hosts, cover and RSVP counts | `/v1/facebook/profile/events?include=details` | 10–90 | | What is on in a city, with description, address and hosts | `/v1/facebook/events?include=details` | 10–130 | `include=engagement` is the right move when you are pulling the feed anyway. The alternative, one `/v1/facebook/post` call (10 credits) per permalink, costs about the same and takes a round trip per post. For reels specifically, `/v1/facebook/profile/reels/full` returns a page of reels with exact engagement on every one. ## Only what is new [#only-what-is-new] When you poll the same account on a schedule, most list endpoints can stop at what you already hold instead of returning it again. | Parameter | What it does | | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `since` | Leaves off posts published before a date (`YYYY-MM-DD` or ISO 8601). The page that reaches an older post ends the walk, and `pagination.stopped_at` is `since`. | | `stop_at_id` | Pass the id or URL of the newest post you already have. The page stops just before it, and `pagination.stopped_at` is `known_id`. | | `recent_days` | On Facebook and Instagram lists, keeps only posts from the last N days. | | `max_age_days` | On search endpoints, keeps only rows published within N days. The filter runs after each page is fetched, so every page walked is still billed. | `since` and `stop_at_id` work on `/v1/tiktok/profile/videos`, `/v1/instagram/profile/posts`, `/v1/instagram/profile/reels`, `/v1/twitter/user/tweets`, `/v1/facebook/profile/posts`, `/v1/youtube/channel/videos`, `/v1/youtube/channel/shorts` and `/v1/threads/user/posts`. See [Pagination](/docs/pagination). ## Which search? [#which-search] Every platform has its own keyword search. Pick the one for the platform you care about, and run several in parallel when you want more than one. | Your goal | Endpoint | Credits | | ------------------------------------------------------------------ | ----------------------------- | ------- | | TikTok videos by keyword | `/v1/tiktok/search` | 10–370 | | TikTok's Top tab for a keyword | `/v1/tiktok/search/top` | 10 | | YouTube videos, channels, playlists and Shorts | `/v1/youtube/search` | 10–150 | | YouTube videos with date, license and country filters | `/v1/youtube/search/advanced` | 10–110 | | X posts, with X search operators in the query | `/v1/twitter/search/tweets` | 10–50 | | A written answer about X, with the posts it cites | `/v1/twitter/ai-search` | 50 | | Reddit posts, site-wide | `/v1/reddit/search` | 10–300 | | Reddit posts inside one subreddit | `/v1/reddit/subreddit/search` | 10–260 | | Reddit threads with their top comments and a per-subreddit roll-up | `/v1/reddit/omni-search` | 50–90 | | Threads posts | `/v1/threads/search` | 10–340 | | Instagram reels | `/v1/instagram/search/reels` | 10–650 | | Facebook posts | `/v1/facebook/search/posts` | 10–50 | | LinkedIn posts and articles | `/v1/linkedin/search/posts` | 10–480 | | Pinterest pins | `/v1/pinterest/search` | 10–260 | Search results are the platform's own matches. When your keyword is also a common word or someone else's brand, filter the rows on your side: check `post.content.text` for the terms that must appear and the ones that must not, and use `post.language` where the platform states one. To find **accounts** rather than posts: | Your goal | Endpoint | Credits | | ------------------------------------------------- | ---------------------------------------------- | -------- | | TikTok creators, with bio and region on every row | `/v1/tiktok/search/users?include=profile` | 10–1,310 | | Threads accounts, with followers and bio | `/v1/threads/search/users?include=profile` | 10–130 | | X accounts | `/v1/twitter/search/users` | 10 | | Instagram profiles whose bio or captions match | `/v1/instagram/search/profiles` | 10–250 | | Reddit communities, with activity and rules | `/v1/reddit/subreddits/search?include=details` | 10–260 | | LinkedIn people | `/v1/linkedin/search/people` | 100–500 | An `include=profile` search replaces a user search plus one profile call per row. You pay per row actually filled. `/v1/reddit/subreddits/search` is usually the step before the other Reddit searches: `author.id` on each row is the `subreddit` value the rest of the platform takes. `/v1/reddit/omni-search` is never the cheap option, because it runs the search and expands the top threads' comments in the same call. Choose it when you want what people are saying rather than a list of links. Its `threads` parameter (1 to 8) is what moves the price. ## Getting a transcript [#getting-a-transcript] YouTube has two cheap options. Every other platform has one transcript endpoint at 100 credits. | Your goal | Endpoint | Credits | | ---------------------------------------------------- | -------------------------------------- | ------- | | YouTube caption files to download and parse yourself | `/v1/youtube/video/subtitles` | 10 | | YouTube transcript as timed segments with language | `/v1/youtube/video/transcript` | 30 | | TikTok video | `/v1/tiktok/post/transcript` | 100 | | Instagram reel or video (up to 2 minutes) | `/v1/instagram/media/transcript` | 100 | | Facebook video post | `/v1/facebook/post/transcript` | 100 | | X video post | `/v1/twitter/tweet/transcript` | 100 | | Reddit video post | `/v1/reddit/post/transcript` | 100 | | LinkedIn video post | `/v1/linkedin/post/transcript` | 100 | | Facebook Ad Library video ad | `/v1/facebook/adlibrary/ad/transcript` | 100 | On YouTube, take `subtitles` if you can parse caption files, and `transcript` if you want the words already split into segments. A video with no captions or no speech comes back as a `404` and is not charged. The [Video transcription recipe](/docs/recipes/video-transcription-api) wraps all of these in one function. ## Which comment endpoint? [#which-comment-endpoint] Each platform has a comment list, and a few have a way to look up one comment directly. | Your goal | Endpoint | Credits | | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | | A page of comments on a TikTok video | `/v1/tiktok/post/comments` | 10–30 | | A page of comments on a YouTube video | `/v1/youtube/video/comments` | 10 | | Replies to a tweet | `/v1/twitter/tweet/replies` | 10 | | Comments on a Facebook post | `/v1/facebook/post/comments` | 10 | | Replies under a Threads post (one window, no cursor) | `/v1/threads/post/comments` | 10–100 | | Comments on an Instagram post | `/v1/instagram/post/comments` | 50–150 | | Top-level comments on a LinkedIn post | `/v1/linkedin/post/comments` | 50 | | A Reddit post's whole comment tree, replies nested | `/v1/reddit/post/comments` | 50 | | One thread of replies | `/v1/tiktok/video/comment/replies`, `/v1/instagram/post/comment/replies`, `/v1/youtube/video/comment/replies`, `/v1/facebook/post/comment/replies`, `/v1/linkedin/post/comments/replies` | 10 (LinkedIn 50) | | Re-check one known TikTok or Instagram comment | `/v1/tiktok/comment`, `/v1/instagram/comment` | 20–60, 50–150 | Comments come back as text with their counts, author and `language` where the platform states one. Classifying them (sentiment, questions, complaints) is up to you; the [Sentiment analysis recipe](/docs/recipes/sentiment-analysis) shows one way. `/v1/reddit/post/comments` costs more because it returns the whole discussion with nested replies expanded under each comment's `replies`, which replaces a paging loop you would otherwise write. `/v1/tiktok/post/comments` and `/v1/instagram/post/comments` take `scan_pages` to read several pages in one call. TikTok and Instagram have no native "read one comment" route, so `/v1/tiktok/comment` and `/v1/instagram/comment` scan for the target and stop as soon as they find it. `deep_scan=true` widens the scan and is what reaches the top of each range. A comment that is not found is not charged. ## Hiring and job postings [#hiring-and-job-postings] Job data lives on LinkedIn. The choice is a keyword search, one company's openings, or just the count. | Your goal | Endpoint | Identifier | Credits | | --------------------------------- | -------------------------------- | ------------ | ------- | | Postings by keyword | `/v1/linkedin/search/jobs` | `query` | 100 | | Every opening at one company | `/v1/linkedin/company/jobs` | `company_id` | 100 | | How many roles a company has open | `/v1/linkedin/company/job-count` | `company_id` | 50 | | One posting in full | `/v1/linkedin/job` | `id` | 50 | To track hiring across a set of competitors, poll `company/job-count` on a schedule and spend the 200 on `company/jobs` only when the count moves. LinkedIn company endpoints need a numeric `company_id`, and you cannot read it from the page URL. Get it from `/v1/linkedin/company` (50 credits) with the page `url`, then store it. ## Ad libraries [#ad-libraries] Each network's ad library has a keyword door and an advertiser door. Use the keyword door when you only know a brand name. Use the advertiser door for a complete run once you have its id. | Your goal | Endpoint | Credits | | ------------------------------------------------------- | ----------------------------------------- | -------- | | Meta ads matching a keyword | `/v1/facebook/adlibrary/search/ads` | 50 | | Resolve a brand name to its Meta advertiser pages | `/v1/facebook/adlibrary/search/companies` | 50 | | Every ad one Meta page is running | `/v1/facebook/adlibrary/company/ads` | 50–350 | | One Meta ad in full | `/v1/facebook/adlibrary/ad` | 50 | | What a Meta video ad says out loud | `/v1/facebook/adlibrary/ad/transcript` | 100 | | LinkedIn ads by company, keyword or country | `/v1/linkedin/ads/search` | 50 | | One LinkedIn ad in full | `/v1/linkedin/ad` | 50 | | TikTok ads by keyword or advertiser name | `/v1/tiktok/adlibrary/search` | 50–170 | | One TikTok ad in full | `/v1/tiktok/adlibrary/ad` | 50 | | TikTok's leaderboard of top-performing ads for a market | `/v1/tiktok/ads/top` | 100–1000 | The [Ad library aggregation recipe](/docs/recipes/ads-library-aggregation) runs the three keyword doors in parallel for one brand. ## Still unsure? [#still-unsure] Ask the catalogue. `GET https://api.insightsocial.app/v1/endpoints?platform=tiktok` lists every TikTok endpoint with its description, parameters and price, and needs no key. To try a call before you write code, use the [Playground](https://www.insightsocial.app/portal/api/explorer) in your dashboard. Every endpoint and its credit price in one table. Holds, metered charges, repeats and what is free. Working builds that chain these endpoints together. Getting past the first page of any list. --- # Computed fields (/docs/computed-fields) The computed block (engagement rate, language, category, estimated reach) was removed in schema 2. How to get the same numbers from the fields every response carries. Responses used to carry a `computed` block: `engagement_rate`, `language`, `content_category`, `estimated_reach` and `vs_creator`. Schema 2, which every key gets from October 4, 2026, removed it on every endpoint. Responses now carry the platform's data only. What changed is on [Schema 2](/docs/schema-2). ## What to use instead [#what-to-use-instead] | Was | Now | | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `engagement_rate` | Compute it from `post.engagement` and the author's follower count. See below. | | `language` | `post.language` and `comment.language`: the language the platform itself states, such as `en`. `null` when the platform gives none. We do not detect languages. | | `content_category` | No replacement. Classify `post.content.text` yourself. | | `estimated_reach` | No replacement. Use `post.engagement.views` where the platform shows views. | | `vs_creator` | Compare a post with the author's other posts yourself, from a page of `/profile/posts` results. | ## Engagement rate [#engagement-rate] Engagement rate is engagement divided by followers. Every post carries the counts in `engagement`. The follower count comes from the author's profile (`data.author.followers` on the platform's profile endpoint). On Twitter/X and TikTok post rows it is also in `post.ext.author_followers`, a snapshot taken with the row. ```python title="engagement_rate.py" def engagement_rate(post, followers): """(likes + comments + shares) / followers. None when it cannot be computed.""" e = post["engagement"] counts = [e["likes"], e["comments"], e["shares"]] if not followers or all(c is None for c in counts): return None return sum(c for c in counts if c is not None) / followers ``` A count is `null` when the platform hides it or does not show it, so leave it out of the sum rather than reading it as zero. Instagram does not show share counts on most endpoints, so an Instagram rate is usually (likes + comments) / followers. Say which counts you used when you compare platforms. A full worked example is in [Creator engagement scoring](/docs/recipes/creator-engagement-scoring). Every field of a post is described on [Post](/docs/schema/post). --- # Credits (/docs/credits) What a call costs, how metered calls are held and settled, what is free, and where your balance comes from. Every call is priced per endpoint in credits. A fixed endpoint always costs the same; a metered endpoint quotes a range and charges what the call actually used. Calls that fail or find nothing cost nothing. Your balance is the same one you use for InsightSocial exports. ## What does a call cost? [#what-does-a-call-cost] Each endpoint has its own price, listed on its reference page and in the full table at [Endpoint pricing](/docs/endpoint-pricing). There are two kinds. **Fixed.** One number, charged on every successful call. | Endpoint | Credits | | -------------------------------- | ------- | | `GET /v1/instagram/profile` | 10 | | `GET /v1/linkedin/profile` | 50 | | `GET /v1/tiktok/post/transcript` | 100 | | `GET /v1/youtube/channel/about` | 250 | **Metered.** A range. What you pay depends on how much the call reads, for example how many pages of comments it walked. | Endpoint | Credits | | --------------------------------- | -------- | | `GET /v1/instagram/profile/posts` | 10–130 | | `GET /v1/tiktok/search` | 10–370 | | `GET /v1/linkedin/search/people` | 100–500 | | `GET /v1/reddit/profile/comments` | 20–2,000 | `GET /v1/endpoints` returns the same prices as JSON: `credits` is a number for a fixed endpoint and `{ "min", "max" }` for a metered one. It needs no key and costs nothing. ### Reserve the ceiling, settle the actual [#reserve-the-ceiling-settle-the-actual] Before a call runs, we hold the most it can cost against your balance: the fixed price, or, on a metered endpoint, the most that call can use with the parameters you sent. A call without `limit` or `include` holds one plain page, not the top of the range; asking for more rows or a join holds more. When the call finishes, we charge what it really used and release the rest. On a metered endpoint `credits_used` is often below the hold. You never pay more than the ceiling. If you send `max_pages` (see [Pagination](/docs/pagination)), the ceiling is the listed maximum multiplied by `max_pages`, and that product is the most the call can cost. If your balance cannot cover the ceiling, the call is refused before it starts: ```json title="Response" { "success": false, "error": { "type": "INSUFFICIENT_CREDITS", "message": "This call needs up to 130 credits and 120 remain. Top up at https://www.insightsocial.app/pricing" }, "request_id": "req_1a2b3c4d5e6f", "credits_used": 0, "credits_remaining": 120 } ``` The status is `402` and nothing is charged. ## Why a call cost what it did [#why-a-call-cost-what-it-did] Every successful response carries `charge_reason`, so you can tell why `credits_used` has the value it has. | `charge_reason` | What happened | Credits | | --------------- | ------------------------------------------------------ | -------------------------------------------- | | `miss` | We fetched the data for you | The endpoint's price (metered: what it used) | | `shared_cache` | The answer came from our shared cache (`cached: true`) | 2 | | `replay` | A retry with the same `Idempotency-Key` | 0 | | `no_result` | Nothing was found | 0 | | `dry_run` | You asked for a price quote with `dry_run=1` | 0 | Errors are never charged either: every error response has `credits_used: 0`. That includes a request refused with `400 UNSUPPORTED_PARAMETER` because it sent an analysis parameter that schema 2 does not accept; see [Errors](/docs/errors#unsupported_parameter). ### Repeating a call [#repeating-a-call] Repeating a call is charged like the first time, because a repeat can return newer data. Store responses you need again on your side. A retry with the same `Idempotency-Key` is the exception: it returns the recorded outcome and costs 0 (`replay`). See [Caching](/docs/caching). ### Price a call without running it [#price-a-call-without-running-it] Add `dry_run=1` to any data endpoint to get our price quote instead of the data. Nothing is fetched and nothing is charged: ```bash title="cURL" curl "https://api.insightsocial.app/v1/instagram/profile/posts?handle=nasa&dry_run=1" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```json title="Response" { "success": true, "platform": "instagram", "endpoint": "/v1/instagram/profile/posts", "schema_version": "2", "data": { "dry_run": { "credits_min": 10, "credits_max": 130 } }, "unavailable": [], "credits_used": 0, "credits_remaining": 9980, "request_id": "req_2ac2c2b769ef", "cached": false, "idempotent_replay": false, "charge_reason": "dry_run", "free_call": false } ``` `credits_min` is the least the call can cost and `credits_max` is what we would hold for those exact parameters, including the `max_pages` you sent. A quote never uses a free call. ### Prices under schema 2 [#prices-under-schema-2] Prices did not change with schema 2. A few metered endpoints hold less of your balance while they run, because the analysis they used to do, and could be charged for, is gone. The charge is still what the call actually used. ## Free calls [#free-calls] Every account gets **10 free calls**, once, for its lifetime. A free call is any call that would have been charged (a `miss` or a `shared_cache`) whose ceiling is 100 credits or less. It comes back with `free_call: true` and `credits_used: 0`. Calls above 100 credits are charged normally even while free calls remain. Empty results, errors and replays of a call we already answered never use one up. The one replay that can use a free call is a replay of a call we never answered successfully, because that replay is charged; see [Production checklist](/docs/production-checklist#make-retries-safe). ## Where your credits come from [#where-your-credits-come-from] | Source | Credits | Expires | | ---------------------------------------------------- | ---------------- | ------------------------------------- | | Free plan | 500 per month | Resets on the 1st of each month (UTC) | | Pro plan ($9.99/month, or $7.99/month billed yearly) | 10,000 per month | Resets monthly | | Credit packs (one-time purchase) | Pack size | Never | The monthly grant is spent first and pack credits second. Referral bonuses are added to your pack credits. This is **one balance**, shared with InsightSocial exports. Credits you spend on the API reduce what you can export, and the other way round. Buy a plan or a pack at [insightsocial.app/pricing](https://www.insightsocial.app/pricing). API credits are non-refundable. Failed calls, empty results and `dry_run` calls are simply never charged in the first place. ## How do I check my balance? [#how-do-i-check-my-balance] 1. **On every response.** The body has `credits_remaining`, and the `X-Credits-Remaining` header carries the same number. The header is left out when the balance could not be read. 2. **With `GET /v1/credits`.** Needs your key, costs 0 credits. 3. **In the dashboard.** The API section of your portal shows balance, usage and an activity log. ```bash title="cURL" curl "https://api.insightsocial.app/v1/credits" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```json title="Response" { "success": true, "credits_used": 0, "credits_remaining": 9180, "plan": { "tier": "pro", "credits_per_month": 10000, "resets_at": "2026-10-01T00:00:00.000Z" }, "usage": { "window_start": "2026-09-01T00:00:00.000Z", "used": 1320, "used_export": 400, "used_api": 920 }, "pack_credits": 500, "free_calls": { "remaining": 0, "total": 10 }, "key": { "id": "3f6c2a8e-7b41-4d0f-9a52-1c8e5d7b9f20", "name": "production" }, "request_id": "req_1a2b3c4d5e6f" } ``` ## Retries with an Idempotency-Key are not charged twice [#retries-with-an-idempotency-key-are-not-charged-twice] Send an `Idempotency-Key` header and retry with the same value. A replay of a call we already answered comes back with `idempotent_replay: true`, `charge_reason: "replay"` and `credits_used: 0`. See [Production checklist](/docs/production-checklist#make-retries-safe) for the key format and the edge cases. ## Next steps [#next-steps] The price of every endpoint. Shared-cache hits and forcing a fresh fetch. What a full walk costs, and how `max_pages` changes the ceiling. Checks that cost nothing. ## Endpoints billed per item [#endpoints-billed-per-item] `/v1/linkedin/profile/all` and `/v1/pinterest/url-stats` take a list in `urls` and are billed per item: the listed price is for one URL, and a call with three URLs costs three times as much. The hold and the `dry_run` quote count the URLs the same way. --- # Data availability (/docs/data-availability) How to read values, nulls, absent fields, the unavailable list and cache state in a response, and which fields each platform never shows. A successful response tells you what one endpoint returned for one request. It is not proof that nothing else exists on the source platform. The excerpt below is trimmed from a real `GET /v1/twitter/user/tweets` response. It contains each state your parser needs to handle: ```json title="Response" { "success": true, "platform": "twitter", "endpoint": "/v1/twitter/user/tweets", "schema_version": "2", "data": { "items": [ { "post": { "id": "2106502020849127865", "kind": null, "content": { "text": "RT @Astro_Jessica: Room with a view…", "media_urls": null }, "author": { "id": null, "username": "NASA", "display_name": "NASA" }, "engagement": { "views": 40169, "likes": 0, "comments": 0, "shares": 572, "saves": 0 }, "published_at": "2026-10-03T21:49:53.000Z", "language": "en", "ext": { "author_followers": 92378243, "quote_count": 0 } } } ], "total": null, "truncated": null }, "pagination": { "next_cursor": "v2c.M-GEP84JXNZPqhQ0NlEOubhbuFGG…", "has_more": true, "page_size": 19 }, "unavailable": ["items[].post.author.id"], "credits_used": 10, "credits_remaining": 9490, "request_id": "req_161a29ec13ca", "cached": false, "idempotent_replay": false, "charge_reason": "miss", "free_call": false } ``` In this row, `post.id` and `post.content.text` hold values. `post.kind` is `null` because the platform did not say what format the post is. `post.author.id` is `null` on every row of the page, so the envelope lists it in `unavailable`. `ext.author_followers` is a platform-specific extra: present here, absent on other platforms. ## Public data only [#public-data-only] InsightSocial returns data that anyone can see on the platform without logging in. Private accounts and content behind a login are out of scope. What is public changes over time. A post can be edited or deleted, an account can go private, and a platform can stop showing a field. A successful call describes what the chosen endpoint could see at that moment, in that cache state. ## Four states to tell apart [#four-states-to-tell-apart] | State | What it means | What to do | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- | | Value | The field is there and its value matches the documented type. | Use it as documented. | | `null` | The field belongs to the schema, but no trustworthy value was available: the platform hides it on this item, does not show it at all, or this response could not fill it. | Keep the `null`. Never turn it into `0`, `""` or an estimate. A real zero is sent as `0`. | | Absent | Only `ext` keys and a few envelope fields (`pagination`, `pagination.stopped_at`) can be absent. Every field of the [unified schema](/docs/schema/post) is always present, as a value or `null`. | Treat `ext` keys as optional. Absent is not the same as `null` or `0`. | | Listed in `unavailable` | The platform normally exposes this field, but this response could not fill it on any item. | Keep the response. Log the path, and do not read the field from this endpoint until it fills again. | `unavailable` never turns a success into an error. For the envelope itself see [Response schema](/docs/response-schema). ## `unavailable` [#unavailable] `unavailable` is a list of paths into `data`, such as `"post.author.id"` for a single entity or `"items[].post.author.id"` for a page. It is `[]` when nothing is missing. A path is listed only when all three hold: 1. **The field is one we judge.** These are the fields every entity has once a platform exposes them: ids, author identity, counts, URLs and times. A `null` bio, `external_url`, `content.text` or `duration_seconds` usually means the profile has no link or the post is a photo, so those are never listed. 2. **The platform exposes it.** Fields a platform never shows are in the table below, once, instead of in every response. 3. **It is `null` on every item of the page.** One filled row shows the endpoint can fill it; a `null` on a single row is the platform hiding it on that item (a hidden like count, for example). An empty page lists nothing: an empty search is not a missing field. Fields judged per entity. `kind` is not judged on posts: it is `null` whenever the source does not say what format a post is, which on some endpoints is every row. | Entity | Judged fields | | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Post | `url`, `author.id`, `author.username`, `author.display_name`, `author.avatar_url`, `engagement.views`, `engagement.likes`, `engagement.comments`, `engagement.shares`, `engagement.saves`, `published_at` | | Comment | `url`, `post_id`, `author.id`, `author.username`, `author.display_name`, `author.avatar_url`, `engagement.likes`, `engagement.replies`, `published_at` | | Profile | `username`, `display_name`, `avatar_url`, `verified`, `followers`, `following`, `posts_count`, `likes_count`, `url`, `private`, `joined_at` | | Transcript | `text` | Beta endpoints, which return the platform's own structure, always send `unavailable: []`. ## Fields a platform never shows [#fields-a-platform-never-shows] These fields are `null` on every response from that platform. They are never listed in `unavailable`, because nothing can fill them. | Platform | Posts | Comments | Profiles | | --------- | ---------------------------------------------------------------------------------- | --------------------- | ------------------------------------------------------ | | Instagram | `engagement.shares`, `engagement.saves` | — | `likes_count`, `joined_at` | | TikTok | — | `url` | `joined_at` | | Facebook | `author.username`, `engagement.saves` | `author.username` | `following`, `posts_count`, `private` | | Twitter/X | — | — | — | | Threads | `engagement.saves` | — | `following`, `posts_count`, `likes_count`, `joined_at` | | YouTube | `engagement.shares`, `engagement.saves` | — | `following`, `likes_count`, `private` | | Reddit | `engagement.views`, `engagement.shares`, `engagement.saves`, `author.display_name` | `author.display_name` | `following`, `posts_count`, `likes_count` | | LinkedIn | `engagement.views`, `engagement.saves` | — | `posts_count`, `likes_count`, `private`, `joined_at` | | Pinterest | `engagement.views` | — | — | The table is checked against live traffic, and it changes when a platform changes what it shows publicly. ## One page is not the whole set [#one-page-is-not-the-whole-set] A list page only proves what is on that page. A row the platform sends without an id is left out of `items` and does not count toward `page_size`, but the page is still charged at its price: the platform returned it and we paid for it. Only when every row on a page lacks an id is the call refused instead, with `503 UPSTREAM_INVALID`, and nothing is charged. Follow the top-level `pagination` object if the response has one. Pass `pagination.next_cursor` back exactly as you received it (it starts with `v2c.`) in the `cursor` parameter, with the same parameters as the first page, and stop when `pagination.has_more` is `false`. Do not stop based on `data.total`, which is the platform's own total and not a page count. The full loop is in [Pagination](/docs/pagination). Reaching the last page means you have every page the endpoint offered. It does not guarantee full history: endpoint depth, deletions and access restrictions can still leave gaps. When the platform itself cuts a list short, `data.truncated` is `true`. ## Order is not stable [#order-is-not-stable] A platform can return records in a different order from one request to the next. Do not use a row's position as an identifier, and do not assume page one always spans the same time window. When an endpoint offers a sort parameter, use it. Store the IDs and timestamps from each row, and deduplicate across pages and runs when your workflow needs it. ## Region, language and date filters [#region-language-and-date-filters] Filters and depth differ per endpoint. A `region`, language or date parameter on one search endpoint says nothing about any other endpoint on the same platform. Check the endpoint's parameters before you design a query. If a filter is not listed, assume it is not applied. When you filter on your side, do it only on fields that are present and fit the decision. For example, read the [TikTok profile videos reference](/docs/api-reference/tiktok/profile-videos) before you build on `/v1/tiktok/profile/videos`. It lists the parameters that endpoint accepts and the fields it returns. ## Freshness and cache state [#freshness-and-cache-state] The body field `cached` tells you whether the response came from a recent fetch rather than a new one. A cached response costs 2 credits. A cache hit is never guaranteed. When your decision needs a new fetch, send `Cache-Control: no-cache` or add `fresh=1`. The call is then charged at the endpoint's normal price. Details are in [Caching](/docs/caching). A new fetch is fresher, not more complete. It still reflects only what the platform shows at that moment. An empty result is never charged. See [Credits](/docs/credits). ## Provenance and extension fields [#provenance-and-extension-fields] Store `platform`, `endpoint` and `request_id` alongside the IDs and URLs you keep. They let you trace any stored record back to the call that produced it. Fields under `ext.*` carry platform-specific details and the IDs you need to chain one endpoint into another. Their keys are snake\_case. Treat every `ext` field as optional, and check the [unified schema](/docs/schema/post) for which platforms fill it. Values that every platform can mean the same way live in the core instead: the author's id is `author.id`, the content language is `language`, and a bio link is `external_url`. Endpoints marked **beta** on their reference page (metrics and some lists) return the source's own structure under our naming rules: snake\_case keys, string ids, and ISO times where the source gives a timestamp. Some carry the source's own figures and date text. Expect their shape to differ by platform, and their fields to change. `profile/full` is beta as well, but its `data.profile` is a typed profile and its `data.posts[].post` are typed posts; only its other keys follow these beta rules. New optional fields can appear in responses. Ignore fields you do not recognize instead of rejecting the response, and validate only the fields you actually use. ## Pick the endpoint that fits the question [#pick-the-endpoint-that-fits-the-question] The endpoint you choose sets what you can see. A profile lookup, a post list, a keyword search and a comment list answer different questions, with different filters and depth. Start from the question, then use [Choosing endpoints](/docs/choosing-endpoints) to find the narrowest endpoint that returns the object, coverage and controls you need. ## Checklist [#checklist] Before relying on an endpoint, confirm that: * It returns the object and fields your question needs. * The records you expect are public. * Its region, language, date, ordering and depth controls are documented for that endpoint. * Your loop sends `pagination.next_cursor` back unchanged and stops on `pagination.has_more`. * You log `unavailable` on every response and know which fields the platform never shows. * Your parser treats a value, `null` and an absent field as three different cases. * Your freshness policy reads `cached` and budgets for fresh fetches at the normal price. * Stored records keep their IDs plus `request_id`, `platform` and `endpoint`. * Unknown fields are ignored, and the fields you use are validated. Match the question to the endpoint with the right scope. The envelope, what sits inside data, and unavailable. Walk every page with the cursor and the stop signal. Read cache state and decide when to pay for a fresh fetch. --- # Endpoint pricing (/docs/endpoint-pricing) The price of every endpoint, in credits. 302 endpoints across 25 platforms. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Prices are per call and per endpoint. A range means the endpoint is **metered**: we hold the top of the range when the call starts and charge what it actually read when it finishes. Empty results, failed calls and `dry_run=1` cost nothing. How charging works: [Credits](/docs/credits). ## Bluesky [#bluesky] | Endpoint | Credits | Paginates | | ------------------------------------------------------------------ | ------- | --------- | | [`/v1/bluesky/profile`](/docs/api-reference/bluesky/profile) | 10 | — | | [`/v1/bluesky/user/posts`](/docs/api-reference/bluesky/user-posts) | 10 | — | | [`/v1/bluesky/post`](/docs/api-reference/bluesky/post) | 10 | — | | [`/v1/bluesky/search`](/docs/api-reference/bluesky/search) | 10 | — | ## Douyin [#douyin] | Endpoint | Credits | Paginates | | -------------------------------------------------------------------------- | ------- | --------- | | [`/v1/douyin/search`](/docs/api-reference/douyin/search) | 50–1250 | — | | [`/v1/douyin/profile`](/docs/api-reference/douyin/profile) | 60 | — | | [`/v1/douyin/profile/posts`](/docs/api-reference/douyin/profile-posts) | 50–1250 | — | | [`/v1/douyin/post`](/docs/api-reference/douyin/post) | 100 | — | | [`/v1/douyin/post/comments`](/docs/api-reference/douyin/post-comments) | 20–1000 | — | | [`/v1/douyin/comment/replies`](/docs/api-reference/douyin/comment-replies) | 50–2500 | Yes | | [`/v1/douyin/search/users`](/docs/api-reference/douyin/search-users) | 50–1250 | Yes | | [`/v1/douyin/trending`](/docs/api-reference/douyin/trending) | 250 | — | ## Facebook [#facebook] | Endpoint | Credits | Paginates | | ------------------------------------------------------------------------------------------------------ | ------- | --------- | | [`/v1/facebook/profile`](/docs/api-reference/facebook/profile) | 10 | — | | [`/v1/facebook/profile/posts`](/docs/api-reference/facebook/profile-posts) | 10–40 | Yes | | [`/v1/facebook/post`](/docs/api-reference/facebook/post) | 10 | — | | [`/v1/facebook/post/comments`](/docs/api-reference/facebook/post-comments) | 10 | Yes | | [`/v1/facebook/group/posts`](/docs/api-reference/facebook/group-posts) | 10 | Yes | | [`/v1/facebook/group`](/docs/api-reference/facebook/group) | 10 | — | | [`/v1/facebook/post/transcript`](/docs/api-reference/facebook/post-transcript) | 100 | — | | [`/v1/facebook/profile/photos`](/docs/api-reference/facebook/profile-photos) | 10–90 | Yes | | [`/v1/facebook/profile/reels`](/docs/api-reference/facebook/profile-reels) | 10 | Yes | | [`/v1/facebook/adlibrary/ad`](/docs/api-reference/facebook/adlibrary-ad) | 50 | — | | [`/v1/facebook/adlibrary/company/ads`](/docs/api-reference/facebook/adlibrary-company-ads) | 50–350 | Yes | | [`/v1/facebook/adlibrary/search/ads`](/docs/api-reference/facebook/adlibrary-search-ads) | 50 | Yes | | [`/v1/facebook/adlibrary/search/companies`](/docs/api-reference/facebook/adlibrary-search-companies) | 50 | — | | [`/v1/facebook/profile/events`](/docs/api-reference/facebook/profile-events) | 10–90 | Yes | | [`/v1/facebook/post/comment/replies`](/docs/api-reference/facebook/post-comment-replies) | 10 | Yes | | [`/v1/facebook/marketplace/location/search`](/docs/api-reference/facebook/marketplace-location-search) | 10 | — | | [`/v1/facebook/marketplace/search`](/docs/api-reference/facebook/marketplace-search) | 10 | Yes | | [`/v1/facebook/marketplace/item`](/docs/api-reference/facebook/marketplace-item) | 10 | — | | [`/v1/facebook/events/search`](/docs/api-reference/facebook/events-search) | 10 | Yes | | [`/v1/facebook/events`](/docs/api-reference/facebook/events) | 10–130 | Yes | | [`/v1/facebook/event/details`](/docs/api-reference/facebook/event-details) | 10 | — | | [`/v1/facebook/adlibrary/ad/transcript`](/docs/api-reference/facebook/adlibrary-ad-transcript) | 100 | — | | [`/v1/facebook/profile/full`](/docs/api-reference/facebook/profile-full) | 50 | Yes | | [`/v1/facebook/profile/reels/full`](/docs/api-reference/facebook/profile-reels-full) | 50–250 | Yes | | [`/v1/facebook/search/posts`](/docs/api-reference/facebook/search-posts) | 10–50 | Yes | | [`/v1/facebook/search/pages`](/docs/api-reference/facebook/search-pages) | 10 | Yes | | [`/v1/facebook/search/people`](/docs/api-reference/facebook/search-people) | 10 | Yes | | [`/v1/facebook/search/videos`](/docs/api-reference/facebook/search-videos) | 10 | Yes | | [`/v1/facebook/search/groups`](/docs/api-reference/facebook/search-groups) | 10–110 | Yes | ## Instagram [#instagram] | Endpoint | Credits | Paginates | | ------------------------------------------------------------------------------------------ | ------- | --------- | | [`/v1/instagram/profile`](/docs/api-reference/instagram/profile) | 10 | — | | [`/v1/instagram/profile/about`](/docs/api-reference/instagram/profile-about) | 10 | — | | [`/v1/instagram/profile/posts`](/docs/api-reference/instagram/profile-posts) | 10–130 | Yes | | [`/v1/instagram/post`](/docs/api-reference/instagram/post) | 10 | — | | [`/v1/instagram/post/comments`](/docs/api-reference/instagram/post-comments) | 50–150 | Yes | | [`/v1/instagram/comment`](/docs/api-reference/instagram/comment) | 50–150 | — | | [`/v1/instagram/basic-profile`](/docs/api-reference/instagram/basic-profile) | 10 | — | | [`/v1/instagram/profile/reels`](/docs/api-reference/instagram/profile-reels) | 10–140 | Yes | | [`/v1/instagram/highlights`](/docs/api-reference/instagram/highlights) | 10 | — | | [`/v1/instagram/highlight/detail`](/docs/api-reference/instagram/highlight-detail) | 10 | — | | [`/v1/instagram/search/reels`](/docs/api-reference/instagram/search-reels) | 10–650 | Yes | | [`/v1/instagram/media/transcript`](/docs/api-reference/instagram/media-transcript) | 100 | — | | [`/v1/instagram/media/screen-text`](/docs/api-reference/instagram/media-screen-text) | 50 | — | | [`/v1/instagram/audio/reels`](/docs/api-reference/instagram/audio-reels) | 10 | Yes | | [`/v1/instagram/search/hashtag`](/docs/api-reference/instagram/search-hashtag) | 50 | Yes | | [`/v1/instagram/search/profiles`](/docs/api-reference/instagram/search-profiles) | 10–250 | Yes | | [`/v1/instagram/reels/trending`](/docs/api-reference/instagram/reels-trending) | 50 | — | | [`/v1/instagram/followers`](/docs/api-reference/instagram/followers) | 50–100 | Yes | | [`/v1/instagram/following`](/docs/api-reference/instagram/following) | 50–100 | Yes | | [`/v1/instagram/similar`](/docs/api-reference/instagram/similar) | 50–850 | — | | [`/v1/instagram/post/likers`](/docs/api-reference/instagram/post-likers) | 50 | — | | [`/v1/instagram/post/stats`](/docs/api-reference/instagram/post-stats) | 50–90 | — | | [`/v1/instagram/tagged`](/docs/api-reference/instagram/tagged) | 50 | Yes | | [`/v1/instagram/location/posts`](/docs/api-reference/instagram/location-posts) | 50 | Yes | | [`/v1/instagram/engagement`](/docs/api-reference/instagram/engagement) | 50 | — | | [`/v1/instagram/search/location`](/docs/api-reference/instagram/search-location) | 50 | — | | [`/v1/instagram/search`](/docs/api-reference/instagram/search) | 10 | — | | [`/v1/instagram/search/popular`](/docs/api-reference/instagram/search-popular) | 10–130 | Yes | | [`/v1/instagram/post/comment/replies`](/docs/api-reference/instagram/post-comment-replies) | 10 | Yes | | [`/v1/instagram/username-suggestions`](/docs/api-reference/instagram/username-suggestions) | 50 | — | | [`/v1/instagram/search/music`](/docs/api-reference/instagram/search-music) | 50 | Yes | | [`/v1/instagram/stories`](/docs/api-reference/instagram/stories) | 50 | — | | [`/v1/instagram/story/download`](/docs/api-reference/instagram/story-download) | 50 | — | | [`/v1/instagram/profile/full`](/docs/api-reference/instagram/profile-full) | 50 | Yes | | [`/v1/instagram/profile/reels/full`](/docs/api-reference/instagram/profile-reels-full) | 50–250 | Yes | | [`/v1/instagram/profile/posts/full`](/docs/api-reference/instagram/profile-posts-full) | 50–250 | Yes | ## Kick [#kick] | Endpoint | Credits | Paginates | | ------------------------------------------------ | ------- | --------- | | [`/v1/kick/clip`](/docs/api-reference/kick/clip) | 10 | — | ## Kwai [#kwai] | Endpoint | Credits | Paginates | | ------------------------------------------------------------ | ------- | --------- | | [`/v1/kwai/profile`](/docs/api-reference/kwai/profile) | 10 | — | | [`/v1/kwai/user/posts`](/docs/api-reference/kwai/user-posts) | 10 | Yes | | [`/v1/kwai/post`](/docs/api-reference/kwai/post) | 10 | — | ## LinkedIn [#linkedin] | Endpoint | Credits | Paginates | | ---------------------------------------------------------------------------------------------------------- | ------- | --------- | | [`/v1/linkedin/profile`](/docs/api-reference/linkedin/profile) | 50 | — | | [`/v1/linkedin/company`](/docs/api-reference/linkedin/company) | 50 | — | | [`/v1/linkedin/company/by-domain`](/docs/api-reference/linkedin/company-by-domain) | 50 | — | | [`/v1/linkedin/profile/similar`](/docs/api-reference/linkedin/profile-similar) | 50 | — | | [`/v1/linkedin/search/companies`](/docs/api-reference/linkedin/search-companies) | 100 | Yes | | [`/v1/linkedin/profile/posted-jobs`](/docs/api-reference/linkedin/profile-posted-jobs) | 50 | — | | [`/v1/linkedin/job/hiring-team`](/docs/api-reference/linkedin/job-hiring-team) | 50 | — | | [`/v1/linkedin/profile/articles`](/docs/api-reference/linkedin/profile-articles) | 50 | Yes | | [`/v1/linkedin/article`](/docs/api-reference/linkedin/article) | 50 | — | | [`/v1/linkedin/article/comments`](/docs/api-reference/linkedin/article-comments) | 50 | Yes | | [`/v1/linkedin/article/reactions`](/docs/api-reference/linkedin/article-reactions) | 50 | Yes | | [`/v1/linkedin/profile/activity`](/docs/api-reference/linkedin/profile-activity) | 50 | — | | [`/v1/linkedin/company/also-viewed`](/docs/api-reference/linkedin/company-also-viewed) | 50 | — | | [`/v1/linkedin/company/employees-count`](/docs/api-reference/linkedin/company-employees-count) | 50 | — | | [`/v1/linkedin/profile/interests/schools`](/docs/api-reference/linkedin/profile-interests-schools) | 50 | Yes | | [`/v1/linkedin/profile/interests/newsletters`](/docs/api-reference/linkedin/profile-interests-newsletters) | 50 | Yes | | [`/v1/linkedin/profile/interests/top-voices`](/docs/api-reference/linkedin/profile-interests-top-voices) | 50 | — | | [`/v1/linkedin/profile/position-skills`](/docs/api-reference/linkedin/profile-position-skills) | 50 | — | | [`/v1/linkedin/profile/top-position`](/docs/api-reference/linkedin/profile-top-position) | 50 | — | | [`/v1/linkedin/search/people/by-url`](/docs/api-reference/linkedin/search-people-by-url) | 100 | — | | [`/v1/linkedin/profile/all`](/docs/api-reference/linkedin/profile-all) | 50 | — | | [`/v1/linkedin/profile/complete`](/docs/api-reference/linkedin/profile-complete) | 100 | — | | [`/v1/linkedin/profile/with-posts`](/docs/api-reference/linkedin/profile-with-posts) | 100 | — | | [`/v1/linkedin/post/with-comments`](/docs/api-reference/linkedin/post-with-comments) | 50 | — | | [`/v1/linkedin/post`](/docs/api-reference/linkedin/post) | 50 | — | | [`/v1/linkedin/search/people`](/docs/api-reference/linkedin/search-people) | 100–500 | Yes | | [`/v1/linkedin/company/people`](/docs/api-reference/linkedin/company-people) | 100–500 | Yes | | [`/v1/linkedin/post/comments`](/docs/api-reference/linkedin/post-comments) | 50 | Yes | | [`/v1/linkedin/profile/posts`](/docs/api-reference/linkedin/profile-posts) | 50–2000 | — | | [`/v1/linkedin/profile/reactions`](/docs/api-reference/linkedin/profile-reactions) | 50 | — | | [`/v1/linkedin/post/reposts`](/docs/api-reference/linkedin/post-reposts) | 50 | Yes | | [`/v1/linkedin/group/posts`](/docs/api-reference/linkedin/group-posts) | 50 | Yes | | [`/v1/linkedin/company/affiliated-pages`](/docs/api-reference/linkedin/company-affiliated-pages) | 50 | — | | [`/v1/linkedin/post/comments/replies`](/docs/api-reference/linkedin/post-comments-replies) | 50 | Yes | | [`/v1/linkedin/profile/experiences`](/docs/api-reference/linkedin/profile-experiences) | 50 | Yes | | [`/v1/linkedin/profile/educations`](/docs/api-reference/linkedin/profile-educations) | 50 | Yes | | [`/v1/linkedin/profile/skills`](/docs/api-reference/linkedin/profile-skills) | 50 | Yes | | [`/v1/linkedin/profile/honors`](/docs/api-reference/linkedin/profile-honors) | 50 | Yes | | [`/v1/linkedin/profile/certifications`](/docs/api-reference/linkedin/profile-certifications) | 50 | Yes | | [`/v1/linkedin/profile/publications`](/docs/api-reference/linkedin/profile-publications) | 50 | Yes | | [`/v1/linkedin/profile/volunteers`](/docs/api-reference/linkedin/profile-volunteers) | 50 | Yes | | [`/v1/linkedin/profile/recommendations`](/docs/api-reference/linkedin/profile-recommendations) | 50 | Yes | | [`/v1/linkedin/profile/interests/companies`](/docs/api-reference/linkedin/profile-interests-companies) | 50 | Yes | | [`/v1/linkedin/profile/interests/groups`](/docs/api-reference/linkedin/profile-interests-groups) | 50 | Yes | | [`/v1/linkedin/profile/images`](/docs/api-reference/linkedin/profile-images) | 50 | — | | [`/v1/linkedin/profile/videos`](/docs/api-reference/linkedin/profile-videos) | 50 | — | | [`/v1/linkedin/profile/comments`](/docs/api-reference/linkedin/profile-comments) | 50 | — | | [`/v1/linkedin/post/reactions`](/docs/api-reference/linkedin/post-reactions) | 100–500 | Yes | | [`/v1/linkedin/company/jobs`](/docs/api-reference/linkedin/company-jobs) | 100 | Yes | | [`/v1/linkedin/search/jobs`](/docs/api-reference/linkedin/search-jobs) | 100 | Yes | | [`/v1/linkedin/search/location`](/docs/api-reference/linkedin/search-location) | 10 | — | | [`/v1/linkedin/search/schools`](/docs/api-reference/linkedin/search-schools) | 10 | Yes | | [`/v1/linkedin/search/industry`](/docs/api-reference/linkedin/search-industry) | 10 | — | | [`/v1/linkedin/profile/about`](/docs/api-reference/linkedin/profile-about) | 50 | — | | [`/v1/linkedin/profile/contact`](/docs/api-reference/linkedin/profile-contact) | 50 | — | | [`/v1/linkedin/profile/stats`](/docs/api-reference/linkedin/profile-stats) | 50 | — | | [`/v1/linkedin/company/job-count`](/docs/api-reference/linkedin/company-job-count) | 50 | — | | [`/v1/linkedin/company/insights`](/docs/api-reference/linkedin/company-insights) | 50 | — | | [`/v1/linkedin/group`](/docs/api-reference/linkedin/group) | 50 | — | | [`/v1/linkedin/job`](/docs/api-reference/linkedin/job) | 50 | — | | [`/v1/linkedin/company/posts`](/docs/api-reference/linkedin/company-posts) | 50 | Yes | | [`/v1/linkedin/ad`](/docs/api-reference/linkedin/ad) | 50 | — | | [`/v1/linkedin/ads/search`](/docs/api-reference/linkedin/ads-search) | 50 | Yes | | [`/v1/linkedin/profile/posts/archive`](/docs/api-reference/linkedin/profile-posts-archive) | 50–5000 | Yes | | [`/v1/linkedin/search/posts`](/docs/api-reference/linkedin/search-posts) | 10–480 | Yes | | [`/v1/linkedin/post/transcript`](/docs/api-reference/linkedin/post-transcript) | 100 | — | | [`/v1/linkedin/profile/full`](/docs/api-reference/linkedin/profile-full) | 50 | Yes | ## Nextdoor [#nextdoor] | Endpoint | Credits | Paginates | | ---------------------------------------------------------------- | ------- | --------- | | [`/v1/nextdoor/business`](/docs/api-reference/nextdoor/business) | 30 | — | | [`/v1/nextdoor/search`](/docs/api-reference/nextdoor/search) | 30–170 | — | ## Pinterest [#pinterest] | Endpoint | Credits | Paginates | | ------------------------------------------------------------------------ | ------- | --------- | | [`/v1/pinterest/search`](/docs/api-reference/pinterest/search) | 10–260 | Yes | | [`/v1/pinterest/pin`](/docs/api-reference/pinterest/pin) | 10 | — | | [`/v1/pinterest/url-stats`](/docs/api-reference/pinterest/url-stats) | 10 | — | | [`/v1/pinterest/board`](/docs/api-reference/pinterest/board) | 10–160 | Yes | | [`/v1/pinterest/user/boards`](/docs/api-reference/pinterest/user-boards) | 10 | — | | [`/v1/pinterest/trends`](/docs/api-reference/pinterest/trends) | 100 | — | ## Quora [#quora] | Endpoint | Credits | Paginates | | ---------------------------------------------------------- | ------- | --------- | | [`/v1/quora/search`](/docs/api-reference/quora/search) | 50 | — | | [`/v1/quora/post`](/docs/api-reference/quora/post) | 50 | — | | [`/v1/quora/answers`](/docs/api-reference/quora/answers) | 50 | — | | [`/v1/quora/posts`](/docs/api-reference/quora/posts) | 50 | — | | [`/v1/quora/profiles`](/docs/api-reference/quora/profiles) | 50 | — | | [`/v1/quora/spaces`](/docs/api-reference/quora/spaces) | 50 | — | | [`/v1/quora/topics`](/docs/api-reference/quora/topics) | 50 | — | ## Reddit [#reddit] | Endpoint | Credits | Paginates | | ------------------------------------------------------------------------------ | ------- | --------- | | [`/v1/reddit/subreddit`](/docs/api-reference/reddit/subreddit) | 10 | Yes | | [`/v1/reddit/subreddit/details`](/docs/api-reference/reddit/subreddit-details) | 10 | — | | [`/v1/reddit/search`](/docs/api-reference/reddit/search) | 10–300 | Yes | | [`/v1/reddit/post`](/docs/api-reference/reddit/post) | 10 | — | | [`/v1/reddit/post/comments`](/docs/api-reference/reddit/post-comments) | 50 | Yes | | [`/v1/reddit/subreddit/search`](/docs/api-reference/reddit/subreddit-search) | 10–260 | Yes | | [`/v1/reddit/profile`](/docs/api-reference/reddit/profile) | 10 | — | | [`/v1/reddit/profile/posts`](/docs/api-reference/reddit/profile-posts) | 10 | Yes | | [`/v1/reddit/profile/comments`](/docs/api-reference/reddit/profile-comments) | 20–2000 | — | | [`/v1/reddit/search/comments`](/docs/api-reference/reddit/search-comments) | 10 | Yes | | [`/v1/reddit/subreddits/search`](/docs/api-reference/reddit/subreddits-search) | 10–260 | Yes | | [`/v1/reddit/search/media`](/docs/api-reference/reddit/search-media) | 10 | Yes | | [`/v1/reddit/post/transcript`](/docs/api-reference/reddit/post-transcript) | 100 | — | | [`/v1/reddit/omni-search`](/docs/api-reference/reddit/omni-search) | 50–90 | Yes | ## Rumble [#rumble] | Endpoint | Credits | Paginates | | ---------------------------------------------------------------------------- | ------- | --------- | | [`/v1/rumble/search`](/docs/api-reference/rumble/search) | 10 | Yes | | [`/v1/rumble/channel/videos`](/docs/api-reference/rumble/channel-videos) | 10 | Yes | | [`/v1/rumble/video`](/docs/api-reference/rumble/video) | 10 | — | | [`/v1/rumble/video/transcript`](/docs/api-reference/rumble/video-transcript) | 100 | — | | [`/v1/rumble/video/comments`](/docs/api-reference/rumble/video-comments) | 10 | — | ## Snapchat [#snapchat] | Endpoint | Credits | Paginates | | ------------------------------------------------------------------------------------ | ------- | --------- | | [`/v1/snapchat/profile`](/docs/api-reference/snapchat/profile) | 10 | — | | [`/v1/snapchat/spotlight/comments`](/docs/api-reference/snapchat/spotlight-comments) | 10 | Yes | ## Substack [#substack] | Endpoint | Credits | Paginates | | ---------------------------------------------------------- | ------- | --------- | | [`/v1/substack/posts`](/docs/api-reference/substack/posts) | 10–120 | — | ## Telegram [#telegram] | Endpoint | Credits | Paginates | | -------------------------------------------------------------------------- | ------- | --------- | | [`/v1/telegram/profile`](/docs/api-reference/telegram/profile) | 10 | — | | [`/v1/telegram/profile/posts`](/docs/api-reference/telegram/profile-posts) | 10 | Yes | | [`/v1/telegram/post`](/docs/api-reference/telegram/post) | 10 | — | ## Threads [#threads] | Endpoint | Credits | Paginates | | ------------------------------------------------------------------------ | ------- | --------- | | [`/v1/threads/profile`](/docs/api-reference/threads/profile) | 10 | — | | [`/v1/threads/user/posts`](/docs/api-reference/threads/user-posts) | 10–1650 | — | | [`/v1/threads/post`](/docs/api-reference/threads/post) | 10 | — | | [`/v1/threads/search`](/docs/api-reference/threads/search) | 10–340 | Yes | | [`/v1/threads/post/comments`](/docs/api-reference/threads/post-comments) | 10–100 | — | | [`/v1/threads/search/users`](/docs/api-reference/threads/search-users) | 10–130 | — | ## TikTok [#tiktok] | Endpoint | Credits | Paginates | | -------------------------------------------------------------------------------------- | -------- | --------- | | [`/v1/tiktok/profile`](/docs/api-reference/tiktok/profile) | 10 | — | | [`/v1/tiktok/profile/videos`](/docs/api-reference/tiktok/profile-videos) | 10 | Yes | | [`/v1/tiktok/post`](/docs/api-reference/tiktok/post) | 10 | — | | [`/v1/tiktok/post/comments`](/docs/api-reference/tiktok/post-comments) | 10–30 | Yes | | [`/v1/tiktok/video/comment/replies`](/docs/api-reference/tiktok/video-comment-replies) | 10 | Yes | | [`/v1/tiktok/comment`](/docs/api-reference/tiktok/comment) | 20–60 | — | | [`/v1/tiktok/search`](/docs/api-reference/tiktok/search) | 10–370 | Yes | | [`/v1/tiktok/trending`](/docs/api-reference/tiktok/trending) | 50 | — | | [`/v1/tiktok/search/hashtag`](/docs/api-reference/tiktok/search-hashtag) | 10 | Yes | | [`/v1/tiktok/search/top`](/docs/api-reference/tiktok/search-top) | 10 | Yes | | [`/v1/tiktok/search/users`](/docs/api-reference/tiktok/search-users) | 10–1310 | Yes | | [`/v1/tiktok/similar`](/docs/api-reference/tiktok/similar) | 50 | — | | [`/v1/tiktok/user/audience`](/docs/api-reference/tiktok/user-audience) | 50 | — | | [`/v1/tiktok/user/followers`](/docs/api-reference/tiktok/user-followers) | 10 | Yes | | [`/v1/tiktok/user/following`](/docs/api-reference/tiktok/user-following) | 10 | Yes | | [`/v1/tiktok/user/live`](/docs/api-reference/tiktok/user-live) | 10 | — | | [`/v1/tiktok/post/transcript`](/docs/api-reference/tiktok/post-transcript) | 100 | — | | [`/v1/tiktok/video/screen-text`](/docs/api-reference/tiktok/video-screen-text) | 50 | — | | [`/v1/tiktok/song`](/docs/api-reference/tiktok/song) | 10 | — | | [`/v1/tiktok/song/videos`](/docs/api-reference/tiktok/song-videos) | 10 | Yes | | [`/v1/tiktok/hashtags/popular`](/docs/api-reference/tiktok/hashtags-popular) | 60–960 | — | | [`/v1/tiktok/videos/popular`](/docs/api-reference/tiktok/videos-popular) | 260–450 | — | | [`/v1/tiktok/profile/region`](/docs/api-reference/tiktok/profile-region) | 10 | — | | [`/v1/tiktok/adlibrary/search`](/docs/api-reference/tiktok/adlibrary-search) | 50–170 | Yes | | [`/v1/tiktok/adlibrary/ad`](/docs/api-reference/tiktok/adlibrary-ad) | 50 | — | | [`/v1/tiktok/search/suggestions`](/docs/api-reference/tiktok/search-suggestions) | 10 | — | | [`/v1/tiktok/collection/videos`](/docs/api-reference/tiktok/collection-videos) | 10 | Yes | | [`/v1/tiktok/profile/playlists`](/docs/api-reference/tiktok/profile-playlists) | 10 | — | | [`/v1/tiktok/playlist/videos`](/docs/api-reference/tiktok/playlist-videos) | 10 | Yes | | [`/v1/tiktok/user/liked`](/docs/api-reference/tiktok/user-liked) | 10 | Yes | | [`/v1/tiktok/location/posts`](/docs/api-reference/tiktok/location-posts) | 10 | Yes | | [`/v1/tiktok/effects`](/docs/api-reference/tiktok/effects) | 10 | — | | [`/v1/tiktok/effect/videos`](/docs/api-reference/tiktok/effect-videos) | 10 | Yes | | [`/v1/tiktok/search/music`](/docs/api-reference/tiktok/search-music) | 10 | Yes | | [`/v1/tiktok/hashtag`](/docs/api-reference/tiktok/hashtag) | 10 | — | | [`/v1/tiktok/ads/top`](/docs/api-reference/tiktok/ads-top) | 100–1000 | — | | [`/v1/tiktok/profile/full`](/docs/api-reference/tiktok/profile-full) | 50 | Yes | ## TikTok Shop [#tiktok-shop] | Endpoint | Credits | Paginates | | ---------------------------------------------------------------------------------- | ------- | --------- | | [`/v1/tiktokshop/product`](/docs/api-reference/tiktokshop/product) | 10 | — | | [`/v1/tiktokshop/product/reviews`](/docs/api-reference/tiktokshop/product-reviews) | 10 | Yes | | [`/v1/tiktokshop/products`](/docs/api-reference/tiktokshop/products) | 10 | Yes | | [`/v1/tiktokshop/search`](/docs/api-reference/tiktokshop/search) | 10 | Yes | | [`/v1/tiktokshop/user/showcase`](/docs/api-reference/tiktokshop/user-showcase) | 10 | Yes | | [`/v1/tiktokshop/sales`](/docs/api-reference/tiktokshop/sales) | 0–80 | — | ## Truth Social [#truth-social] | Endpoint | Credits | Paginates | | -------------------------------------------------------------------------- | ------- | --------- | | [`/v1/truthsocial/profile`](/docs/api-reference/truthsocial/profile) | 10 | — | | [`/v1/truthsocial/user/posts`](/docs/api-reference/truthsocial/user-posts) | 10 | Yes | | [`/v1/truthsocial/post`](/docs/api-reference/truthsocial/post) | 10 | — | ## Twitch [#twitch] | Endpoint | Credits | Paginates | | ---------------------------------------------------------------------- | ------- | --------- | | [`/v1/twitch/profile`](/docs/api-reference/twitch/profile) | 10 | — | | [`/v1/twitch/clip`](/docs/api-reference/twitch/clip) | 10 | — | | [`/v1/twitch/user/videos`](/docs/api-reference/twitch/user-videos) | 10 | — | | [`/v1/twitch/user/schedule`](/docs/api-reference/twitch/user-schedule) | 10 | — | ## Twitter/X [#twitterx] | Endpoint | Credits | Paginates | | ------------------------------------------------------------------------------ | ------- | --------- | | [`/v1/twitter/profile`](/docs/api-reference/twitter/profile) | 10 | — | | [`/v1/twitter/user/tweets`](/docs/api-reference/twitter/user-tweets) | 10 | Yes | | [`/v1/twitter/tweet`](/docs/api-reference/twitter/tweet) | 10 | — | | [`/v1/twitter/community`](/docs/api-reference/twitter/community) | 10 | — | | [`/v1/twitter/community/tweets`](/docs/api-reference/twitter/community-tweets) | 10 | — | | [`/v1/twitter/tweet/transcript`](/docs/api-reference/twitter/tweet-transcript) | 100 | — | | [`/v1/twitter/ai-search`](/docs/api-reference/twitter/ai-search) | 50 | — | | [`/v1/twitter/search/tweets`](/docs/api-reference/twitter/search-tweets) | 10–50 | Yes | | [`/v1/twitter/tweet/replies`](/docs/api-reference/twitter/tweet-replies) | 10 | Yes | | [`/v1/twitter/user/media`](/docs/api-reference/twitter/user-media) | 10 | Yes | | [`/v1/twitter/user/followers`](/docs/api-reference/twitter/user-followers) | 10 | Yes | | [`/v1/twitter/user/following`](/docs/api-reference/twitter/user-following) | 10 | Yes | | [`/v1/twitter/tweet/retweeters`](/docs/api-reference/twitter/tweet-retweeters) | 10 | Yes | | [`/v1/twitter/search/users`](/docs/api-reference/twitter/search-users) | 10 | Yes | | [`/v1/twitter/profile/full`](/docs/api-reference/twitter/profile-full) | 50 | Yes | ## Weibo [#weibo] | Endpoint | Credits | Paginates | | -------------------------------------------------------------------- | ------- | --------- | | [`/v1/weibo/post`](/docs/api-reference/weibo/post) | 10 | — | | [`/v1/weibo/profile`](/docs/api-reference/weibo/profile) | 10 | — | | [`/v1/weibo/user/posts`](/docs/api-reference/weibo/user-posts) | 10 | Yes | | [`/v1/weibo/post/comments`](/docs/api-reference/weibo/post-comments) | 10 | Yes | | [`/v1/weibo/search`](/docs/api-reference/weibo/search) | 10 | Yes | | [`/v1/weibo/trending`](/docs/api-reference/weibo/trending) | 10 | — | ## Xiaohongshu (RedNote) [#xiaohongshu-rednote] | Endpoint | Credits | Paginates | | -------------------------------------------------------------------------------- | ------- | --------- | | [`/v1/xiaohongshu/search`](/docs/api-reference/xiaohongshu/search) | 50–1000 | Yes | | [`/v1/xiaohongshu/trending`](/docs/api-reference/xiaohongshu/trending) | 50–1000 | — | | [`/v1/xiaohongshu/profile`](/docs/api-reference/xiaohongshu/profile) | 50 | — | | [`/v1/xiaohongshu/profile/posts`](/docs/api-reference/xiaohongshu/profile-posts) | 50–1000 | Yes | | [`/v1/xiaohongshu/post`](/docs/api-reference/xiaohongshu/post) | 50 | — | | [`/v1/xiaohongshu/post/comments`](/docs/api-reference/xiaohongshu/post-comments) | 50–1000 | Yes | ## YouTube [#youtube] | Endpoint | Credits | Paginates | | -------------------------------------------------------------------------------------------- | ------- | --------- | | [`/v1/youtube/channel`](/docs/api-reference/youtube/channel) | 10 | — | | [`/v1/youtube/channel/about`](/docs/api-reference/youtube/channel-about) | 250 | — | | [`/v1/youtube/channel/videos`](/docs/api-reference/youtube/channel-videos) | 10–20 | Yes | | [`/v1/youtube/video`](/docs/api-reference/youtube/video) | 10 | — | | [`/v1/youtube/video/sponsors`](/docs/api-reference/youtube/video-sponsors) | 100 | — | | [`/v1/youtube/video/comments`](/docs/api-reference/youtube/video-comments) | 10 | Yes | | [`/v1/youtube/video/comment/replies`](/docs/api-reference/youtube/video-comment-replies) | 10 | Yes | | [`/v1/youtube/search`](/docs/api-reference/youtube/search) | 10–150 | Yes | | [`/v1/youtube/channel/shorts`](/docs/api-reference/youtube/channel-shorts) | 10–20 | Yes | | [`/v1/youtube/community-post`](/docs/api-reference/youtube/community-post) | 10 | — | | [`/v1/youtube/playlist`](/docs/api-reference/youtube/playlist) | 10–110 | Yes | | [`/v1/youtube/search/hashtag`](/docs/api-reference/youtube/search-hashtag) | 10–110 | Yes | | [`/v1/youtube/shorts/trending`](/docs/api-reference/youtube/shorts-trending) | 50–150 | — | | [`/v1/youtube/video/transcript`](/docs/api-reference/youtube/video-transcript) | 30 | — | | [`/v1/youtube/channel/playlists`](/docs/api-reference/youtube/channel-playlists) | 10 | Yes | | [`/v1/youtube/channel/lives`](/docs/api-reference/youtube/channel-lives) | 10–70 | Yes | | [`/v1/youtube/channel/community-posts`](/docs/api-reference/youtube/channel-community-posts) | 10 | Yes | | [`/v1/youtube/videos/trending`](/docs/api-reference/youtube/videos-trending) | 10–60 | Yes | | [`/v1/youtube/playlist/items`](/docs/api-reference/youtube/playlist-items) | 10–110 | Yes | | [`/v1/youtube/search/advanced`](/docs/api-reference/youtube/search-advanced) | 10–110 | Yes | | [`/v1/youtube/search/suggestions`](/docs/api-reference/youtube/search-suggestions) | 10 | — | | [`/v1/youtube/video/audio`](/docs/api-reference/youtube/video-audio) | 50 | — | | [`/v1/youtube/video/files`](/docs/api-reference/youtube/video-files) | 50 | — | | [`/v1/youtube/video/subtitles`](/docs/api-reference/youtube/video-subtitles) | 10 | — | | [`/v1/youtube/video/thumbnails`](/docs/api-reference/youtube/video-thumbnails) | 10 | — | | [`/v1/youtube/profile/full`](/docs/api-reference/youtube/profile-full) | 50 | Yes | ## Zhihu [#zhihu] | Endpoint | Credits | Paginates | | -------------------------------------------------------------------------- | ------- | --------- | | [`/v1/zhihu/answer`](/docs/api-reference/zhihu/answer) | 10 | — | | [`/v1/zhihu/question`](/docs/api-reference/zhihu/question) | 10 | — | | [`/v1/zhihu/question/answers`](/docs/api-reference/zhihu/question-answers) | 10 | — | | [`/v1/zhihu/profile`](/docs/api-reference/zhihu/profile) | 10 | — | | [`/v1/zhihu/search/articles`](/docs/api-reference/zhihu/search-articles) | 10 | Yes | --- # Errors (/docs/errors) Every InsightSocial API error type with its status, what to do about it, and copy-paste retry loops. Every failure returns the same error body, not the success envelope: `success: false`, an `error` object with a `type` to branch on, a readable `message` and, when one parameter is at fault, `param`, plus the usual `request_id` and credit fields. A failed call is never charged. ```json title="Response" { "success": false, "error": { "type": "INSUFFICIENT_CREDITS", "message": "This call needs up to 170 credits and 120 remain. Top up at https://www.insightsocial.app/pricing" }, "request_id": "req_1a2b3c4d5e6f", "credits_used": 0, "credits_remaining": 120 } ``` * **`error.type`** is what your code should branch on. * **`error.param`** names the query parameter at fault, when there is one (for example `cursor` on a cursor error). It is absent otherwise. * **`error.message`** explains the failure in a sentence and often names what to change. Show it or log it, but do not parse it. * **`credits_used`** is `0` on every error. * **`credits_remaining`** is your balance when we know it, and `null` when we do not (authentication failures, for example). * **`request_id`** matches the `X-Request-Id` header. Include it when you contact support. That is the whole error body. It carries no `schema_version`, `data` or `unavailable`, so check `success` before reading anything else. Refusals made before a call reaches a platform (`UNSUPPORTED_PARAMETER`, `CURSOR_INVALID`, `CURSOR_EXPIRED`, authentication, rate-limit and idempotency errors, `INSUFFICIENT_CREDITS`) do not appear in the dashboard's Activity Log. Other errors, including the `400` parameter errors a platform returns, do. ## Error types [#error-types] | `error.type` | Status | Retry? | What it means | | ------------------------------------------------------------ | -------------------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | | `MISSING_API_KEY` | 401 | No | No `x-api-key` header on the request | | `INVALID_API_KEY` | 401 | No | The value is not an InsightSocial key, or no key matches it | | `API_KEY_REVOKED` | 401 | No | The key has been revoked | | `INSUFFICIENT_CREDITS` | 402 | No | Your balance is below what this call needs to hold | | `UNKNOWN_PLATFORM` | 404 | No | The first path segment is not one of the platforms we serve | | `UNKNOWN_ENDPOINT` | 404 | No | The platform exists but that path does not | | `RESOURCE_NOT_FOUND` | 404 | No | Nothing exists at the identifier you sent | | `METHOD_NOT_SUPPORTED` | 405 | No | Every endpoint is a GET: any other method, and the three POST endpoints in the catalogue, answer 405 | | `UNSUPPORTED_PARAMETER` | 400 | No | A parameter this endpoint does not have, or one that asks for analysis schema 2 does not carry. `error.param` names it | | `CURSOR_INVALID` | 400 | No | The cursor was not issued by this API, was edited, or belongs to a different request | | `CURSOR_EXPIRED` | 400 | No | The cursor is more than 24 hours old | | `IDEMPOTENCY_IN_PROGRESS` | 409 | Yes, after `Retry-After` | A call with the same `Idempotency-Key` is still running | | `IDEMPOTENCY_KEY_REUSED` | 409 | No | The `Idempotency-Key` was already used for a different request | | `IDEMPOTENCY_REPLAY_UNAVAILABLE` | 409 | No | The original call succeeded, but its response is too large to replay | | `RATE_LIMITED` | 429 | Yes, after `Retry-After` | More than 60 requests in 60 seconds on this key | | `CONCURRENCY_LIMIT` | 429 | Yes, after `Retry-After` | More than 10 requests in flight on this key | | `CLIENT_CLOSED_REQUEST` | 499 | Your call | Your client disconnected before the response | | `INTERNAL_ERROR` | 500 | Yes, with backoff | Something went wrong on our side | | `SERVICE_UNAVAILABLE` | 503 | Yes, after `Retry-After` or backoff | We could not complete the call right now | | `UPSTREAM_ERROR` (or the platform's own type) | 503 | Yes, after `Retry-After` | The platform did not answer in time | | `UPSTREAM_INVALID` | 503 | Yes, after `Retry-After` | The platform answered with something we could not read | | `ENDPOINT_UNAVAILABLE` | 503 | Not soon: `Retry-After` is an hour | This endpoint is switched off while its data source is broken | | Parameter errors, such as `INVALID_REQUEST` or `BAD_REQUEST` | 400 (or another 4xx) | No | A parameter is missing, invalid, or not accepted by this endpoint | When a request is rejected because of its parameters, the `error.type` is not drawn from a fixed list: you will usually see `INVALID_REQUEST` or `BAD_REQUEST`, but other values occur. Treat any 4xx you do not recognise as "fix the request", and read `error.message`, which says what was wrong. ## Which errors to retry [#which-errors-to-retry] Retry only `RATE_LIMITED`, `CONCURRENCY_LIMIT`, `IDEMPOTENCY_IN_PROGRESS`, `SERVICE_UNAVAILABLE`, `UPSTREAM_ERROR`, `UPSTREAM_INVALID` and `INTERNAL_ERROR`, plus any `429`, `500` or `503` whatever its `error.type`: when a platform fails, the 503 can carry that platform's own error type. A `409` is retryable only when its type is `IDEMPOTENCY_IN_PROGRESS`. Everything else is deterministic: the same request fails the same way until you change the request, the key or your balance. When a response carries `Retry-After`, wait at least that many seconds. Otherwise back off exponentially with jitter, and give up after a few attempts. ## Error reference [#error-reference] ### MISSING\_API\_KEY [#missing_api_key] **401.** No `x-api-key` header reached the API. Send the header, and if you already do, make sure nothing between you and the API, such as a proxy or gateway, removes it. A key sent in `Authorization` is not read and also produces this error. See [Authentication](/docs/authentication). ### INVALID\_API\_KEY [#invalid_api_key] **401.** Either the value does not look like an InsightSocial key (keys start `isk_live_` or `isk_test_`), or it looks right but matches no key. Copy it again from [API keys](https://www.insightsocial.app/portal/api/keys). ### API\_KEY\_REVOKED [#api_key_revoked] **401.** The key was revoked in the dashboard. Create a new one in the dashboard. ### INSUFFICIENT\_CREDITS [#insufficient_credits] **402.** Before a call runs, we hold the most it can cost. If your balance is below that amount, the call is refused and nothing is charged. `credits_remaining` shows your balance, and the message states both numbers. For a metered endpoint, the amount held is the most the call can cost with the parameters you sent, and on endpoints that accept `max_pages` it is multiplied by the number of pages. You can therefore get a 402 on a call that would have cost less in the end. A smaller `limit`, fewer `include` joins or fewer pages hold less; `dry_run=1` shows the hold for free. Top up on the [pricing page](https://www.insightsocial.app/pricing); retrying without a top-up fails the same way. ### UNKNOWN\_PLATFORM [#unknown_platform] **404.** The platform segment of the path is not one we serve. The 25 platforms are `instagram`, `tiktok`, `facebook`, `linkedin`, `twitter`, `threads`, `youtube`, `reddit`, `pinterest`, `bluesky`, `truthsocial`, `snapchat`, `telegram`, `twitch`, `kick`, `rumble`, `kwai`, `douyin`, `xiaohongshu`, `quora`, `tiktokshop`, `weibo`, `zhihu`, `substack` and `nextdoor`. ### UNKNOWN\_ENDPOINT [#unknown_endpoint] **404.** The path is not in the catalogue. Check it against `GET /v1/endpoints` or the [API reference](/docs/api-reference). ### RESOURCE\_NOT\_FOUND [#resource_not_found] **404.** The endpoint exists, but nothing is there for the identifier you sent: a handle that does not exist, a deleted post, and so on. You are not charged. Retrying the same identifier returns the same answer, so record it and move on. ### INVALID\_REQUEST for a missing parameter [#invalid_request-for-a-missing-parameter] A required parameter left out is refused before anything is fetched: `400 INVALID_REQUEST`, with `error.param` naming it and a message such as "`url` is required on this endpoint". Nothing is charged. ### METHOD\_NOT\_SUPPORTED [#method_not_supported] **405.** This path is in the catalogue but cannot be called through the API. Pick another endpoint. ### UNSUPPORTED\_PARAMETER [#unsupported_parameter] **400.** You sent a parameter this endpoint does not have (a typo, or one that belongs to another endpoint), or one that asks for analysis: labels, relevance scoring, fit or brand judgments. Both are refused instead of being ignored, which would return unfiltered rows and still charge for them. `error.param` names the parameter; the endpoint's parameters are in `GET /v1/endpoints`. Nothing is charged. ```json title="Response" { "success": false, "error": { "type": "UNSUPPORTED_PARAMETER", "message": "`relevance` is not supported: it asks for analysis, which is not part of schema 2. Remove it and retry. Nothing was charged.", "param": "relevance" }, "request_id": "req_80d197121a1e", "credits_used": 0, "credits_remaining": null } ``` The refused parameters are `label`, `label_evidence`, `relevance`, `relevance_threshold`, `relevant_to`, `judgments`, `fit`, `goal`, `fit_tokens`, `seen`, `brand`, `brand_description`, `offer`, `exclude` (except on `/v1/pinterest/trends`, where it filters terms), `brief`, `use`, `moments`, and the value `format=stationery`. Two more are refused on specific endpoints only, where they ask for analysis; elsewhere they are ordinary parameters: | Parameter | Refused on | What it asked for | | ------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ | | `q` | `/v1/tiktok/post/transcript`, `/v1/youtube/video/transcript` | A question answered about the video's transcript. On search endpoints `q` is the search query and works as before. | | `switch_from` | `/v1/twitter/search/tweets`, `/v1/reddit/search/comments` | Posts about switching away from a named brand. | Only the first refused parameter in your query is reported, so remove it and check the next. To filter rows by topic or language, do it on your side after the call; see [Schema 2](/docs/schema-2). ### CURSOR\_INVALID [#cursor_invalid] **400.** The cursor cannot be used for this request. Either it was not issued by this API (a cursor from the previous schema starts `is.` or `is2.`), it was edited or truncated, it was sent under a name other than `cursor`, or it belongs to a different request: another endpoint, or the same endpoint with different parameters. `error.param` names the parameter that carried it. Nothing is charged. Send `pagination.next_cursor` back unchanged, as `cursor`, with the same parameters as the first page. Only `limit`, `max_pages`, `dry_run` and `fresh` may change between pages. Otherwise start again without a cursor. See [Pagination](/docs/pagination#bad-or-stale-cursors). ### CURSOR\_EXPIRED [#cursor_expired] **400.** The cursor is more than 24 hours old. Start the walk again without a cursor. Nothing is charged. ### IDEMPOTENCY\_IN\_PROGRESS [#idempotency_in_progress] **409.** An earlier call with the same `Idempotency-Key` has not finished. Wait for `Retry-After`, then send the same request with the same key. Once the original has finished, you get its result as a free replay, unless the original never completed (see below). ### IDEMPOTENCY\_KEY\_REUSED [#idempotency_key_reused] **409.** The `Idempotency-Key` was already used for a different request: another endpoint or other parameters. Use a new key. Nothing is charged. ### IDEMPOTENCY\_REPLAY\_UNAVAILABLE [#idempotency_replay_unavailable] **409.** The original call with this `Idempotency-Key` succeeded, but its response is too large for us to replay. Send the request again with a new key to fetch it. Nothing is charged. ### RATE\_LIMITED [#rate_limited] **429.** This key sent more than 60 requests in a sliding 60-second window. `Retry-After` is the number of seconds until the oldest request leaves the window. Nothing is charged. See [Rate limits](/docs/rate-limits). ### CONCURRENCY\_LIMIT [#concurrency_limit] **429.** This key already had 10 requests in flight. `Retry-After` is `1`, since a slot frees as soon as any open request returns. Shrink your worker pool. ### CLIENT\_CLOSED\_REQUEST [#client_closed_request] **499.** Your client closed the connection before we answered. You only see this in logs, since nobody was listening for the response. Give slow endpoints a generous client timeout. ### INTERNAL\_ERROR [#internal_error] **500.** An unexpected error on our side. Nothing was charged. Retry with backoff, and if it keeps happening, contact support with the `request_id`. ### SERVICE\_UNAVAILABLE [#service_unavailable] **503.** We could not complete the call right now. Causes include being unable to verify your key or read your balance for a moment, or temporarily running out of capacity. Nothing was charged. When `Retry-After` is present (often `30`), wait that long; otherwise retry with backoff. ### UPSTREAM\_ERROR [#upstream_error] **503.** The platform did not answer, even after our own retries and a second data source where one exists. Nothing was charged. `Retry-After` is at least `30`, longer when the platform asked for more. ### UPSTREAM\_INVALID [#upstream_invalid] **503.** The platform answered, but with something that does not fit the response schema, so we did not serve it. A page whose rows all lack an id is also refused this way, rather than charged as an empty page. Nothing was charged. `Retry-After` is `30`. If it repeats on the same input, contact support with the `request_id`. ### ENDPOINT\_UNAVAILABLE [#endpoint_unavailable] **503.** The endpoint exists but is switched off, because its data source stopped returning data for every input. It is left out of `GET /v1/endpoints` and the API reference until it works again. Nothing was charged. `Retry-After` is `3600`: retrying sooner gets the same answer, so a generic retry loop should honour it or stop. We check these endpoints daily and put them back as soon as they answer. ### Parameter errors [#parameter-errors] **400**, or another 4xx. A required parameter is missing, a value is in the wrong format, or the endpoint does not accept a parameter you sent. The `message` says what was rejected and usually names the parameter to fix. Check the endpoint's parameters on its [API reference](/docs/api-reference) page or in `GET /v1/endpoints`. Nothing is charged. ## Idempotency [#idempotency] Send an `Idempotency-Key` header, a fresh UUID per logical operation, to make any call safe to retry after a timeout or a dropped connection. * A retry with the same key returns the original result with `idempotent_replay: true`, `charge_reason: "replay"` and `credits_used: 0`. * A retry that arrives while the original is still running gets `409 IDEMPOTENCY_IN_PROGRESS`. Wait for `Retry-After` and try again with the same key. * A key reused for a different request, or across schemas, gets `409 IDEMPOTENCY_KEY_REUSED`. Use a new key. * Keys are scoped to your account. * A key longer than 255 characters, or with characters other than letters, digits, `.`, `_`, `:` and `-`, is ignored. The call then runs normally, with no replay protection. * If we never answered the original successfully (for example your client disconnected before it finished), the retry that delivers the data is charged as a normal call (`charge_reason: "miss"`) at exactly the ceiling the call reserved: the listed price, or the top of the range for a metered endpoint, multiplied by `max_pages` if you sent it. Like any charged call, it can be covered by one of your free calls. It costs 0 if the replay returns nothing. ## What is never charged [#what-is-never-charged] Every error on this page costs 0 credits, including a request refused for an unsupported parameter or a bad cursor. So do empty results, `dry_run=1` calls and replays of a call we already answered. API credits are non-refundable, and a call that fails is simply never charged. See [Credits](/docs/credits). ## Retry loops [#retry-loops] Each loop retries only the transient statuses (429, 500, 503, and 409 when the type is `IDEMPOTENCY_IN_PROGRESS`), honors `Retry-After` when it is sent, otherwise backs off exponentially with jitter, and stops after five retries. ```bash title="cURL" #!/usr/bin/env bash url="https://api.insightsocial.app/v1/tiktok/profile?handle=khaby.lame" max_retries=5 attempt=0 while :; do body=$(mktemp); headers=$(mktemp) status=$(curl -s -o "$body" -D "$headers" -w '%{http_code}' \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" "$url") if [ "$status" -lt 400 ]; then cat "$body"; rm -f "$body" "$headers"; break fi retry_409=false if [ "$status" = "409" ] && grep -q '"IDEMPOTENCY_IN_PROGRESS"' "$body"; then retry_409=true; fi case "$status" in 429|500|503) ;; 409) $retry_409 || { echo "Not retryable: 409"; cat "$body"; rm -f "$body" "$headers"; exit 1; } ;; *) echo "Not retryable: $status"; cat "$body"; rm -f "$body" "$headers"; exit 1 ;; esac attempt=$((attempt + 1)) if [ "$attempt" -gt "$max_retries" ]; then echo "Giving up after $max_retries retries (last status $status)" rm -f "$body" "$headers"; exit 1 fi wait_s=$(grep -i '^retry-after:' "$headers" | tr -d '\r' | awk '{print $2}') if [ -z "$wait_s" ]; then wait_s=$(awk "BEGIN{srand(); print (2 ^ $attempt) * (0.5 + rand() / 2)}") fi rm -f "$body" "$headers" sleep "$wait_s" done ``` ```python title="Python" import os import random import time import uuid import requests BASE = "https://api.insightsocial.app/v1" RETRYABLE = {429, 500, 503} def retryable(res): if res.status_code == 409: return res.json()["error"]["type"] == "IDEMPOTENCY_IN_PROGRESS" return res.status_code in RETRYABLE def get_with_retry(path, params=None, max_retries=5): headers = { "x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"], # One key for every attempt, so a retry never pays twice. "Idempotency-Key": str(uuid.uuid4()), } for attempt in range(max_retries + 1): res = requests.get(f"{BASE}{path}", params=params, headers=headers, timeout=120) if not retryable(res) or attempt == max_retries: return res.json() retry_after = res.headers.get("Retry-After") delay = float(retry_after) if retry_after else (2 ** attempt) * (0.5 + random.random() / 2) time.sleep(delay) body = get_with_retry("/tiktok/profile", {"handle": "khaby.lame"}) print(body["success"], body.get("error")) ``` ```ts title="Node.js" const BASE = "https://api.insightsocial.app/v1"; const RETRYABLE = new Set([429, 500, 503]); const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); async function getWithRetry(path: string, params: Record = {}, maxRetries = 5) { const url = new URL(BASE + path); for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v); const headers = { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY!, // One key for every attempt, so a retry never pays twice. "Idempotency-Key": crypto.randomUUID(), }; for (let attempt = 0; ; attempt++) { const res = await fetch(url, { headers }); const body = await res.json(); const retryable = RETRYABLE.has(res.status) || (res.status === 409 && body.error?.type === "IDEMPOTENCY_IN_PROGRESS"); if (!retryable || attempt >= maxRetries) return body; const retryAfter = Number(res.headers.get("Retry-After")); const delayMs = retryAfter > 0 ? retryAfter * 1000 : 2 ** attempt * 1000 * (0.5 + Math.random() / 2); await sleep(delayMs); } } const body = await getWithRetry("/tiktok/profile", { handle: "khaby.lame" }); console.log(body.success, body.error); ``` ## Debugging tips [#debugging-tips] * Log `request_id` (also sent as `X-Request-Id`) with every failure, so support can trace the exact call. * A `503` on an input that worked before is usually temporary. Retry after `Retry-After`. * A `400` that names `error.param` tells you exactly which parameter to remove or fix. * Some calls take a while to answer. Set a client timeout of a minute or more on heavy endpoints rather than aborting early. ## Next steps [#next-steps] The two 429s and how to size a job. What calls cost and what is never charged. The envelope and every response header. --- # Introduction (/docs) Public data from 25 social platforms through one API key, one JSON envelope and one credit balance. InsightSocial API gives you public data from 25 social platforms through more than 300 GET endpoints. You authenticate every call with the same `x-api-key` header, every answer arrives in the same JSON envelope, and every call draws on one credit balance, the same one you already use for InsightSocial exports. ## Your first call [#your-first-call] ```bash title="cURL" curl "https://api.insightsocial.app/v1/tiktok/profile?handle=khaby.lame" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` That is the whole protocol: one GET request with one header. The profile call costs 10 credits, and the response tells you what you were charged and what is left, so you never need a separate balance check to keep track. Sign up and your key is ready on the next screen. The first 10 calls are free. Run any endpoint from your browser before you write code. ## One request shape, 25 platforms [#one-request-shape-25-platforms] Switch platforms by changing one path segment. The header stays the same and the envelope stays the same. ```bash title="cURL" curl "https://api.insightsocial.app/v1/tiktok/profile?handle=nasa" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" curl "https://api.insightsocial.app/v1/instagram/profile?handle=nasa" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" curl "https://api.insightsocial.app/v1/youtube/channel?handle=nasa" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" curl "https://api.insightsocial.app/v1/twitter/profile?handle=nasa" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` The platforms differ in what they expose, so field coverage differs too, but the answer has one shape everywhere: a profile is a profile, a post is a post, whichever platform it came from. A field a platform does not show is `null`. See [Schema 2](/docs/schema-2). | Platform | Base path | Overview | | --------------------- | --------------------- | ---------------------------------------------------- | | Instagram | `/v1/instagram/...` | [Instagram](/docs/platforms/instagram) | | TikTok | `/v1/tiktok/...` | [TikTok](/docs/platforms/tiktok) | | Facebook | `/v1/facebook/...` | [Facebook](/docs/platforms/facebook) | | LinkedIn | `/v1/linkedin/...` | [LinkedIn](/docs/platforms/linkedin) | | Twitter/X | `/v1/twitter/...` | [Twitter/X](/docs/platforms/twitter) | | Threads | `/v1/threads/...` | [Threads](/docs/platforms/threads) | | YouTube | `/v1/youtube/...` | [YouTube](/docs/platforms/youtube) | | Reddit | `/v1/reddit/...` | [Reddit](/docs/platforms/reddit) | | Pinterest | `/v1/pinterest/...` | [Pinterest](/docs/platforms/pinterest) | | Bluesky | `/v1/bluesky/...` | [Bluesky](/docs/platforms/bluesky) | | Truth Social | `/v1/truthsocial/...` | [Truth Social](/docs/platforms/truthsocial) | | Snapchat | `/v1/snapchat/...` | [Snapchat](/docs/platforms/snapchat) | | Telegram | `/v1/telegram/...` | [Telegram](/docs/platforms/telegram) | | Twitch | `/v1/twitch/...` | [Twitch](/docs/platforms/twitch) | | Kick | `/v1/kick/...` | [Kick](/docs/platforms/kick) | | Rumble | `/v1/rumble/...` | [Rumble](/docs/platforms/rumble) | | Kwai | `/v1/kwai/...` | [Kwai](/docs/platforms/kwai) | | Douyin | `/v1/douyin/...` | [Douyin](/docs/platforms/douyin) | | Xiaohongshu (RedNote) | `/v1/xiaohongshu/...` | [Xiaohongshu (RedNote)](/docs/platforms/xiaohongshu) | | Quora | `/v1/quora/...` | [Quora](/docs/platforms/quora) | | TikTok Shop | `/v1/tiktokshop/...` | [TikTok Shop](/docs/platforms/tiktokshop) | | Weibo | `/v1/weibo/...` | [Weibo](/docs/platforms/weibo) | | Zhihu | `/v1/zhihu/...` | [Zhihu](/docs/platforms/zhihu) | | Substack | `/v1/substack/...` | [Substack](/docs/platforms/substack) | | Nextdoor | `/v1/nextdoor/...` | [Nextdoor](/docs/platforms/nextdoor) | ## The response envelope [#the-response-envelope] Every successful call to a data endpoint returns a body with this top level, and the platform payload sits under `data`. A failed call returns a smaller error body instead: `success: false`, an `error` object with `type`, `message` and sometimes `param`, plus `request_id`, `credits_used` and `credits_remaining`, and no `schema_version`, `data` or `unavailable`; see [Errors](/docs/errors). `GET /v1/credits` and `GET /v1/endpoints` return bodies of their own; see [Response schema](/docs/response-schema). ```json title="Response" { "success": true, "platform": "instagram", "endpoint": "/v1/instagram/profile", "schema_version": "2", "data": { "author": { "id": "528817151", "username": "nasa", "display_name": "NASA", "followers": 104294818, "url": "https://www.instagram.com/nasa/", "external_url": "https://www.nasa.gov" } }, "unavailable": [], "credits_used": 10, "credits_remaining": 490, "request_id": "req_1a2b3c4d5e6f", "cached": false, "idempotent_replay": false, "charge_reason": "miss", "free_call": false } ``` | Field | Use it to | | ------------------- | --------------------------------------------------------------------------------------------------------------- | | `success` | Branch first. When it is `false`, the body is an error body: read `error.type`. | | `schema_version` | Check you are reading the shape these docs describe: `"2"`. | | `data` | Read the platform payload. | | `unavailable` | See which fields this platform normally fills but this response could not, as paths into `data`. Usually empty. | | `pagination` | Page through list endpoints. Present only on lists. | | `credits_used` | See what this call cost. | | `credits_remaining` | See your balance after this call. | | `charge_reason` | See why you were charged what you were. | | `request_id` | Quote it when you contact support. | List endpoints add a top-level `pagination` object. Send `pagination.next_cursor` back unchanged as `cursor` until `has_more` is `false`. See [Pagination](/docs/pagination) and [Response schema](/docs/response-schema). ## Browse the catalogue for free [#browse-the-catalogue-for-free] `GET /v1/endpoints` lists every endpoint with its price, parameters and whether it paginates. It needs no key and costs nothing. `GET /v1/credits` returns your balance, also free. ```bash title="cURL" # Every Instagram endpoint, with prices and parameters curl "https://api.insightsocial.app/v1/endpoints?platform=instagram" # Your balance curl "https://api.insightsocial.app/v1/credits" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## What calls cost [#what-calls-cost] Each endpoint has its own price in credits, listed in [Endpoint pricing](/docs/endpoint-pricing) and on every [API reference](/docs/api-reference) page. Some endpoints are metered: you see a range, we hold the top of the range while the call runs, and you pay what the call actually used. * The free plan includes 500 credits a month and Pro includes 10,000. Credit packs are on the [pricing page](https://www.insightsocial.app/pricing). * Your first 10 calls priced at 100 credits or less are free. * Failed calls, empty results and `dry_run` calls are never charged. * Repeating a call is charged again. Only an `Idempotency-Key` replay is free. * API credits are non-refundable. See [Credits](/docs/credits) for the full rules. Every key is limited to 60 requests a minute and 10 in flight; see [Rate limits](/docs/rate-limits). ## When a call fails [#when-a-call-fails] Failures use the same envelope with `success: false`. Branch on `error.type`, not on the message text. | `error.type` | HTTP | What to do | | ----------------------- | ---- | ----------------------------------------------------------------- | | `MISSING_API_KEY` | 401 | Send your key in the `x-api-key` header. | | `INVALID_API_KEY` | 401 | Copy the key again from the dashboard. | | `INSUFFICIENT_CREDITS` | 402 | Top up, or pick a cheaper endpoint. | | `UNSUPPORTED_PARAMETER` | 400 | Remove the parameter named in `error.param`. Nothing was charged. | | `UNKNOWN_ENDPOINT` | 404 | Check the path against the catalogue. | | `RATE_LIMITED` | 429 | Wait for `Retry-After`, then slow down. | | `SERVICE_UNAVAILABLE` | 503 | Retry after `Retry-After` or a short backoff. | The complete list, with retry advice, is in [Errors](/docs/errors). ## Next steps [#next-steps] Key, first call, a multi-platform loop and your first paginated list. Creating, sending, rotating and revoking keys. Prices, metered endpoints, free calls and the shared balance. What each of the 25 platforms offers. Every endpoint with parameters and prices. --- # Labels (/docs/labels) Labels, relevance scores and their parameters were removed in schema 2. What those parameters return now, and how to filter results yourself. Comment and post lists used to carry model judgments: `labels`, `relevance`, `label_share` and `labels_evidence`, with parameters to tune them. Schema 2, which every key gets from October 4, 2026, removed them on every endpoint. Responses now carry the platform's data only. What changed is on [Schema 2](/docs/schema-2). ## The parameters now return 400 [#the-parameters-now-return-400] These parameters asked for analysis. Under schema 2 they answer `400 UNSUPPORTED_PARAMETER`, with `error.param` naming the parameter, and nothing is charged: `label`, `label_evidence`, `relevance`, `relevance_threshold`, `relevant_to`, `judgments`, `fit`, `goal`, `fit_tokens`, `seen`, `brand`, `brand_description`, `offer`, `exclude` (except on `/v1/pinterest/trends`, where it filters terms), `brief`, `use`, `moments`, and `format=stationery`. Two more are refused on specific endpoints only, where they ask for analysis; elsewhere they are ordinary parameters: | Parameter | Refused on | What it asked for | | ------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ | | `q` | `/v1/tiktok/post/transcript`, `/v1/youtube/video/transcript` | A question answered about the video's transcript. On search endpoints `q` is the search query and works as before. | | `switch_from` | `/v1/twitter/search/tweets`, `/v1/reddit/search/comments` | Posts about switching away from a named brand. | ```json title="Response" { "success": false, "error": { "type": "UNSUPPORTED_PARAMETER", "message": "`relevance` is not supported: it asks for analysis, which is not part of schema 2. Remove it and retry. Nothing was charged.", "param": "relevance" }, "request_id": "req_80d197121a1e", "credits_used": 0, "credits_remaining": null } ``` Remove the parameter and send the call again. ## Filtering results yourself [#filtering-results-yourself] A search returns what the platform returns for your query. To keep only the rows about your topic, filter on the text in your own code. A keyword filter covers most cases: ```python title="filter.py" KEYWORDS = ["insightsocial", "insight social"] def mentions(text): t = (text or "").lower() return any(k in t for k in KEYWORDS) posts = [item["post"] for item in body["data"]["items"]] on_topic = [p for p in posts if mentions(p["content"]["text"])] ``` For tone, topic or paid-promotion checks, run your own classifier or model on `post.content.text` or `comment.text`. To keep one language, filter on `post.language` or `comment.language`, the platform's own tag. A full monitoring example is in [Brand mention monitoring](/docs/recipes/brand-mention-monitoring). --- # MCP server, CLI and agent skills (/docs/mcp) Connect Claude Code, Cursor, Codex, Gemini CLI or any MCP client to the InsightSocial API. A hosted MCP server at one URL, plus the open-source local MCP server, CLI and agent skill. There are two MCP servers with the same tools: a hosted one at `https://api.insightsocial.app/mcp`, which needs nothing installed, and a local one in the official `insightsocial` npm package, which is also a CLI. The agent skill teaches your agent how to use either. The package and the skill are open source under the [InsightSocial GitHub organization](https://github.com/insightsocial). You do not need any of them to use the API, which is plain HTTPS (see [Using an AI agent](/docs/ai-agents)), but they save your agent from writing its own HTTP client and keep large responses out of its context. ## Hosted MCP server [#hosted-mcp-server] In Claude (desktop, claude.ai, Cowork), ChatGPT or any client that supports sign-in, add a connector with the URL `https://api.insightsocial.app/mcp`, click Connect, sign in to InsightSocial and click Allow. The app gets its own key, listed under Connected apps on [API keys](https://www.insightsocial.app/portal/api/keys), where you can disconnect it. Step-by-step for each app is on [Connect](https://www.insightsocial.app/portal/api/connect). A client that cannot sign in can send your API key as a Bearer token instead: ```json title="mcpServers config" { "mcpServers": { "insightsocial": { "url": "https://api.insightsocial.app/mcp", "headers": { "Authorization": "Bearer $INSIGHTSOCIAL_API_KEY" } } } } ``` ```bash title="Claude Code" claude mcp add --transport http insightsocial https://api.insightsocial.app/mcp --header "Authorization: Bearer $INSIGHTSOCIAL_API_KEY" ``` It has the tools listed under [MCP server](#mcp-server) below, with three differences: results are kept on our side for 3 days instead of on your disk, `read_result` reshapes them with fields, max items or an outline but not jq, and there are two more tools, `quote_endpoint` (the exact price of a call before you make it, free) and `send_feedback` (tell us about a missing endpoint or field). `call_endpoint` is marked as a tool that spends, so clients ask before running it. Get a key at [insightsocial.app/portal/api/keys](https://www.insightsocial.app/portal/api/keys). ## One command [#one-command] ```bash title="Terminal" npx -y insightsocial init ``` `init` signs you in through your browser and saves a key for this machine to `~/.insightsocial/config.json`, installs the agent skill into the agents it finds, and writes or prints the MCP config. Signing in opens a sign-in page and prints a short code: type the code on the page and click Allow. The CLI then prints which account it signed into. Nothing to copy or paste. To use a key you already have, run `npx -y insightsocial login --api-key isk_live_…`. New accounts include 10 free calls. ## MCP server [#mcp-server] The local server runs over stdio and reads the key that `insightsocial login` (or `init`) saved, so your MCP config holds no secret. ```bash title="Claude Code" claude mcp add insightsocial --scope user -- npx -y insightsocial mcp ``` ```json title="Cursor, Windsurf, Claude Desktop and other clients" { "mcpServers": { "insightsocial": { "command": "npx", "args": ["-y", "insightsocial", "mcp"] } } } ``` You can pass `INSIGHTSOCIAL_API_KEY` in the server's `env` instead of saving it. Without a key, searching and describing endpoints still work. The server is listed in the official MCP Registry as `app.insightsocial/insightsocial`. | Tool | What it does | Cost | | ------------------- | -------------------------------------------------------------------------------------------------------------------- | -------------------- | | `search_endpoints` | Find endpoints by keywords and platform | Free | | `describe_endpoint` | Inputs, allowed values, an example and the price for one endpoint | Free | | `call_endpoint` | Fetch data. Saves the full response and returns a `result_id` with a trimmed view and a ready call for the next page | The endpoint's price | | `read_result` | Re-slice a saved result with jq, fields, max items or an outline with sizes | Free | | `get_credits` | Balance, plan and usage | Free | Because every result is saved in full and re-read with `read_result`, a 50 KB page of comments never has to sit in the model's context, and reading it again costs nothing. ## Agent skill [#agent-skill] The skill teaches an agent the workflow: search, price, call, page. It also covers when a call is free and when to stop and ask before spending. It uses the MCP server when it is connected and falls back to the CLI or plain REST when it is not. ```bash title="Any agent that supports Agent Skills" npx skills add insightsocial/skills ``` ```bash title="Claude Code plugin (skill and MCP server together)" claude plugin marketplace add insightsocial/skills claude plugin install insightsocial@insightsocial ``` ```bash title="Gemini CLI" gemini extensions install https://github.com/insightsocial/skills ``` ## CLI [#cli] The same package works from a terminal or a script. Results are saved under `./.insightsocial/`, so you can reshape them later without paying again. ```bash title="Terminal" npx -y insightsocial search instagram followers npx -y insightsocial describe /v1/instagram/profile npx -y insightsocial run /v1/instagram/profile -p handle=natgeo npx -y insightsocial view --last --jq '.data.author | {username, followers}' npx -y insightsocial credits ``` `search`, `list`, `describe`, `view` and `credits` are free. `run` costs the endpoint's price and prints the exact command for the next page when there is one. Each page is a separate charged call. ## OpenAPI spec [#openapi-spec] To generate a client or your own agent tools, use the OpenAPI 3.1 spec. It is live and keyless at `https://api.insightsocial.app/v1/openapi.json`, and a copy synced daily from the live API is at [github.com/insightsocial/openapi](https://github.com/insightsocial/openapi). ## Next steps [#next-steps] The community node and two ready-made workflows. One paste-in prompt, no install. What each call costs and what is free. How cursors work across every list endpoint. --- # Metric substitutions (/docs/metric-substitutions) Which unified fields stay null on which platform, how to fill the ones a join can recover, and the one place a field carries a different metric. Every platform maps onto one unified schema, but no platform publishes every field in it. When a platform does not provide a value, the response carries `null`. A unified field never holds a made-up number, and never holds a different metric under its name. When a platform has a related metric worth keeping, it goes under `ext.*`, so the unified field keeps its exact meaning. This page lists those cases. If a field is listed here as always `null` on a platform, that is how the platform works, not a bug. Fields a platform never shows are also never listed in a response's `unavailable` array; that array is for fields the platform does show but this response could not fill. See [Data availability](/docs/data-availability). ## Ratios you compute yourself [#ratios-you-compute-yourself] Schema 2 returns counts, not derived metrics. There is no engagement rate, reach estimate, language guess or category in the response: compute what you need from the fields that are there. * **Engagement rate by followers:** `(likes + comments + shares) / followers`, using `post.engagement.*` and the author's `followers` from a profile call. On X and TikTok, post rows also carry the author's follower count in `post.ext.author_followers`, so no second call is needed there. * **Engagement rate by views:** `(likes + comments + shares) / views`, where the platform shows views (video on most platforms). * Leave out any count that is `null` rather than treating it as `0`, and skip the ratio when the denominator is `null` or `0`. A `null` count means the platform does not show it; it is not zero. * **Language:** `post.language` and `comment.language` carry the platform's own tag when it states one, and are `null` otherwise. Nothing is detected for you. ## The one substitution [#the-one-substitution] Facebook events are the only place a unified field carries a different metric. An event has no views or likes, so on `/v1/facebook/profile/events` and `/v1/facebook/events` the RSVP counts use those two fields: * `post.engagement.views` holds the **interested** count. * `post.engagement.likes` holds the **going** count. The same numbers are also under their own names in `post.ext.event.interested_count` and `post.ext.event.going_count`. The mapping is identical whether the value came with the row or was filled by `include=details`. ## Instagram [#instagram] Instagram's nulls depend on the type of media. * `post.engagement.saves` is `null` everywhere except `/v1/instagram/post/stats` with `include=saves`, which reads save and repost counts for one post. List rows have no save count and stay `null`. * `post.engagement.likes` and `post.engagement.comments` are Instagram's own counts. Some reads of a post that was also shared to Facebook include the Facebook likes and comments; those are subtracted so the number matches what instagram.com shows. Accounts that hide their like count return `null` rather than Instagram's placeholder figure. * When something was subtracted, the row tells you. `post.ext.facebook_likes` and `post.ext.facebook_comments` hold the Facebook part that was removed. Add the two together for the combined figure. Both fields are absent on posts with no Facebook share, and each one is absent on its own when nothing was removed from that count. * `post.engagement.shares` is `null` on the basic endpoints. Share counts come only from `/v1/instagram/post/stats`, and from `/v1/instagram/profile/reels/full` and `/v1/instagram/profile/posts/full`, which are beta endpoints that keep the source's own structure. * `post.engagement.views` and the share count exist only on **video**: reels, videos, and carousels that contain a video. Photos and photo-only carousels have neither, on any endpoint, so a rate by views cannot be computed for them. Likes and comments are filled on every media type. * **Stories have no engagement numbers.** Story views, replies, likes and reach are private analytics for the account owner. `/v1/instagram/stories` returns each live story's media, timestamps and IDs, with the engagement fields `null`. No API can return story metrics for an account you do not own. ## Pinterest [#pinterest] Pinterest spreads its counts between the list endpoints and the pin itself. * Search rows have no counts: `post.engagement.saves`, `likes` (reactions), `comments` and `shares` are `null` on a plain `/v1/pinterest/search` call. Add `include=engagement` and each row is filled from the pin's own record in the same call, at 10 extra credits per row filled. `/v1/pinterest/pin` returns the same counts for a single pin for 10 credits. * `post.engagement.views` is `null` on search rows. Pinterest publishes no view count there. * Board rows have saves, comments and shares but no date: `post.published_at` is `null` on a plain `/v1/pinterest/board` call because the board data has no date. `include=engagement` fills the date from the pin, along with any missing reaction count. * `post.content.text` is the pin's title, or its description when there is no title. Many pins have neither, so `null` means the pin really has no text. Pinterest's auto-generated image caption is never used here. ## Threads [#threads] Threads puts some numbers only on single posts, and the lists can fill them on request. * `post.engagement.views` is `null` on `/v1/threads/user/posts` and `/v1/threads/search`, because Threads shows a view count only on a post's own page. `/v1/threads/post` returns it for one post for 10 credits, and `include=engagement` fills it on each row of either list in the same call, at 10 extra credits per row filled. On `search` this covers the first 20 rows. * **Brand-new posts** have no view count anywhere yet; Threads adds one later. On `search`, such a row adds no credits, because the join only charges when it fills a view count or display name, and it still gets its pinned flag. A direct `/v1/threads/post` call on such a post will not have a view count either. * `post.flags.pinned` is `null` on `search` rows, where Threads leaves the field out instead of sending `false`. `include=engagement` fills it. * `author.followers` and `author.bio` are `null` on `/v1/threads/search/users`, which returns identity only. `include=profile` fills both, plus the private flag and the bio link, at 10 extra credits per account filled. * `post.author.display_name` is `null` on `user/posts` by default. `include=engagement` fills it, and so does asking for more than 15 posts. * A join charges only for rows it fills, and adds nothing for a row it serves from a recent fetch. Read `credits_used` for what it cost. See [Threads](/docs/platforms/threads). ## LinkedIn [#linkedin] Two author fields are weaker on people-list rows than on the member's own profile, and a join recovers both in the same call. * `author.followers` on `/v1/linkedin/search/people`, `/v1/linkedin/company/people` and `/v1/linkedin/post/reactions` rows is LinkedIn's **rounded display figure**, not an exact count: 39,000 for a member with 39,278, or 2,000 for one with 2,344. Those rows set `author.followers_approximate` to `true`. Many rows show no figure at all, and there `author.followers` is `null`, not a guess. * `author.following` is `null` on every plain people-list row. * Send `include=profile` to join each row to that member's profile in the same call. `author.followers` becomes the exact count, `author.following` gets the member's connection count (on LinkedIn, following means connections), and `author.location`, `author.joined_at`, plus country, website, cover image and profile flags under `author.ext`, are filled where the row lacked them. Every row the join fills sets `author.followers_approximate` to `false`, including rows that had no count before, so the flag always tells you whether the figure is exact. Rows the join could not fill keep their original flag. * The list costs 100 credits, and the join adds 40 credits per row it fills, up to 500 credits in total. Rows served from a recent fetch and rows the join cannot fill add nothing. `limit` (1 to 10) caps both the rows and the extra credits. See [LinkedIn](/docs/platforms/linkedin). ## Facebook [#facebook] Facebook shows several per-post numbers only on the post's own page, so list rows have `null` there until you ask for the join. * Rows from `/v1/facebook/profile/posts` never carry `post.engagement.shares`, and reels in that feed also lack `post.engagement.views` and `post.content.duration_seconds`. Facebook publishes those numbers on the post page itself, not in the feed. Send `include=engagement` to fill each row from its own post in the same call, at 10 extra credits per row filled. A post with no public share count stays `null` rather than `0`, and that row adds nothing. * `post.engagement.saves` is always `null`. Facebook shows no save count anywhere. * A `/v1/facebook/profile/photos` row is the image, its caption and its link. `post.author.display_name`, `post.author.avatar_url`, `post.engagement.likes`, `comments`, `shares` and `post.published_at` are all `null` on a plain call. `include=details` fills them from the photo's own post, at 10 extra credits per photo filled. A photo posted alone carries that post's counts; a photo inside a multi-photo post carries its own, usually much lower. Photos have no view count, so `post.engagement.views` stays `null` either way. * `post.ext.event.end_timestamp` and `post.ext.event.ticket_url` are `null` on both event lists, with or without the join. Facebook did not publish either on any event checked, so the join cannot promise them. * A Facebook join charges only for rows it fills, and adds nothing for rows served from a recent fetch. Read `credits_used` for what it cost. See [Facebook](/docs/platforms/facebook). ## Always-null fields by platform [#always-null-fields-by-platform] These fields are `null` on every surface of a platform because the platform never shows them. No parameter will fill them. | Platform | Always `null` | Why | | ------------------------ | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | | YouTube | `post.engagement.shares`, `post.engagement.saves` | Both are private to the channel owner | | Reddit | `post.engagement.views`, `.shares`, `.saves` | Reddit shows no view or share counts, and "saved" is a per-user flag, not a count | | Reddit | `author.verified` | Not shown | | Facebook | `post.engagement.saves` | Not shown | | Facebook, list endpoints | `author.avatar_url`, `post.content.media_urls` | List rows are slimmer; on photos, `include=details` fills the author | | Threads | `author.following`, `author.posts_count`, `author.likes_count`, `author.joined_at` | None of the four appear on a public Threads profile | | Threads | `comment.parent_id` on replies | Threads does not say which reply a nested reply answers | On every platform, a top-level comment has `parent_id: null`, and a reply carries its parent comment's id where the platform says which one it is. One field has a default instead of a `null`: a single tweet from Twitter/X has no pinned flag, so `post.flags.pinned` is `false`. ## Defaults we set [#defaults-we-set] `/v1/instagram/search/hashtag` uses `type=top` when you leave `type` out, so results are predictable. Send `type=recent` or `type=clips` to change it. What changed in the response shape, and what was removed. Every unified post field and which platforms fill it. Reading values, nulls, missing fields and `unavailable` . --- # n8n integration (/docs/n8n) Use the InsightSocial API in n8n with the official community node, or start from two free workflows for viral content research and creator outreach lists. The `n8n-nodes-insightsocial` community node calls any InsightSocial endpoint from an n8n workflow. It reads the live endpoint catalogue, so every endpoint, parameter and price shows up in n8n as soon as the API has it, with no node update. ## Install the node [#install-the-node] In n8n, open **Settings → Community nodes → Install** and enter: ```text title="Package name" n8n-nodes-insightsocial ``` On a self-hosted n8n you can also run `npm install n8n-nodes-insightsocial` in your custom folder (usually `~/.n8n/custom`) and restart n8n. ## Add your key [#add-your-key] Create an API key at [insightsocial.app/portal/api/keys](https://www.insightsocial.app/portal/api/keys). Keys start with `isk_`. In n8n, create an **InsightSocial API** credential and paste the key. Leave the base URL as it is. Click **Test**. It calls `GET /v1/credits`, which is free. ## Operations [#operations] | Operation | What it does | Cost | | -------------- | --------------------------------------------------------------------- | -------------------- | | Call Endpoint | Pick a platform and an endpoint, fill in its parameters, get the data | The endpoint's price | | Get Credits | Your balance and this month's usage | Free | | List Endpoints | Every endpoint on a platform, with its parameters and price | Free | The endpoint dropdown shows each price, for example `Profile (10 credits)` or, on TikTok, `Post/Comments (10-30 credits, metered)`, so you can see what a workflow costs before you run it. Failed calls and empty results are free. ## Ready-made workflows [#ready-made-workflows] Both are free to import and need only an InsightSocial key. | Workflow | What it builds | | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | [Viral Content Radar](https://github.com/insightsocial/n8n-viral-content-radar) | Every weekday, this week's top TikToks and Reels in your niche, the top video's transcript, and an AI brief on why they spread | | [Creator Outreach List](https://github.com/insightsocial/n8n-creator-outreach-list) | Instagram creators posting about your keywords, with the business email and phone from their contact card, deduped into Google Sheets or CSV | ## Without the node [#without-the-node] Any n8n **HTTP Request** node works too: send a GET to `https://api.insightsocial.app/v1/...` with your key in an `x-api-key` header. The [endpoint reference](/docs/api-reference) lists every path and parameter. ## Next steps [#next-steps] Connect Claude, Cursor or any MCP client. End-to-end builds you can adapt. What each call costs and what is free. --- # Pagination (/docs/pagination) Read pagination.next_cursor, send it back as cursor, stop when has_more is false. The same loop works on every list endpoint. List endpoints return one page at a time. Walking a list is the same on every platform: take the cursor from the response, send it back, and stop when the response says there is nothing left. ## The rule [#the-rule] **Send `pagination.next_cursor` back as the `cursor` query parameter. Stop when `pagination.has_more` is `false`.** You never build a cursor or decode one. It is an opaque string that starts `v2c.`. Pass it back exactly as you received it, under the name `cursor`, with the same parameters as the first page, within 24 hours. ## The pagination block [#the-pagination-block] A list response carries a `pagination` object next to `data`: ```json title="Response" { "success": true, "platform": "tiktok", "endpoint": "/v1/tiktok/profile/videos", "schema_version": "2", "data": { "items": [ /* this page's videos, each { "post": { ... } } */ ], "total": null, "truncated": null }, "pagination": { "next_cursor": "v2c.pvp_XLAfnPv6WqqWVPeenDzzgzIG5pSI…", "has_more": true, "page_size": 10 }, "unavailable": [], "credits_used": 10, "credits_remaining": 9490, "request_id": "req_1a2b3c4d5e6f", "cached": false, "idempotent_replay": false, "charge_reason": "miss", "free_call": false } ``` | Field | Meaning | | ------------- | -------------------------------------------------------------------------------------- | | `next_cursor` | Token for the next page. Send it back as `cursor`. `null` on the last page. | | `has_more` | `true` while there are more pages. **This is your stop signal.** | | `page_size` | How many items this page returned. It reports; it does not control. | | `stopped_at` | Only when you sent `since` or `stop_at_id`. See [Incremental sync](#incremental-sync). | Do not use an empty page or a total count to decide you are done. `has_more` is the only reliable signal. `data.total`, when present, is the platform's own total (all comments on a post, for example), not the number of pages left. The cursor is only in `pagination.next_cursor`. There is no copy inside `data`. ## A round trip [#a-round-trip] Page 1, no cursor: ```bash title="cURL" curl "https://api.insightsocial.app/v1/tiktok/profile/videos?handle=khaby.lame" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` The response carries `"pagination": { "next_cursor": "v2c.pvp_XLAf…", "has_more": true, "page_size": 10 }`. Page 2 sends that value back, with the same `handle`: ```bash title="cURL" curl "https://api.insightsocial.app/v1/tiktok/profile/videos?handle=khaby.lame&cursor=v2c.pvp_XLAf…" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` Repeat until `has_more` is `false`. URL-encode the cursor if your HTTP client does not do it for you. A cursor is bound to the request that produced it: the same endpoint and the same parameters. Only `limit`, `max_pages`, `dry_run` and `fresh` may change between pages. Change anything else, such as the `handle` or a filter, and the cursor is refused with `CURSOR_INVALID`; start a new walk instead. ## The loop [#the-loop] ```bash title="cURL" #!/usr/bin/env bash # Walk every page of a list and print each item as one JSON line. Needs jq. url="https://api.insightsocial.app/v1/tiktok/profile/videos" cursor="" while :; do if [ -n "$cursor" ]; then page=$(curl -sG "$url" --data-urlencode "handle=khaby.lame" \ --data-urlencode "cursor=$cursor" -H "x-api-key: $INSIGHTSOCIAL_API_KEY") else page=$(curl -sG "$url" --data-urlencode "handle=khaby.lame" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY") fi echo "$page" | jq -c '.data.items[]' [ "$(echo "$page" | jq -r '.pagination.has_more')" = "true" ] || break cursor=$(echo "$page" | jq -r '.pagination.next_cursor') done ``` ```python title="Python" import os import requests BASE = "https://api.insightsocial.app/v1" HEADERS = {"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]} def paginate(path, params): cursor = None while True: query = dict(params) if cursor: query["cursor"] = cursor # always the parameter named "cursor" body = requests.get(f"{BASE}/{path}", params=query, headers=HEADERS).json() if not body["success"]: raise RuntimeError(f'{body["error"]["type"]}: {body["request_id"]}') yield from body["data"]["items"] page = body["pagination"] if not page["has_more"]: break cursor = page["next_cursor"] # send it back unchanged for video in paginate("tiktok/profile/videos", {"handle": "khaby.lame"}): print(video["post"]["url"]) ``` ```ts title="Node.js" const BASE = "https://api.insightsocial.app/v1"; async function* paginate(path: string, params: Record) { let cursor: string | null = null; while (true) { const url = new URL(`${BASE}/${path}`); for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v); if (cursor) url.searchParams.set("cursor", cursor); // always "cursor" const res = await fetch(url, { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! }, }); const body = await res.json(); if (!body.success) throw new Error(`${body.error.type}: ${body.request_id}`); yield* body.data.items; if (!body.pagination.has_more) return; cursor = body.pagination.next_cursor; // send it back unchanged } } for await (const video of paginate("tiktok/profile/videos", { handle: "khaby.lame" })) { console.log(video.post.url); } ``` Two things matter: 1. **Send the cursor back byte for byte, under the name `cursor`, with the same parameters.** Do not trim, decode or re-encode it. 2. **Stop on `has_more: false`**, which is also when `next_cursor` is `null`. 3. **Finish a walk within 24 hours.** An older cursor is refused with `CURSOR_EXPIRED`; start again from page 1. ## Draining a whole list [#draining-a-whole-list] A full walk is a series of normal calls, so plan for three things. * **Cost.** Every page is billed as one call to that endpoint. Fifty pages of a 10-credit endpoint cost about 500 credits. Multiply the page count by the price in [Endpoint pricing](/docs/endpoint-pricing) before you start, and cap the number of pages if you only need the most recent items. Re-walking the same pages is charged again, so keep the pages you already have. * **Limits.** Pages of one list are sequential, since each needs the previous cursor. You can walk several lists in parallel, but one key allows 10 calls in flight and 60 calls per minute. Past that you get `429` (`CONCURRENCY_LIMIT` or `RATE_LIMITED`) with a `Retry-After` header, and nothing is charged. * **Retries.** On a `429` or a `503`, wait and retry **the same cursor**. Never move past a page you did not read successfully. ```python title="Python" import time def get_page(path, query): for attempt in range(5): res = requests.get(f"{BASE}/{path}", params=query, headers=HEADERS) if res.status_code in (429, 503): time.sleep(float(res.headers.get("Retry-After", 2 ** attempt))) continue # same query, same cursor return res.json() raise RuntimeError("gave up after 5 attempts") ``` ## Several pages in one call: `max_pages` [#several-pages-in-one-call-max_pages] Some endpoints take `max_pages` (1 to 5, default 1). The call walks up to that many pages itself and returns the rows from all of them in `data.items`. `pagination.next_cursor` continues from where it stopped, and `has_more` says whether there is more. The walk can stop before `max_pages` when the list runs out. Each page walked is billed as one call. The walk itself is still one request, so it takes a single slot of your rate limit however many pages it covers. What we hold before the call is the most one page can cost with your parameters, times `max_pages`, and the call never costs more than that. See [Credits](/docs/credits#reserve-the-ceiling-settle-the-actual). ```bash title="cURL" curl "https://api.insightsocial.app/v1/tiktok/search/top?query=basketball&max_pages=3" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` This holds up to 30 credits (10 × 3) and charges for the pages actually walked. To see the hold before you run it, add `dry_run=1`: the response carries `data.dry_run` with `credits_min` and `credits_max`, and costs nothing. ## Incremental sync [#incremental-sync] A daily poll only needs what was posted since the last run. Eight creator feeds take two parameters that end the walk where your previous poll stopped, so you pay only for the pages that have new posts. | Platform | Endpoint | | --------- | -------------------------------------------------------------------- | | Facebook | `GET /v1/facebook/profile/posts` | | Instagram | `GET /v1/instagram/profile/posts`, `GET /v1/instagram/profile/reels` | | Threads | `GET /v1/threads/user/posts` | | TikTok | `GET /v1/tiktok/profile/videos` | | Twitter/X | `GET /v1/twitter/user/tweets` | | YouTube | `GET /v1/youtube/channel/videos`, `GET /v1/youtube/channel/shorts` | | Parameter | Value | Effect | | ------------ | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `stop_at_id` | `post.id` or `post.url` of the newest post you already have | The page ends just before that post. It and everything older are left off, no `next_cursor` is returned, and `stopped_at` is `known_id`. If the post is not on this page, the full page comes back with its cursor, so keep walking. | | `since` | `YYYY-MM-DD` (midnight UTC) or an ISO 8601 timestamp | Older posts are left off. The page that reaches one ends the walk, with `stopped_at` set to `since`. | ```bash title="cURL" curl "https://api.insightsocial.app/v1/instagram/profile/posts?handle=nasa&since=2026-09-01" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```json title="Response" "pagination": { "next_cursor": null, "has_more": false, "page_size": 6, "stopped_at": "since" } ``` | `stopped_at` | Meaning | | ------------ | --------------------------------------------------------------------- | | `known_id` | The walk reached the post named in `stop_at_id`. | | `since` | The walk reached a post older than `since`. | | `end` | The list ran out before either boundary. | | `null` | No boundary on this page yet. Send `next_cursor` back and keep going. | Send the same `stop_at_id` or `since` on every page of the walk, and store the newest post you received for the next run. ### Date filters on other lists [#date-filters-on-other-lists] Over a hundred more lists of posts and comments take `since` and `until` as filters, in the same formats (`until` includes its whole UTC day), and the eight feeds above take `until` too. Each endpoint's reference shows whether it has them. On ten of them `since` also ends the walk as above and sorts the list newest first: comments on Instagram, LinkedIn, TikTok and YouTube posts, LinkedIn company posts, X search, and Reddit search, subreddits and user posts. Elsewhere the list is not in date order, so rows outside the window are left off each page but the walk does not stop: keep following `next_cursor`. A page is charged even when every row on it was filtered out. A row with no date is kept. Pinned posts sit out of date order. They never end the walk and are never taken as the stop point, so do not store a pinned post as your newest. These parameters do not change what a page costs. What they save is the pages you never fetch: if the post you already have shows up on the first page, the walk ends there and that first page is the whole bill. ## Native paging parameters [#native-paging-parameters] Every endpoint pages with `cursor` and nothing else, with one exception. `/v1/youtube/video/comment/replies` requires `continuationToken`, and its value is not a page token: it is the `id` of a top-level comment from `/v1/youtube/video/comments`, which names the thread to open. Page through that thread's replies with `cursor` like any other list. A response never hands you a platform page token: the only cursor it carries is `pagination.next_cursor`. Sending `next_cursor` under any other name, such as `continuationToken`, or a cursor from the previous schema (`is.` or `is2.`), is refused with `CURSOR_INVALID` instead of quietly returning page 1 again. A few search endpoints also take `page`, a page number. It picks the page a walk starts on; it is not a token. Walk on from there with `cursor`. ## Bad or stale cursors [#bad-or-stale-cursors] A bad cursor is refused before anything is fetched, with `error.param` naming the parameter that carried it. Errors are never charged. | `error.type` | Cause | What to do | | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | | `CURSOR_INVALID` | The cursor was edited or truncated, was not issued by this API, was sent under a name other than `cursor`, or belongs to a different endpoint or different parameters. | Send `pagination.next_cursor` back unchanged with the original parameters, or start again without a cursor. | | `CURSOR_EXPIRED` | The cursor is more than 24 hours old. | Start again without a cursor. | ```json title="Response" { "success": false, "error": { "type": "CURSOR_INVALID", "message": "This cursor was not issued by this API. Send `pagination.next_cursor` back unchanged. Nothing was charged.", "param": "cursor" }, "request_id": "req_a7f5ccf4ce56", "credits_used": 0, "credits_remaining": null } ``` ## `limit` is not always a page size [#limit-is-not-always-a-page-size] What `limit` does depends on the endpoint. Read its description on the endpoint page before you rely on it. * **Page size or row cap.** It trims one page. On `GET /v1/linkedin/search/people`, `limit` takes the top rows of a page and the cursor still moves past the whole page, so the skipped rows do not reappear on the next one. * **Collect until N.** The endpoint walks pages for you until it has `limit` items, and bills per page it used. This is how `GET /v1/instagram/profile/posts/full`, `GET /v1/instagram/profile/reels/full` and `GET /v1/facebook/profile/reels/full` (up to 50) and `GET /v1/threads/search` (up to 100) behave. `pagination.page_size` in the response is a report of what arrived, never a control. ## Every paginated endpoint [#every-paginated-endpoint] These 120 endpoints return the `pagination` block and take `cursor`. Each page is one call at the price shown; a range means the endpoint is metered. ### Instagram [#instagram] | Endpoint | Credits per call | Also takes | | --------------------------------------------------------------------------------------- | ---------------- | --------------------- | | [`/instagram/profile/posts`](/docs/api-reference/instagram/profile-posts) | 20–260 | `since`, `stop_at_id` | | [`/instagram/post/comments`](/docs/api-reference/instagram/post-comments) | 100–300 | — | | [`/instagram/profile/reels`](/docs/api-reference/instagram/profile-reels) | 20–280 | `since`, `stop_at_id` | | [`/instagram/search/reels`](/docs/api-reference/instagram/search-reels) | 20–1,300 | `max_pages` | | [`/instagram/audio/reels`](/docs/api-reference/instagram/audio-reels) | 20 | — | | [`/instagram/search/hashtag`](/docs/api-reference/instagram/search-hashtag) | 100 | `max_pages` | | [`/instagram/search/profiles`](/docs/api-reference/instagram/search-profiles) | 20–500 | — | | [`/instagram/followers`](/docs/api-reference/instagram/followers) | 100–200 | — | | [`/instagram/following`](/docs/api-reference/instagram/following) | 100–200 | — | | [`/instagram/tagged`](/docs/api-reference/instagram/tagged) | 100 | — | | [`/instagram/location/posts`](/docs/api-reference/instagram/location-posts) | 100 | — | | [`/instagram/search/popular`](/docs/api-reference/instagram/search-popular) | 20–260 | — | | [`/instagram/post/comment/replies`](/docs/api-reference/instagram/post-comment-replies) | 20 | — | | [`/instagram/search/music`](/docs/api-reference/instagram/search-music) | 100 | — | | [`/instagram/profile/full`](/docs/api-reference/instagram/profile-full) | 100 | — | | [`/instagram/profile/reels/full`](/docs/api-reference/instagram/profile-reels-full) | 100–500 | — | | [`/instagram/profile/posts/full`](/docs/api-reference/instagram/profile-posts-full) | 100–500 | — | ### TikTok [#tiktok] | Endpoint | Credits per call | Also takes | | ----------------------------------------------------------------------------------- | ---------------- | --------------------- | | [`/tiktok/profile/videos`](/docs/api-reference/tiktok/profile-videos) | 20 | `since`, `stop_at_id` | | [`/tiktok/post/comments`](/docs/api-reference/tiktok/post-comments) | 20–60 | — | | [`/tiktok/video/comment/replies`](/docs/api-reference/tiktok/video-comment-replies) | 20 | — | | [`/tiktok/search`](/docs/api-reference/tiktok/search) | 20–740 | `max_pages` | | [`/tiktok/search/hashtag`](/docs/api-reference/tiktok/search-hashtag) | 20 | `max_pages` | | [`/tiktok/search/top`](/docs/api-reference/tiktok/search-top) | 20 | `max_pages` | | [`/tiktok/search/users`](/docs/api-reference/tiktok/search-users) | 20–2,620 | — | | [`/tiktok/user/followers`](/docs/api-reference/tiktok/user-followers) | 20 | — | | [`/tiktok/user/following`](/docs/api-reference/tiktok/user-following) | 20 | — | | [`/tiktok/song/videos`](/docs/api-reference/tiktok/song-videos) | 20 | — | | [`/tiktok/adlibrary/search`](/docs/api-reference/tiktok/adlibrary-search) | 100–340 | — | | [`/tiktok/collection/videos`](/docs/api-reference/tiktok/collection-videos) | 20 | — | | [`/tiktok/playlist/videos`](/docs/api-reference/tiktok/playlist-videos) | 20 | — | | [`/tiktok/user/liked`](/docs/api-reference/tiktok/user-liked) | 20 | — | | [`/tiktok/location/posts`](/docs/api-reference/tiktok/location-posts) | 20 | — | | [`/tiktok/effect/videos`](/docs/api-reference/tiktok/effect-videos) | 20 | — | | [`/tiktok/search/music`](/docs/api-reference/tiktok/search-music) | 20 | — | | [`/tiktok/profile/full`](/docs/api-reference/tiktok/profile-full) | 100 | — | ### Facebook [#facebook] | Endpoint | Credits per call | Also takes | | --------------------------------------------------------------------------------------- | ---------------- | --------------------- | | [`/facebook/profile/posts`](/docs/api-reference/facebook/profile-posts) | 20–80 | `since`, `stop_at_id` | | [`/facebook/post/comments`](/docs/api-reference/facebook/post-comments) | 20 | — | | [`/facebook/group/posts`](/docs/api-reference/facebook/group-posts) | 20 | — | | [`/facebook/profile/photos`](/docs/api-reference/facebook/profile-photos) | 20–180 | — | | [`/facebook/profile/reels`](/docs/api-reference/facebook/profile-reels) | 20 | — | | [`/facebook/adlibrary/company/ads`](/docs/api-reference/facebook/adlibrary-company-ads) | 100 | — | | [`/facebook/adlibrary/search/ads`](/docs/api-reference/facebook/adlibrary-search-ads) | 100 | — | | [`/facebook/profile/events`](/docs/api-reference/facebook/profile-events) | 20–180 | — | | [`/facebook/post/comment/replies`](/docs/api-reference/facebook/post-comment-replies) | 20 | — | | [`/facebook/marketplace/search`](/docs/api-reference/facebook/marketplace-search) | 20 | — | | [`/facebook/events/search`](/docs/api-reference/facebook/events-search) | 20 | — | | [`/facebook/events`](/docs/api-reference/facebook/events) | 20–260 | — | | [`/facebook/profile/full`](/docs/api-reference/facebook/profile-full) | 100 | — | | [`/facebook/profile/reels/full`](/docs/api-reference/facebook/profile-reels-full) | 100–500 | — | | [`/facebook/search/posts`](/docs/api-reference/facebook/search-posts) | 20–100 | `max_pages` | | [`/facebook/search/pages`](/docs/api-reference/facebook/search-pages) | 20 | — | | [`/facebook/search/people`](/docs/api-reference/facebook/search-people) | 20 | — | | [`/facebook/search/videos`](/docs/api-reference/facebook/search-videos) | 20 | — | | [`/facebook/search/groups`](/docs/api-reference/facebook/search-groups) | 20–220 | — | ### LinkedIn [#linkedin] | Endpoint | Credits per call | Also takes | | ------------------------------------------------------------------------------------------------------- | ---------------- | ---------- | | [`/linkedin/search/companies`](/docs/api-reference/linkedin/search-companies) | 200 | — | | [`/linkedin/profile/articles`](/docs/api-reference/linkedin/profile-articles) | 100 | — | | [`/linkedin/article/comments`](/docs/api-reference/linkedin/article-comments) | 100 | — | | [`/linkedin/article/reactions`](/docs/api-reference/linkedin/article-reactions) | 100 | — | | [`/linkedin/profile/interests/schools`](/docs/api-reference/linkedin/profile-interests-schools) | 100 | — | | [`/linkedin/profile/interests/newsletters`](/docs/api-reference/linkedin/profile-interests-newsletters) | 100 | — | | [`/linkedin/search/people`](/docs/api-reference/linkedin/search-people) | 200–1,000 | — | | [`/linkedin/company/people`](/docs/api-reference/linkedin/company-people) | 200–1,000 | — | | [`/linkedin/post/comments`](/docs/api-reference/linkedin/post-comments) | 100 | — | | [`/linkedin/post/reposts`](/docs/api-reference/linkedin/post-reposts) | 100 | — | | [`/linkedin/group/posts`](/docs/api-reference/linkedin/group-posts) | 100 | — | | [`/linkedin/post/comments/replies`](/docs/api-reference/linkedin/post-comments-replies) | 100 | — | | [`/linkedin/profile/experiences`](/docs/api-reference/linkedin/profile-experiences) | 100 | — | | [`/linkedin/profile/educations`](/docs/api-reference/linkedin/profile-educations) | 100 | — | | [`/linkedin/profile/skills`](/docs/api-reference/linkedin/profile-skills) | 100 | — | | [`/linkedin/profile/honors`](/docs/api-reference/linkedin/profile-honors) | 100 | — | | [`/linkedin/profile/certifications`](/docs/api-reference/linkedin/profile-certifications) | 100 | — | | [`/linkedin/profile/publications`](/docs/api-reference/linkedin/profile-publications) | 100 | — | | [`/linkedin/profile/volunteers`](/docs/api-reference/linkedin/profile-volunteers) | 100 | — | | [`/linkedin/profile/recommendations`](/docs/api-reference/linkedin/profile-recommendations) | 100 | — | | [`/linkedin/profile/interests/companies`](/docs/api-reference/linkedin/profile-interests-companies) | 100 | — | | [`/linkedin/profile/interests/groups`](/docs/api-reference/linkedin/profile-interests-groups) | 100 | — | | [`/linkedin/post/reactions`](/docs/api-reference/linkedin/post-reactions) | 200–1,000 | — | | [`/linkedin/company/jobs`](/docs/api-reference/linkedin/company-jobs) | 200 | — | | [`/linkedin/search/jobs`](/docs/api-reference/linkedin/search-jobs) | 200 | — | | [`/linkedin/search/schools`](/docs/api-reference/linkedin/search-schools) | 20 | — | | [`/linkedin/company/posts`](/docs/api-reference/linkedin/company-posts) | 100 | — | | [`/linkedin/ads/search`](/docs/api-reference/linkedin/ads-search) | 100 | — | | [`/linkedin/profile/posts/archive`](/docs/api-reference/linkedin/profile-posts-archive) | 100–10,000 | — | | [`/linkedin/search/posts`](/docs/api-reference/linkedin/search-posts) | 20–960 | — | | [`/linkedin/profile/full`](/docs/api-reference/linkedin/profile-full) | 100 | — | ### Twitter/X [#twitterx] | Endpoint | Credits per call | Also takes | | --------------------------------------------------------------------------- | ---------------- | --------------------- | | [`/twitter/user/tweets`](/docs/api-reference/twitter/user-tweets) | 20 | `since`, `stop_at_id` | | [`/twitter/search/tweets`](/docs/api-reference/twitter/search-tweets) | 20–100 | `max_pages` | | [`/twitter/tweet/replies`](/docs/api-reference/twitter/tweet-replies) | 20 | — | | [`/twitter/user/media`](/docs/api-reference/twitter/user-media) | 20 | — | | [`/twitter/user/followers`](/docs/api-reference/twitter/user-followers) | 20 | — | | [`/twitter/user/following`](/docs/api-reference/twitter/user-following) | 20 | — | | [`/twitter/tweet/retweeters`](/docs/api-reference/twitter/tweet-retweeters) | 20 | — | | [`/twitter/search/users`](/docs/api-reference/twitter/search-users) | 20 | — | | [`/twitter/profile/full`](/docs/api-reference/twitter/profile-full) | 100 | — | ### Threads [#threads] | Endpoint | Credits per call | Also takes | | ------------------------------------------------------- | ---------------- | ----------- | | [`/threads/search`](/docs/api-reference/threads/search) | 20–680 | `max_pages` | ### YouTube [#youtube] | Endpoint | Credits per call | Also takes | | ----------------------------------------------------------------------------------------- | ---------------- | --------------------- | | [`/youtube/channel/videos`](/docs/api-reference/youtube/channel-videos) | 20–40 | `since`, `stop_at_id` | | [`/youtube/video/comments`](/docs/api-reference/youtube/video-comments) | 20 | — | | [`/youtube/video/comment/replies`](/docs/api-reference/youtube/video-comment-replies) | 20 | — | | [`/youtube/search`](/docs/api-reference/youtube/search) | 20–300 | `max_pages` | | [`/youtube/channel/shorts`](/docs/api-reference/youtube/channel-shorts) | 20–40 | `since`, `stop_at_id` | | [`/youtube/playlist`](/docs/api-reference/youtube/playlist) | 20–220 | — | | [`/youtube/search/hashtag`](/docs/api-reference/youtube/search-hashtag) | 20–220 | `max_pages` | | [`/youtube/channel/playlists`](/docs/api-reference/youtube/channel-playlists) | 20 | — | | [`/youtube/channel/lives`](/docs/api-reference/youtube/channel-lives) | 20–140 | — | | [`/youtube/channel/community-posts`](/docs/api-reference/youtube/channel-community-posts) | 20 | — | | [`/youtube/videos/trending`](/docs/api-reference/youtube/videos-trending) | 20–120 | — | | [`/youtube/playlist/items`](/docs/api-reference/youtube/playlist-items) | 20–220 | — | | [`/youtube/search/advanced`](/docs/api-reference/youtube/search-advanced) | 20–220 | `max_pages` | | [`/youtube/profile/full`](/docs/api-reference/youtube/profile-full) | 100 | — | ### Reddit [#reddit] | Endpoint | Credits per call | Also takes | | --------------------------------------------------------------------------- | ---------------- | ----------- | | [`/reddit/subreddit`](/docs/api-reference/reddit/subreddit) | 20 | — | | [`/reddit/search`](/docs/api-reference/reddit/search) | 20–600 | `max_pages` | | [`/reddit/post/comments`](/docs/api-reference/reddit/post-comments) | 100 | — | | [`/reddit/subreddit/search`](/docs/api-reference/reddit/subreddit-search) | 20–520 | — | | [`/reddit/profile/posts`](/docs/api-reference/reddit/profile-posts) | 20 | — | | [`/reddit/search/comments`](/docs/api-reference/reddit/search-comments) | 20 | — | | [`/reddit/subreddits/search`](/docs/api-reference/reddit/subreddits-search) | 20–520 | — | | [`/reddit/search/media`](/docs/api-reference/reddit/search-media) | 20 | — | | [`/reddit/omni-search`](/docs/api-reference/reddit/omni-search) | 100–180 | — | ### Pinterest [#pinterest] | Endpoint | Credits per call | Also takes | | ----------------------------------------------------------- | ---------------- | ---------- | | [`/pinterest/search`](/docs/api-reference/pinterest/search) | 20–520 | — | | [`/pinterest/board`](/docs/api-reference/pinterest/board) | 20–320 | — | ## See also [#see-also] How each page is held and charged. Shared-cache hits and forcing a fresh fetch. The price of every endpoint. Retries, idempotency and rate limits for long walks. --- # Production checklist (/docs/production-checklist) Timeouts, retries, idempotency, pagination, rate limits, caching, keys, budgets and logging before you ship. Route every call through one server-side wrapper that handles the key, timeouts, retries and logging. Keep per-endpoint choices, like the timeout, as arguments so each call site can set its own. ## Start with one request wrapper [#start-with-one-request-wrapper] ```ts title="Node.js" const BASE_URL = "https://api.insightsocial.app"; const BASE_BACKOFF_MS = 500; const MAX_BACKOFF_MS = 8_000; // error.type values worth retrying. Everything else is a permanent answer. const RETRYABLE = new Set([ "RATE_LIMITED", "CONCURRENCY_LIMIT", "IDEMPOTENCY_IN_PROGRESS", "SERVICE_UNAVAILABLE", "UPSTREAM_ERROR", "UPSTREAM_INVALID", "INTERNAL_ERROR", ]); const RETRYABLE_STATUS = new Set([429, 500, 503]); interface Envelope { success: boolean; schema_version?: "2"; data?: T; pagination?: { next_cursor: string | null; has_more: boolean; page_size: number; stopped_at?: "since" | "known_id" | "end" | null; }; unavailable?: string[]; error?: { type: string; message: string; param?: string }; request_id: string; credits_used: number; credits_remaining: number | null; cached?: boolean; idempotent_replay?: boolean; charge_reason?: "miss" | "shared_cache" | "replay" | "no_result" | "dry_run"; free_call?: boolean; } interface Attempt { endpoint: string; attempt: number; status: number | null; errorType: string | null; requestId: string | null; creditsUsed: number | null; chargeReason: string | null; latencyMs: number; } export class InsightSocialError extends Error { constructor(message: string, readonly attempt: Attempt) { super(message); this.name = "InsightSocialError"; } } const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); function backoff(attempt: number, retryAfterMs = 0) { const cap = Math.min(MAX_BACKOFF_MS, BASE_BACKOFF_MS * 2 ** attempt); return Math.max(retryAfterMs, Math.random() * cap); // full jitter, never below Retry-After } export async function callApi(opts: { endpoint: `/v1/${string}`; query?: Record; attemptTimeoutMs: number; maxRetries?: number; onAttempt?: (a: Attempt) => void; }): Promise> { const key = process.env.INSIGHTSOCIAL_API_KEY; if (!key) throw new Error("INSIGHTSOCIAL_API_KEY is not set"); const url = new URL(opts.endpoint, BASE_URL); for (const [k, v] of Object.entries(opts.query ?? {})) url.searchParams.set(k, v); const maxRetries = opts.maxRetries ?? 3; const idempotencyKey = crypto.randomUUID(); // one per logical call, reused on every retry for (let attempt = 0; attempt <= maxRetries; attempt++) { const started = performance.now(); const log = (a: Omit): Attempt => { const entry = { endpoint: opts.endpoint, attempt: attempt + 1, latencyMs: Math.round(performance.now() - started), ...a, }; try { opts.onAttempt?.(entry); } catch { /* logging must not change the outcome */ } return entry; }; let res: Response; try { res = await fetch(url, { headers: { "x-api-key": key, "Idempotency-Key": idempotencyKey }, signal: AbortSignal.timeout(opts.attemptTimeoutMs), }); } catch (cause) { const entry = log({ status: null, errorType: "NETWORK_OR_TIMEOUT", requestId: null, creditsUsed: null, chargeReason: null }); if (attempt < maxRetries) { await sleep(backoff(attempt)); continue; } throw new InsightSocialError(String(cause), entry); } const retryAfter = Number(res.headers.get("retry-after")); const retryAfterMs = Number.isFinite(retryAfter) && retryAfter > 0 ? retryAfter * 1_000 : undefined; let body: Envelope | undefined; try { body = (await res.json()) as Envelope; } catch { body = undefined; } const entry = log({ status: res.status, errorType: body?.error?.type ?? (body ? null : "INVALID_RESPONSE"), requestId: body?.request_id ?? res.headers.get("x-request-id"), creditsUsed: body?.credits_used ?? null, chargeReason: body?.charge_reason ?? null, }); if (res.ok && body?.success) { // These docs describe schema 2. Fail loudly if a body arrives in another shape. if (body.schema_version !== "2") { throw new InsightSocialError(`Unexpected schema_version: ${String(body.schema_version)}`, entry); } return body; } // By status as well as type: a 503 can carry the platform's own error type. const retryable = RETRYABLE_STATUS.has(res.status) || RETRYABLE.has(body?.error?.type); if (!retryable || attempt === maxRetries) { throw new InsightSocialError(body?.error?.message ?? `HTTP ${res.status}`, entry); } await sleep(backoff(attempt, retryAfterMs)); } throw new Error("unreachable"); } const profile = await callApi({ endpoint: "/v1/instagram/profile", query: { handle: "natgeo" }, attemptTimeoutMs: 90_000, onAttempt: (a) => console.info("insightsocial_attempt", a), }); console.log(profile.request_id, profile.credits_used, profile.charge_reason); ``` The key is read from the server environment only. The wrapper checks `schema_version` on every success, so a change of response shape fails at the edge of your code, not deep inside a parser. Each attempt reports `request_id`, the charge and the charge reason to your logs. See [Authentication](/docs/authentication) and [Response schema](/docs/response-schema). ## Set timeouts per endpoint [#set-timeouts-per-endpoint] Pass a per-attempt timeout on every call. When a source is slow we retry it ourselves before answering, so one call can take a minute or more in the worst case. A timeout much shorter than that turns slow successes into failures and retries. Start from your own measurements of uncached calls, per endpoint. A single profile, a feed page, a metered search with `max_pages` and a transcript all behave differently. Total wall time for a logical call is the attempts plus backoff plus any `Retry-After` waits. If you abandon an attempt, the call is not charged. See the idempotency caveat below before you retry it. ## Retry only transient failures [#retry-only-transient-failures] Retry these `error.type` values: `RATE_LIMITED`, `CONCURRENCY_LIMIT`, `IDEMPOTENCY_IN_PROGRESS`, `SERVICE_UNAVAILABLE`, `UPSTREAM_ERROR`, `UPSTREAM_INVALID`, `INTERNAL_ERROR`, and any `429`, `500` or `503` whatever its type. Honor `Retry-After` when it is present, otherwise back off exponentially with full jitter, and cap the number of attempts. Do not retry answers that will not change: `MISSING_API_KEY`, `INVALID_API_KEY`, `API_KEY_REVOKED`, `INSUFFICIENT_CREDITS`, `UNKNOWN_PLATFORM`, `UNKNOWN_ENDPOINT`, `METHOD_NOT_SUPPORTED`, parameter errors such as `INVALID_REQUEST` and `UNSUPPORTED_PARAMETER` (remove the parameter named in `error.param`), `IDEMPOTENCY_KEY_REUSED` (use a new key), and `RESOURCE_NOT_FOUND`, which means nothing exists at the identifier you sent (not charged). `CURSOR_INVALID` and `CURSOR_EXPIRED` are not retried with the same cursor: restart the list without one. Branch on `error.type`, never on the message text. See [Error handling](/docs/errors). Errors are never charged, so a retry costs only time and rate-limit room. ## Make retries safe [#make-retries-safe] Generate one `Idempotency-Key` per logical call and send the same value on every retry of that call. * The key may be up to 255 characters of letters, digits, `.`, `_`, `:` and `-`. A key outside that format is ignored, not rejected, so the retry is treated as a new call. * Keys are scoped to your account. * A replay of a call we already answered comes back with `idempotent_replay: true`, `charge_reason: "replay"` and `credits_used: 0`. * A retry that arrives while the first attempt is still running gets `409 IDEMPOTENCY_IN_PROGRESS` with `Retry-After`. Wait and send it again. * Do not reuse a key for a new page, a changed parameter or a different endpoint. A key reused with a different request is rejected with `409 IDEMPOTENCY_KEY_REUSED`. If we never answered the first attempt successfully, for example because your client timed it out, the retry with the same key that delivers the data is charged as a normal call (`charge_reason: "miss"`) at exactly the ceiling the call reserved: the listed price, or the top of the range for a metered endpoint, multiplied by `max_pages` if you sent it. Like any charged call, it can be covered by one of your free calls. It costs 0 if the replay returns nothing. ## Drain every page [#drain-every-page] For a list, send `pagination.next_cursor` back unchanged as `cursor`, with the same parameters as the first page, and stop when `pagination.has_more` is `false`. Do not treat an empty page or a count as the end. Cursors start with `v2c.`, are opaque, and last 24 hours. A cursor sent with different parameters answers `400 CURSOR_INVALID`; an old one answers `400 CURSOR_EXPIRED`. Neither is charged. Long walks should save their place in your own store and be able to restart from page 1. Pages are sequential. Give each page its own idempotency key, and keep that key for the page's retries. On a retry, resend the same cursor. See [Pagination](/docs/pagination). ## Control rate and concurrency [#control-rate-and-concurrency] Each key allows **60 calls per rolling minute** and **10 calls in flight**. They fail differently: `RATE_LIMITED` means you started too many calls in the window, `CONCURRENCY_LIMIT` means too many are still running. Both are `429` with `Retry-After`, and neither is charged. Keep your worker pool and your call rate below both limits, and apply backpressure before adding retries, since retries use the same budget. There are no rate-limit headers to read, so track your own counts. See [Rate limits](/docs/rate-limits). ## Decide when cached data is acceptable [#decide-when-cached-data-is-acceptable] A response with `cached: true` came from our shared cache and costs 2 credits. It is not a guarantee: the entry may not exist when you call. Every repeat of a call is charged, so cache responses you need again on your side. Send `Cache-Control: no-cache` (or `fresh=1`) only where your application needs data as of now. It is always charged at the endpoint's full price. See [Caching](/docs/caching). ## Separate keys by environment [#separate-keys-by-environment] Create separate keys for production, staging, CI and local development, and store each in a server-side secret store. You can hold up to 25 keys. Revoking one does not touch the others, and each key has its own rate and concurrency limits. Keys have no scopes: every key reaches every endpoint. And every key draws on the same account balance, so a separate key isolates revocation and rate limits, not spend. ## Budget your spend [#budget-your-spend] List every endpoint you will call in production and its expected volume, and price it with [Endpoint pricing](/docs/endpoint-pricing). For metered endpoints, budget the ceiling. `dry_run=1` returns the range in `data.dry_run` (`credits_min`, `credits_max`) with `charge_reason: "dry_run"`, and charges nothing. A call whose ceiling your balance cannot cover fails with `402 INSUFFICIENT_CREDITS` before it runs, and nothing is charged. Read `credits_remaining` from each response, or poll `GET /v1/credits` (free), and alert on your own threshold. Remember that exports on the same account draw on the same balance. See [Credits](/docs/credits). ## Pin the response shape [#pin-the-response-shape] From October 4, 2026 every key gets [schema 2](/docs/schema-2). Before you ship: * Check `schema_version` is `"2"` on every success (the wrapper above does). * Read `unavailable` on every response. It lists fields, as paths into `data`, that this platform normally fills but this response could not. Log it, and decide per field whether a gap is acceptable. * Treat every id as a string and every time as ISO 8601 UTC. * Keep a default branch wherever you switch on `post.kind`. New values may be added. * Send no parameter that schema 2 refuses. They answer `400 UNSUPPORTED_PARAMETER`. * Treat beta endpoints (metrics, untyped lists) as loosely typed. They keep the source's own structure under our naming rules (snake\_case keys, string ids, ISO times where the source gives a timestamp), can carry the source's own figures, and their fields may change. On `profile/full`, parse `data.profile` as a profile and `data.posts[].post` as posts, and treat only its other keys as loosely typed. ## Log what support needs [#log-what-support-needs] For every attempt, record: * `request_id` (also the `X-Request-Id` header) * the endpoint and HTTP status * `error.type` on failures * `credits_used` and `charge_reason` * `cached` * `unavailable`, when it is not empty * your measured latency and the attempt number Never log the API key or full sensitive request data. When you report a problem, include the `request_id` values involved. ## Deployment checklist [#deployment-checklist] * [ ] The production endpoint set and required parameters are fixed. * [ ] Each endpoint has a tested per-attempt timeout. * [ ] Only the transient `error.type` values are retried, with a cap, `Retry-After`, and jittered backoff. * [ ] Each logical call has its own `Idempotency-Key`, reused only for its retries. * [ ] Every success is checked for `"schema_version": "2"`, and no request sends a refused parameter. * [ ] `unavailable` is logged, and each field your product needs has a plan for when it is listed. * [ ] Pagination stops on `has_more: false`, sends the cursor back unchanged with the same parameters, and restarts on `CURSOR_EXPIRED`. * [ ] Call rate and in-flight calls are both bounded below the per-key limits. * [ ] Each endpoint is marked as accepting cached data or requiring a fresh fetch. * [ ] The production key is loaded from a server-side secret store and used nowhere else. * [ ] Expected spend is budgeted from current prices, with an alert on `credits_remaining`. * [ ] `request_id`, status, error type, credits, charge reason, latency and attempt number are logged. * [ ] A clean pre-production run passed, following [Testing your integration](/docs/testing-your-integration). ## Next steps [#next-steps] Check success, failure, pagination, retry and cache paths before launch. Every error type and whether to retry it. Call rate and concurrency per key. Holds, settlement and charge reasons. --- # Quickstart (/docs/quickstart) Get a key, make a call, read the envelope, page through a list and check your balance. Five steps. The first call takes a minute; by the end you will have read four platforms from one script and walked a paginated list. If Claude, Cursor or another agent is writing your integration, see [AI agents](/docs/ai-agents) for what to hand it. ### Get your API key [#get-your-api-key] Sign in and open [API keys](https://www.insightsocial.app/portal/api/keys) in the dashboard. Your first key is created for you the first time you visit the API section; you can make more there at any time. A key starts with `isk_live_`. You can show or copy the full value from the dashboard at any time. Keep it in a secret store rather than in your code, and export it for the examples below: ```bash title="cURL" export INSIGHTSOCIAL_API_KEY="isk_live_..." ``` Keep the key on your server. See [Authentication](/docs/authentication) for rotation and revocation. ### Make your first call [#make-your-first-call] ```bash title="cURL" curl "https://api.insightsocial.app/v1/instagram/profile?handle=nasa" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os import requests res = requests.get( "https://api.insightsocial.app/v1/instagram/profile", params={"handle": "nasa"}, headers={"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}, ) print(res.json()) ``` ```ts title="Node.js" const res = await fetch( "https://api.insightsocial.app/v1/instagram/profile?handle=nasa", { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! } }, ); console.log(await res.json()); ``` No token exchange and no per-platform setup: the header is the whole credential. ### Read the response [#read-the-response] ```json title="Response" { "success": true, "platform": "instagram", "endpoint": "/v1/instagram/profile", "schema_version": "2", "data": { "author": { "id": "528817151", "username": "nasa", "display_name": "NASA", "avatar_url": "https://scontent-phl2-1.cdninstagram.com/…", "bio": "Making the seemingly impossible, possible. ✨", "verified": true, "followers": 104294818, "followers_approximate": null, "following": 90, "posts_count": 4942, "likes_count": null, "url": "https://www.instagram.com/nasa/", "location": null, "external_url": "https://www.nasa.gov", "private": false, "joined_at": null, "last_post_at": "2026-10-03T14:10:18.000Z", "ext": { "business_category": null } } }, "unavailable": [], "credits_used": 0, "credits_remaining": 500, "request_id": "req_d6ad52ab1f9f", "cached": false, "idempotent_replay": false, "charge_reason": "miss", "free_call": true } ``` The payload is under `data`. A profile has the same fields on every platform; a field the platform does not show is `null` (Instagram shows no `likes_count` or `joined_at`). Ids are always strings and times are ISO 8601 in UTC. `schema_version` says which response shape you got, and `unavailable` lists any field this platform normally fills that this response could not; it is empty here. See [Schema 2](/docs/schema-2). `credits_used` is what this call cost and `credits_remaining` is your balance after it. Here `free_call` is `true`: your first 10 calls priced at 100 credits or less cost nothing, so this 10-credit call came out of that allowance. The same numbers are also sent as headers, which is handy for logging: | Header | Carries | | --------------------- | --------------------------------------------------------------- | | `X-Request-Id` | The same value as `request_id`. Quote it to support. | | `X-Credits-Used` | Credits charged for this call. | | `X-Credits-Remaining` | Your balance after the call. Omitted when it could not be read. | Every field is described in [Response schema](/docs/response-schema). ### Read four platforms in one script [#read-four-platforms-in-one-script] Changing platform means changing the path, nothing else. This loop reads the same handle on four platforms, prints the follower count and the running cost, and spends up to 40 credits (less while you still have free calls). ```python title="Python" import os import requests KEY = os.environ["INSIGHTSOCIAL_API_KEY"] BASE = "https://api.insightsocial.app/v1" for path in ["tiktok/profile", "instagram/profile", "youtube/channel", "twitter/profile"]: body = requests.get( f"{BASE}/{path}", params={"handle": "nasa"}, headers={"x-api-key": KEY}, ).json() if not body["success"]: print(path, body["error"]["type"], body["error"]["message"]) continue followers = body["data"]["author"]["followers"] print(f"{path:<20} {followers:>14,} cost {body['credits_used']} left {body['credits_remaining']}") ``` ```ts title="Node.js" const KEY = process.env.INSIGHTSOCIAL_API_KEY!; const BASE = "https://api.insightsocial.app/v1"; for (const path of ["tiktok/profile", "instagram/profile", "youtube/channel", "twitter/profile"]) { const res = await fetch(`${BASE}/${path}?handle=nasa`, { headers: { "x-api-key": KEY }, }); const body = await res.json(); if (!body.success) { console.log(path, body.error.type, body.error.message); continue; } console.log( `${path.padEnd(20)} ${body.data.author.followers.toLocaleString().padStart(14)}` + ` cost ${body.credits_used} left ${body.credits_remaining}`, ); } ``` ```bash title="cURL" for path in tiktok/profile instagram/profile youtube/channel twitter/profile; do curl -s "https://api.insightsocial.app/v1/$path?handle=nasa" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" \ | python3 -c "import sys, json; r = json.load(sys.stdin); print('$path', r['data']['author']['followers'], 'cost', r['credits_used'])" done ``` ### Page through a list [#page-through-a-list] List endpoints return a `pagination` object next to `data`. Send `pagination.next_cursor` back as the `cursor` parameter, exactly as you received it, and stop when `has_more` is `false`. This walks the first three pages of NASA's followers on X, at 10 credits a page. ```python title="Python" import os import requests KEY = os.environ["INSIGHTSOCIAL_API_KEY"] URL = "https://api.insightsocial.app/v1/twitter/user/followers" params = {"handle": "nasa"} for page in range(3): body = requests.get(URL, params=params, headers={"x-api-key": KEY}).json() if not body["success"]: print(body["error"]["type"], body["error"]["message"]) break print("page", page + 1, "rows", len(body["data"].get("items", []))) pagination = body.get("pagination") or {} if not pagination.get("has_more"): break params["cursor"] = pagination["next_cursor"] ``` ```ts title="Node.js" const KEY = process.env.INSIGHTSOCIAL_API_KEY!; const url = new URL("https://api.insightsocial.app/v1/twitter/user/followers"); url.searchParams.set("handle", "nasa"); for (let page = 1; page <= 3; page++) { const body = await (await fetch(url, { headers: { "x-api-key": KEY } })).json(); if (!body.success) { console.log(body.error.type, body.error.message); break; } console.log("page", page, "rows", body.data.items?.length ?? 0); if (!body.pagination?.has_more) break; url.searchParams.set("cursor", body.pagination.next_cursor); } ``` Cursors start with `v2c.` and are opaque: do not parse or edit them. Send them back only as `cursor`, with the same other parameters as the first page, within 24 hours. Details in [Pagination](/docs/pagination). ## Check your balance [#check-your-balance] `GET /v1/credits` is free and returns your balance, this month's usage and how many free calls you have left. ```bash title="cURL" curl "https://api.insightsocial.app/v1/credits" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```json title="Response" { "success": true, "credits_used": 0, "credits_remaining": 380, "plan": { "tier": "free", "credits_per_month": 500, "resets_at": "2026-10-01T00:00:00Z" }, "usage": { "window_start": "2026-09-01T00:00:00Z", "used": 120, "used_export": 40, "used_api": 80 }, "pack_credits": 0, "free_calls": { "remaining": 4, "total": 10 }, "key": { "id": "5b0c1e2a-7d4f-4a8e-9c3b-2f6d1a0e8b7c", "name": "production" }, "request_id": "req_1a2b3c4d5e6f" } ``` The balance is shared with InsightSocial export credits, which is why `usage` splits `used_export` from `used_api`. To find endpoints without spending anything, query the catalogue at `GET /v1/endpoints`, which needs no key. ## Make a retry safe [#make-a-retry-safe] Add an `Idempotency-Key` header with a fresh UUID per logical operation. If a timeout makes you send the same request again, the replay comes back with `idempotent_replay: true` and costs 0 credits. ```bash title="cURL" curl "https://api.insightsocial.app/v1/instagram/profile?handle=nasa" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" ``` ## When a call fails [#when-a-call-fails] Failures share the envelope, with `success: false` and an `error` object. Branch on `error.type`. ```json title="Response" { "success": false, "error": { "type": "INSUFFICIENT_CREDITS", "message": "This call needs up to 170 credits and 120 remain. Top up at https://www.insightsocial.app/pricing" }, "request_id": "req_1a2b3c4d5e6f", "credits_used": 0, "credits_remaining": 120 } ``` A failed call is never charged. Retry only `RATE_LIMITED`, `CONCURRENCY_LIMIT`, `IDEMPOTENCY_IN_PROGRESS`, `SERVICE_UNAVAILABLE`, `UPSTREAM_ERROR`, `UPSTREAM_INVALID` and `INTERNAL_ERROR`; the rest fail the same way until you change the request. A `400 UNSUPPORTED_PARAMETER` names the parameter to remove in `error.param`. See [Errors](/docs/errors). ## Next steps [#next-steps] The cursor loop and the endpoints that page differently. Prices, metered endpoints, free calls and the shared cache. Every error type and which ones to retry. What each of the 25 platforms offers. --- # Rate limits (/docs/rate-limits) 60 requests per minute and 10 concurrent requests per API key, what a 429 looks like, and how to size a job. Each API key may send **60 requests per minute** and have **10 requests in flight** at once. Going over either limit returns a `429`, and a rejected request costs nothing. Beyond those two ceilings, what bounds your volume is your credit balance. ## The limits at a glance [#the-limits-at-a-glance] | Limit | Applies to | Value | When exceeded | | ------------ | ----------- | ------------------------------------ | ------------------------------------------------------------------- | | Request rate | Per API key | **60 requests / sliding 60 seconds** | `429 RATE_LIMITED` with `Retry-After` | | Concurrency | Per API key | **10 requests in flight** | `429 CONCURRENCY_LIMIT` with `Retry-After: 1` | | Credits | Per account | Your balance | `402 INSUFFICIENT_CREDITS` | | API keys | Per account | **25 keys** | Key creation is refused. See [Authentication](/docs/authentication) | The dashboard [Playground](https://www.insightsocial.app/portal/api/explorer) has its own allowance with the same numbers, so trying endpoints there does not eat into your keys' limits. ## Request rate: 60 per minute per key [#request-rate-60-per-minute-per-key] Every data request on a key counts toward a sliding 60-second window. That includes requests that end in a `402`, `404` or `405`. A `max_pages` walk is one request and takes one slot. None of these count: `GET /v1/credits`, `GET /v1/endpoints`, the bare `GET /v1`, and any request rejected for a missing or bad key, since authentication runs first. ```json title="Response" { "success": false, "error": { "type": "RATE_LIMITED", "message": "Too many requests on this key. Honour Retry-After, then back off with jitter." }, "request_id": "req_1a2b3c4d5e6f", "credits_used": 0, "credits_remaining": null } ``` `Retry-After` is the number of seconds until the oldest request in the window drops out, at least `1`. ## Concurrency: 10 in flight per key [#concurrency-10-in-flight-per-key] Separately, a key may have at most 10 open requests. This is not a time window: a slot frees the moment one of your requests returns, so an 11th request sent while 10 are open is rejected. ```json title="Response" { "success": false, "error": { "type": "CONCURRENCY_LIMIT", "message": "Too many calls in flight on this key. Retry shortly." }, "request_id": "req_1a2b3c4d5e6f", "credits_used": 0, "credits_remaining": null } ``` `Retry-After` is `1` on this one. The API sends no headers describing your remaining headroom. The only timing header is `Retry-After`. It comes with every `429`, and also with `409 IDEMPOTENCY_IN_PROGRESS` and some `503` responses. Pace yourself from your own counts, as described below. ## Credits are the volume limit [#credits-are-the-volume-limit] There is no daily or monthly request quota. What limits sustained work is your balance: the free plan includes 500 credits a month, Pro 10,000, and packs add more. Prices differ per endpoint and some are metered, so price every endpoint your job uses on [Endpoint pricing](/docs/endpoint-pricing) before you size it. See [Credits](/docs/credits) for how charging works. ## Handling a 429 [#handling-a-429] ### Read `error.type` [#read-errortype] `RATE_LIMITED` means you are sending too fast: slow the request rate. `CONCURRENCY_LIMIT` means too many requests are open at once: shrink the worker pool. They need different fixes. ### Wait for `Retry-After` [#wait-for-retry-after] Wait at least that many seconds before retrying. ### Back off with jitter [#back-off-with-jitter] If the retry also gets a `429`, double the wait and add random jitter so your workers do not all retry in the same instant. ### Cap the attempts [#cap-the-attempts] After a handful of attempts, give up and surface the error. ```ts title="Node.js" async function callWithBackoff(url: string, init: RequestInit, maxRetries = 5): Promise { for (let attempt = 0; ; attempt++) { const res = await fetch(url, init); // Only 429s are handled here. A 402 or another 4xx will not // change on retry, so return it straight away. if (res.status !== 429 || attempt >= maxRetries) return res; const retryAfter = Number(res.headers.get("Retry-After")) || 1; const backoff = retryAfter * 2 ** attempt; const jittered = backoff * (0.5 + Math.random() * 0.5); await new Promise((r) => setTimeout(r, jittered * 1000)); } } ``` The [Errors](/docs/errors) page has complete retry loops in cURL, Python and Node.js that also cover `503` and `500`. ## Sizing a job [#sizing-a-job] * Run at most **10 workers per key**. * Keep the total under **60 requests a minute**, which is one request a second on average. * Budget credits per page, not per job, and remember that metered calls hold the top of their price range while they run. * Walk paginated endpoints until `pagination.has_more` is `false`. See [Pagination](/docs/pagination). * Send an `Idempotency-Key` on each call so retries never pay twice. ## Next steps [#next-steps] Which failures to retry, with backoff loops. What each call costs and what is never charged. Key management. Cursor loops that stay inside the limits. --- # Response schema (/docs/response-schema) The InsightSocial API envelope under schema 2, what sits inside data, pagination, response headers, idempotent replays and fresh fetches. Every data endpoint answers in one success envelope, and every failure in one error envelope, so a single parser handles every platform. The two share only a few fields: **check `success` first**, then read the body as one or the other. The two account endpoints are the exception: `GET /v1/credits` returns a balance body of its own (it still carries `success`, `credits_used` and `credits_remaining`), and `GET /v1/endpoints` returns the catalogue in its own shape. This page describes **schema 2**, the shape every key gets from October 4, 2026. What changed from the previous shape is on [Schema 2](/docs/schema-2). ## Successful response [#successful-response] ```json title="Response" { "success": true, "platform": "instagram", "endpoint": "/v1/instagram/profile", "schema_version": "2", "data": { "author": { "id": "528817151", "username": "nasa", "display_name": "NASA", "followers": 104294818, "followers_approximate": null, "url": "https://www.instagram.com/nasa/", "external_url": "https://www.nasa.gov", "last_post_at": "2026-10-03T14:10:18.000Z" } }, "unavailable": [], "credits_used": 10, "credits_remaining": 9990, "request_id": "req_d6ad52ab1f9f", "cached": false, "idempotent_replay": false, "charge_reason": "miss", "free_call": false } ``` The profile is trimmed here. Every field of each entity is on its [unified schema](/docs/schema/author) page. ### Envelope fields [#envelope-fields] These are the fields of a **successful** response. An error carries far fewer; see [Error response](#error-response). | Field | Type | On every success | Description | | ------------------- | ---------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `success` | `boolean` | yes | `true`. Also on every error, as `false`: branch on it first. | | `platform` | `string` | yes | The platform you called, such as `instagram`. | | `endpoint` | `string` | yes | The path you called, such as `/v1/instagram/profile`. | | `schema_version` | `string` | yes | `"2"`. The contract `data` follows. An error never carries it. | | `data` | `object` | yes | The result. Its shape depends on the endpoint; see [Inside `data`](#inside-data). | | `pagination` | `object` | no | List endpoints only. See [List endpoints](#list-endpoints). | | `unavailable` | `string[]` | yes | Fields this platform normally fills that this response could not, as paths into `data`. Empty when nothing is missing. See [Data availability](/docs/data-availability#unavailable). | | `credits_used` | `integer` | yes | Credits charged for this call. `0` for replays, free calls, empty results, `dry_run=1` and failures. | | `credits_remaining` | `integer` | yes | Your balance after this call. | | `request_id` | `string` | yes | Our identifier for this call, in the form `req_` plus 12 hex characters. | | `cached` | `boolean` | yes | `true` when the result came from the shared cache rather than a live fetch. | | `idempotent_replay` | `boolean` | yes | `true` when this is a replay of an earlier call with the same `Idempotency-Key`. | | `charge_reason` | `string` | yes | Why `credits_used` has the value it has. See below. | | `free_call` | `boolean` | yes | `true` when the call used one of your free calls, so `credits_used` is `0`. | ### `charge_reason` [#charge_reason] | Value | Meaning | Credits | | -------------- | ----------------------------------------------- | ------------------------------------------------------------------ | | `miss` | A live fetch | The endpoint's price, or for a metered endpoint what the call used | | `shared_cache` | Served from the shared cache | 5 | | `replay` | An idempotent replay of a call you already made | 0 | | `no_result` | Nothing to charge for, such as an empty result | 0 | | `dry_run` | A `dry_run=1` price quote. No data was fetched | 0 | The pricing rules are on [Credits](/docs/credits). ## Inside `data` [#inside-data] `data` holds one entity, or a page of them, under a key named after the entity: | Endpoint returns | `data` holds | Schema | | ------------------ | ---------------------- | ----------------------------------------- | | One post | `data.post` | [Post](/docs/schema/post) | | A list of posts | `data.items[].post` | [Post](/docs/schema/post) | | One profile | `data.author` | [Author and profile](/docs/schema/author) | | A list of profiles | `data.items[].author` | [Author and profile](/docs/schema/author) | | One comment | `data.comment` | [Comment](/docs/schema/comment) | | A list of comments | `data.items[].comment` | [Comment](/docs/schema/comment) | | A transcript | `data.transcript` | [Transcript](/docs/schema/transcript) | A list also carries these fields next to `items`: * **`data.total`**: the platform's own total, when it states one (a comment count, for example). It is not the size of this page, and it is `null` when the platform does not say. * **`data.truncated`**: `true` when the list is known to be cut short by the platform, `false` when the platform says it is complete, `null` when we cannot tell. On a Reddit comment tree it is `true` once the tree reaches Reddit's ten-level depth limit and otherwise `null`: Reddit leaves some branches behind "load more" links that the source does not count. To judge how complete a Reddit thread is, compare with the post's `engagement.comments` from `/v1/reddit/post`, which also counts deleted and removed comments. On `/v1/instagram/post/likers` it is `true` when fewer likers came back than the post has likes: Instagram serves one slice of the list, with no next page. * **`data.ext`**: what the page says about itself beyond its rows, under the same conventions as an entity's `ext` (snake\_case keys, string ids, ISO times). On `/v1/instagram/audio/reels`, for example, it carries the sound the reels use (`ext.sound`, with its title, artist and duration) and `ext.is_trending`. Present only when the platform sends something; absent otherwise, so treat it as optional. The same rules hold everywhere inside `data`: * **Ids are strings.** Several platforms use ids above 2^53, which a JSON number would corrupt. Never parse an id as a number. * **Times are ISO 8601 in UTC**, ending in `Z`. * **`null` means not known.** A count the platform hides is `null`, never `0`. `0` means zero. * **`content.media_urls` is always an array** (or `null`), never a bare string. * **`ext` keys are snake\_case.** `ext` holds what one platform carries that the others cannot mean the same way. Treat every `ext` key as optional. Some long-tail endpoints (metrics, and lists with no matching entity) are marked **beta** on their reference page. They return the source's own structure under our naming rules: snake\_case keys, string ids, and ISO times where the source gives a timestamp. Some pass on figures the source itself computes, such as `engagement_rate_percentages` and `likes_per_hour` on `/v1/instagram/engagement` or a `confidence` on `/v1/youtube/video/sponsors`, and a source's own date text (a local `datetime`, a `time_ago`) stays as the source wrote it. Their fields may change. `profile/full` is beta too, but its profile and posts are typed: `data.profile` is a [profile](/docs/schema/author) and each `data.posts[].post` is a [post](/docs/schema/post). It pages its posts with `cursor` like a list. Any other key under its `data` follows the beta rules above. The typed entities above carry no analysis of ours: no engagement rate, labels or relevance scores. Compute what you need from the raw counts; see [Schema 2](/docs/schema-2) for what was removed and the [creator engagement recipe](/docs/recipes/creator-engagement-scoring) for an engagement rate built from `engagement` and the author's `followers`. ### One schema [#one-schema] Every key gets schema 2, and it is the only shape served. An error has no `schema_version` field. See [Schema 2](/docs/schema-2) for what changed from the previous shape. ## Error response [#error-response] An error is a different, smaller body. It has no `platform`, `endpoint`, `schema_version`, `data`, `unavailable`, `cached` or `charge_reason`, so read `success` before anything else: ```json title="Response" { "success": false, "error": { "type": "UNSUPPORTED_PARAMETER", "message": "`relevance` is not supported: it asks for analysis, which is not part of schema 2. Remove it and retry. Nothing was charged.", "param": "relevance" }, "request_id": "req_80d197121a1e", "credits_used": 0, "credits_remaining": null } ``` | Field | Description | | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | `error.type` | A stable string to branch on. | | `error.message` | A readable explanation, often naming what to fix. Do not parse it. | | `error.param` | The query parameter at fault, when there is one. | | `request_id` | Quote it to support. | | `credits_used` | Always `0`: failed calls are never charged. | | `credits_remaining` | Your balance when we know it, otherwise `null` (for example on authentication failures, or on a request refused before we read your balance). | That is the whole body: `success`, `error` (`type`, `message`, and `param` when one parameter is at fault), `request_id`, `credits_used` and `credits_remaining`. ```ts title="Node.js" const body = await res.json(); if (!body.success) { // { error: { type, message, param? }, request_id, credits_used, credits_remaining } throw new Error(`${body.error.type}: ${body.error.message} (${body.request_id})`); } // Only now: body.schema_version, body.data, body.unavailable, body.pagination ``` **Not every error is in your Activity Log.** A request refused before it reaches a platform writes no log row: `400 UNSUPPORTED_PARAMETER`, `400 CURSOR_INVALID` and `CURSOR_EXPIRED`, authentication and rate-limit errors, idempotency conflicts and `402 INSUFFICIENT_CREDITS`. `dry_run=1` quotes are not logged either. Other errors, including the `400` parameter errors a platform returns, are logged with their status. The full list of types is on [Errors](/docs/errors). ## Response headers [#response-headers] | Header | Value | | --------------------- | --------------------------------------------------------------------------------------------- | | `X-Request-Id` | Same as `request_id` in the body. | | `X-Credits-Used` | Credits charged for this call. | | `X-Credits-Remaining` | Your balance after the call. Omitted when it is unknown, never sent as a placeholder `0`. | | `Retry-After` | Seconds to wait. Sent on `429`, on `409 IDEMPOTENCY_IN_PROGRESS` and on some `503` responses. | | `Cache-Control` | `no-store`. Do not cache API responses in a shared proxy. | CORS headers are also set, and `X-Request-Id`, `X-Credits-Used`, `X-Credits-Remaining` and `Retry-After` are exposed to browser code. Keys still belong on your server; see [Authentication](/docs/authentication). ## List endpoints [#list-endpoints] List responses put their rows in `data.items` and carry a `pagination` object at the root of the envelope: ```json title="Response" { "success": true, "platform": "twitter", "endpoint": "/v1/twitter/user/tweets", "schema_version": "2", "data": { "items": [ { "post": { "id": "2106502020849127865", "kind": null, "author": { "id": null, "username": "NASA" } } } ], "total": null, "truncated": null }, "pagination": { "next_cursor": "v2c.M-GEP84JXNZPqhQ0NlEOubhbuFGGiCK9…", "has_more": true, "page_size": 19 }, "unavailable": ["items[].post.author.id"], "credits_used": 10, "credits_remaining": 9980, "request_id": "req_161a29ec13ca", "cached": false, "idempotent_replay": false, "charge_reason": "miss", "free_call": false } ``` Here `unavailable` says that no row on this page carries an author id, though the platform has one. The rows are still valid; plan for `author.id` being `null` on this endpoint. | Field | Description | | ------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | `next_cursor` | Send it back as the `cursor` query parameter to get the next page. Starts with `v2c.`; pass it unchanged. `null` on the last page. | | `has_more` | `false` on the last page. Stop there. | | `page_size` | Rows on this page. | | `stopped_at` | Only on creator post lists called with `since` or `stop_at_id`. Says which boundary ended the walk: `since`, `known_id` or `end`. | The cursor is only valid as `cursor`, with the same endpoint and parameters, for 24 hours. The full loop is on [Pagination](/docs/pagination). ## Idempotent requests [#idempotent-requests] Send an `Idempotency-Key` header to make a call safe to retry. Use a fresh UUID for each logical operation. ```bash title="cURL" curl "https://api.insightsocial.app/v1/tiktok/profile?handle=khaby.lame" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" \ -H "Idempotency-Key: c3f9d27e-81b4-4e06-a5d2-94be0f6c1a78" ``` ```python title="Python" import os import uuid import requests res = requests.get( "https://api.insightsocial.app/v1/tiktok/profile", params={"handle": "khaby.lame"}, headers={ "x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"], "Idempotency-Key": str(uuid.uuid4()), }, ) body = res.json() print(body["idempotent_replay"], body["credits_used"]) ``` ```ts title="Node.js" const res = await fetch( "https://api.insightsocial.app/v1/tiktok/profile?handle=khaby.lame", { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY!, "Idempotency-Key": crypto.randomUUID(), }, }, ); const body = await res.json(); console.log(body.idempotent_replay, body.credits_used); ``` * A replay returns `idempotent_replay: true`, `charge_reason: "replay"` and `credits_used: 0`. * If the original call with the same key is still running, you get `409 IDEMPOTENCY_IN_PROGRESS` with `Retry-After`. Wait, then send the same key again. * Keys are scoped to your account. Reusing a key for a different request, or with a different schema, answers `409 IDEMPOTENCY_KEY_REUSED`. * A key must be at most 255 characters of letters, digits, `.`, `_`, `:` or `-`. A key that does not fit is ignored, and the call runs as an ordinary request. ## Force a fresh fetch [#force-a-fresh-fetch] Repeat calls can be served from the shared cache (`cached: true`) at 2 credits. To skip it and fetch live, send `Cache-Control: no-cache`, or add `fresh=1` to the query: ```bash title="cURL" curl "https://api.insightsocial.app/v1/tiktok/profile?handle=khaby.lame" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" \ -H "Cache-Control: no-cache" ``` A fresh fetch is always charged at the normal price. See [Credits](/docs/credits) for the shared cache. ## Next steps [#next-steps] The cursor loop, identical on every list endpoint. Every error type and whether to retry it. Prices, charge reasons and the shared cache. --- # Schema 2 (/docs/schema-2) The only response shape since October 4, 2026, and what changed from the previous one. Since **October 4, 2026** every API key gets **schema 2**, and it is the only shape served: one shape per entity (post, profile, comment, transcript) on every platform, whichever data source answered. The envelope is the same; what changed is inside `data`. Every response says which schema it follows: ```json { "success": true, "schema_version": "2", "data": { ... }, "unavailable": [] } ``` The previous shape is no longer served. An `InsightSocial-Version` header is ignored, and `/v1/openapi-legacy.json` is gone. ## What changed [#what-changed] | Before | Schema 2 | | ---------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | | `computed` (engagement rate, language, category, estimated reach, `vs_creator`) on every item | Removed. | | `labels`, `relevance`, `label_share`, `labels_evidence`, `creator_baseline`, `comment_recency`, `dropped`, `_warnings` | Removed. What a response could not fill is listed in `unavailable` instead. | | `data.next_cursor` | Removed. Read `pagination.next_cursor`. | | Cursors `is.…` / `is2.…` | Cursors `v2c.…`. Send them back as `cursor`, with the same parameters, within 24 hours. | | Ids sometimes numbers | Always strings. | | Times as ISO strings or epoch numbers | Always ISO 8601 UTC, ending in `Z`. | | `content.media_urls` a string or an array | Always an array. | | Author id in `ext.author_id` (some platforms) | `author.id` on every post and comment, `null` where the source has none. | | Language in `ext.content_language` / `ext.default_language` | `language` on posts and comments. | | `ext.followers_approximate` | `followers_approximate` on profiles. | | Bio link in `ext.bio_link`, or in `url` on Instagram | `external_url`. `url` is always the profile itself. | | `ext` keys in mixed case (YouTube) | snake\_case: `madeForKids` → `made_for_kids`. | | A top-level comment's `parent_id` set to the post id (X, Reddit) | `null`. Replies carry their parent comment's id. | | Facebook `author.username` holding an opaque `pfbid…` id | `author.id`; `username` is `null`. | | Reddit `author.display_name` holding the `t2_…` account id | `author.id`; `display_name` is `null`. | | YouTube comment `ext.replies_token` | Removed. Fetch replies with the comment's `id` as `continuationToken`. | New on posts: `kind`, the format, one of eight values: `text`, `image`, `carousel`, `video`, `short`, `live`, `article` or `link`. It is `null` when the source does not say and the format cannot be read without guessing. New values may be added; keep a default branch. Transcripts have one shape everywhere: `data.transcript` is `{ "language", "text", "segments": [{ "text", "start_seconds", "duration_seconds" }] }`, where it used to be a string on some platforms and a list on others. ## Parameters that now return 400 [#parameters-that-now-return-400] A parameter the endpoint does not have also answers `400 UNSUPPORTED_PARAMETER` (it used to be ignored and the call still charged). The ones below asked for analysis that schema 2 does not carry. They answer `400 UNSUPPORTED_PARAMETER`, with `error.param` naming the parameter, and nothing is charged: `label`, `label_evidence`, `relevance`, `relevance_threshold`, `relevant_to`, `judgments`, `fit`, `goal`, `fit_tokens`, `seen`, `brand`, `brand_description`, `offer`, `exclude` (except on `/v1/pinterest/trends`, where it filters terms), `brief`, `use`, `moments`, `format=stationery`, `q` on `/v1/tiktok/post/transcript` and `/v1/youtube/video/transcript` (it asked a question about the video), and `switch_from` on `/v1/twitter/search/tweets` and `/v1/reddit/search/comments`. Elsewhere `q` and `switch_from` are not refused. `dry_run=1` returns our price quote, charges nothing, and reports `charge_reason: "dry_run"`: ```json { "data": { "dry_run": { "credits_min": 50, "credits_max": 150 } } } ``` `credits_min` is the least one page can cost. `credits_max` is what the call would hold with those parameters (`limit`, `include` and the like), multiplied by `max_pages` when you send it. ## `unavailable` [#unavailable] A list of the fields this platform normally exposes that this response could not fill, as paths into `data`, for example `"items[].post.author.id"`. Empty when nothing is missing. Fields a platform never shows (Instagram share counts, Reddit view counts) are not listed: they are `null` everywhere and described on each platform's page. ## Prices [#prices] Unchanged: a call is still charged what it used. While a metered call runs, the hold on your balance is lower on 24 endpoints, because the analysis they no longer do cannot be charged, and higher on one, `/v1/tiktok/search/users` (up to 1,310 credits), whose real maximum is above what it used to hold. ## The full schema [#the-full-schema] The OpenAPI document at [`/v1/openapi.json`](https://api.insightsocial.app/v1/openapi.json) types every response. --- # Testing your integration (/docs/testing-your-integration) Test against the live API without surprises. What is free, what is billed, and which paths to check before launch. Begin with a call that costs nothing. It proves your key works and records your starting balance before any billed test. ```bash title="cURL" curl --include "https://api.insightsocial.app/v1/credits" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```json title="Response" { "success": true, "credits_used": 0, "credits_remaining": 500, "plan": { "tier": "free", "credits_per_month": 500, "resets_at": "2026-10-01T00:00:00.000Z" }, "usage": { "window_start": "2026-09-01T00:00:00.000Z", "used": 0, "used_export": 0, "used_api": 0 }, "pack_credits": 0, "free_calls": { "remaining": 10, "total": 10 }, "key": { "id": "3f6c2a8e-7b41-4d0f-9a52-1c8e5d7b9f20", "name": "staging" }, "request_id": "req_1a2b3c4d5e6f" } ``` Check for status `200`, `success: true` and `credits_used: 0`. Keep the `request_id` in your test log. ## There is no sandbox [#there-is-no-sandbox] Every test runs against the real API, with the same prices, caching and rate limits as production. Keys come in two forms, `isk_live_…` and `isk_test_…`. They behave identically: an `isk_test_` key reaches the same data and **spends real credits** from the same balance. The prefix is a label for your own bookkeeping, not a test mode. Use fixtures for parser tests that must be deterministic, and a small number of budgeted live calls to prove the end-to-end path works today. Neither proves that a source will be available tomorrow. ## Use a separate test key [#use-a-separate-test-key] Create a key just for development or staging at [Dashboard → API keys](https://www.insightsocial.app/portal/api/keys) and keep it apart from production. Store it as `INSIGHTSOCIAL_API_KEY` in a server-side secret. Keep it out of source control, browser code, snapshots and logs. A separate key lets you revoke it without touching production, and it has its own rate limits. It does not have its own budget: keys cannot be capped, and every key spends from the account balance. Record the balance before and after each test run, and revoke the key if it ever shows up in test output. See [Authentication](/docs/authentication). ## Free checks first [#free-checks-first] These cost nothing: | Check | What it proves | | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `GET /v1/endpoints` | The catalogue, prices and parameters. Needs no key, so it does not test yours. | | `GET /v1/credits` | Your key is valid. Returns balance, plan and free calls left. | | `dry_run=1` on any data endpoint | Returns the price range in `data.dry_run` (`credits_min`, `credits_max`) without running the call. `credits_max` includes any `max_pages` you send. `charge_reason` is `dry_run`. | | Any call that returns an error | Errors are never charged. That includes `400 UNSUPPORTED_PARAMETER`, so sending a parameter schema 2 refuses is a free way to find it. | A `dry_run=1` call looks like this: ```json title="Response" { "success": true, "platform": "instagram", "endpoint": "/v1/instagram/profile/posts", "schema_version": "2", "data": { "dry_run": { "credits_min": 10, "credits_max": 130 } }, "unavailable": [], "credits_used": 0, "credits_remaining": 480, "request_id": "req_2ac2c2b769ef", "cached": false, "idempotent_replay": false, "charge_reason": "dry_run", "free_call": false } ``` Then make one real call to check auth, transport and your parser against the live service: ```bash title="cURL" curl --include "https://api.insightsocial.app/v1/instagram/profile?handle=natgeo" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` This endpoint costs 10 credits. On a new account it may use one of your 10 lifetime free calls instead (`free_call: true`, `credits_used: 0`). Read `credits_used` and `charge_reason` rather than assuming what was billed, and keep live calls out of unit tests and pull-request loops. Check that the body says `"schema_version": "2"`: that is the contract these docs describe. See [Schema 2](/docs/schema-2). ## Try calls in the Playground [#try-calls-in-the-playground] The [Playground](https://www.insightsocial.app/portal/api/explorer) in your dashboard runs any endpoint from the browser, signed in as you, with no key to paste. Use it to see real responses and prices before you write code. Its calls are real and are charged exactly like API calls. ## Test fixtures, nulls and unavailable fields [#test-fixtures-nulls-and-unavailable-fields] Keep small response fixtures in your own test suite, with keys and unneeded personal data removed. This one is trimmed from a real X response and covers the states a parser must tell apart: ```json title="Success fixture" { "success": true, "platform": "twitter", "endpoint": "/v1/twitter/user/tweets", "schema_version": "2", "data": { "items": [ { "post": { "id": "2106502020849127865", "url": "https://x.com/NASA/status/2106502020849127865", "kind": null, "content": { "text": "RT @Astro_Jessica: Room with a view. …", "media_urls": null, "thumbnail_url": null, "duration_seconds": null }, "author": { "id": null, "username": "NASA", "display_name": "NASA", "avatar_url": "https://pbs.twimg.com/…", "verified": true }, "engagement": { "views": 40169, "likes": 0, "comments": 0, "shares": 572, "saves": 0 }, "flags": { "nsfw": null, "spoiler": null, "pinned": false, "deleted": false, "likes_hidden": null, "comments_hidden": null, "shares_hidden": null, "views_hidden": null, "saves_hidden": null }, "published_at": "2026-10-03T21:49:53.000Z", "language": "en", "ext": { "author_followers": 92378243, "quote_count": 0 } } } ], "total": null, "truncated": null }, "pagination": { "next_cursor": "v2c.M-GEP84JXNZPqhQ0…", "has_more": true, "page_size": 19 }, "unavailable": ["items[].post.author.id"], "credits_used": 10, "credits_remaining": 490, "request_id": "req_161a29ec13ca", "cached": false, "idempotent_replay": false, "charge_reason": "miss", "free_call": false } ``` Assert each case on its own: 1. `schema_version` is `"2"`. Fail the test on anything else, so a key that is still on the old shape is caught early. 2. A present value, such as `post.author.username`. 3. A `null`, such as `post.kind` here: the field exists but this source did not say. `null` means "not known"; `0` means zero. 4. A field listed in `unavailable`. Here `items[].post.author.id` is `null` on every row of the page, and the response says so. Record `unavailable` per call instead of judging completeness from nulls. 5. `ext`: platform-specific keys in snake\_case. Check that a key exists before reading it. 6. Ids are strings, even when they look like numbers. Keep them as strings: several platforms use ids above 2^53. 7. Times are ISO 8601 in UTC, ending in `Z`. 8. `post.kind` is one of `text`, `image`, `carousel`, `video`, `short`, `live`, `article`, `link`, or `null`. New values may be added, so your code needs a default branch. See [Response schema](/docs/response-schema) for the full envelope. For lists, test a one-page and a multi-page walk: send `pagination.next_cursor` back unchanged as `cursor` and stop on `has_more: false`. See [Pagination](/docs/pagination). Keep error fixtures too, and branch on `error.type`: ```json title="Error fixture" { "success": false, "error": { "type": "RATE_LIMITED", "message": "Too many requests on this key. Honour Retry-After, then back off with jitter." }, "request_id": "req_9a8b7c6d5e4f", "credits_used": 0, "credits_remaining": null } ``` Add one for every error type you handle: at least `INVALID_API_KEY`, `INSUFFICIENT_CREDITS`, `RATE_LIMITED`, `CONCURRENCY_LIMIT` and `SERVICE_UNAVAILABLE`. Schema 2 adds five more worth a fixture each: | `error.type` | Status | When | | ------------------------ | ------ | --------------------------------------------------------------------------------------- | | `UNSUPPORTED_PARAMETER` | 400 | A parameter that asked for analysis. `error.param` names it. Nothing is charged. | | `CURSOR_INVALID` | 400 | The cursor was not issued by this API, is malformed, or belongs to a different request. | | `CURSOR_EXPIRED` | 400 | The cursor is more than 24 hours old. Start again without one. | | `UPSTREAM_INVALID` | 503 | The source returned a body we could not serve. Nothing is charged. Retry. | | `IDEMPOTENCY_KEY_REUSED` | 409 | An `Idempotency-Key` reused with different parameters, or across schemas. | ````json title="UNSUPPORTED_PARAMETER fixture" { "success": false, "error": { "type": "UNSUPPORTED_PARAMETER", "message": "`relevance` is not supported: it asks for analysis, which is not part of schema 2. Remove it and retry. Nothing was charged.", "param": "relevance" }, "request_id": "req_80d197121a1e", "credits_used": 0, "credits_remaining": null } ``` See [Error handling](/docs/errors) and the retry wrapper in the [Production checklist](/docs/production-checklist). ## Test repeat calls and forced fetches Send the same call twice: ```bash title="cURL" curl "https://api.insightsocial.app/v1/instagram/profile?handle=natgeo" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" curl "https://api.insightsocial.app/v1/instagram/profile?handle=natgeo" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ```` Each call may come back as a `miss` at the full price, or as `shared_cache` (`cached: true`) at 2 credits. Both calls are charged: a repeat is a new call. Assert on the `charge_reason` and `credits_used` you actually receive. See [Caching](/docs/caching). Test a forced fetch only when you need one, and budget it at the endpoint's full price: ```bash title="cURL" curl "https://api.insightsocial.app/v1/instagram/profile?handle=natgeo" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" \ -H "Cache-Control: no-cache" ``` A successful forced fetch skips the shared cache: assert `charge_reason: "miss"` and the normal charge. ## Test retries with an Idempotency-Key [#test-retries-with-an-idempotency-key] Send the same call twice with one key: ```bash title="cURL" IDEMPOTENCY_KEY="$(uuidgen)" curl "https://api.insightsocial.app/v1/instagram/profile?handle=nasa" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" \ -H "Idempotency-Key: $IDEMPOTENCY_KEY" curl "https://api.insightsocial.app/v1/instagram/profile?handle=nasa" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" \ -H "Idempotency-Key: $IDEMPOTENCY_KEY" ``` The second response should have `idempotent_replay: true`, `charge_reason: "replay"` and `credits_used: 0`. If the second request arrives while the first is still running, expect `409 IDEMPOTENCY_IN_PROGRESS`; wait for `Retry-After` and send it again. Use a new key for every distinct call. A key first used with one schema answers `409 IDEMPOTENCY_KEY_REUSED` when reused with the other. See [Production checklist](/docs/production-checklist#make-retries-safe). ## Pre-production checklist [#pre-production-checklist] * [ ] The test key is separate from production, and the balance is recorded before and after each run. * [ ] The suite lists each endpoint, its current price, and the most calls it will make. * [ ] Free checks (`/v1/credits`, `/v1/endpoints`, `dry_run=1`) run before any billed call. * [ ] Every response is checked for `"schema_version": "2"`. * [ ] Success and error fixtures cover values, `null`, absent `ext` keys, `unavailable` entries, string ids, ISO times and an unknown `kind`. * [ ] No request sends a parameter that schema 2 refuses. See [Schema 2](/docs/schema-2#parameters-that-now-return-400). * [ ] Pagination tests stop on `has_more: false`, return the cursor unchanged with the same parameters, and restart on `CURSOR_EXPIRED`. * [ ] Retry tests branch on `error.type`, use bounded delays, and resend the same cursor. * [ ] Repeat-call tests check `charge_reason`, `cached` and `credits_used`. * [ ] Forced fetches are budgeted as billed calls. * [ ] Idempotency tests replay one identical call and use a new key for anything else. * [ ] Logs tie together status, `request_id`, `error.type`, charge reason, credits and attempt count. * [ ] Production keys never enter the test environment or its output. ## Next steps [#next-steps] Turn these tests into release gates. The cost of every call in your test plan. The success and error envelopes your fixtures should match. --- # API reference (/docs/api-reference) Every endpoint, with its parameters, its price and a request you can send from the page. Every data endpoint is a `GET` on `https://api.insightsocial.app/v1/{platform}/{resource}` with your key in the `x-api-key` header. Each page lists the parameters, the price in credits and the response, and its request panel sends a real call with your key. There is no sandbox. A request sent from these pages is charged like any other call. Free ways to check a call first are on [Testing your integration](/docs/testing-your-integration). Your balance, and the public endpoint catalogue. 36 endpoints. 37 endpoints. 29 endpoints. 67 endpoints. 15 endpoints. 6 endpoints. 26 endpoints. 14 endpoints. 6 endpoints. Using an AI agent? Give it `https://www.insightsocial.app/docs/api-reference.md`: every endpoint on one Markdown page, with its path and required parameters. See [Using an AI agent](/docs/ai-agents). The whole table of prices is on [Endpoint pricing](/docs/endpoint-pricing). The same catalogue as JSON, free and without a key: `GET https://api.insightsocial.app/v1/endpoints`. --- # Bluesky (/docs/platforms/bluesky) Bluesky API reference: 4 endpoints for profiles, posts and post search. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Profiles, posts, a user's posts and post search on Bluesky. Pass handles as they appear, e.g. `espn.com`. Base path: `/v1/bluesky/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/bluesky/profile?handle=espn.com" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] Every endpoint here has a fixed price. | Endpoint | Credits | What it returns | Key parameters | | ------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | | [`/profile`](/docs/api-reference/bluesky/profile) | 10 | Returns a Bluesky user's public profile: handle, display name, avatar, description, follower and follows counts, post count, created date, and verification status. | `handle` | | [`/user/posts`](/docs/api-reference/bluesky/user-posts) | 10 | Returns a feed of posts for a Bluesky user: text, author info, embeds, reply/repost/like/quote counts, and when each was indexed. | `handle`, `user_id` | | [`/post`](/docs/api-reference/bluesky/post) | 10 | Returns one Bluesky post: its record text, author info, embeds, reply count, repost count, like count, quote count, and a threaded replies array. | `url` | | [`/search`](/docs/api-reference/bluesky/search) | 10 | Returns Bluesky posts matching a keyword, up to 100 per page, each with its text, author, like/comment/share counts, and when it was written. | `query` | --- # Douyin (/docs/platforms/douyin) Douyin API reference: 8 endpoints for creators, videos, comments, search and trending. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Creator profiles, videos, comments and replies, creator and video search, and the hot-search board. Base path: `/v1/douyin/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/douyin/profile?url=https%3A%2F%2Fwww.douyin.com%2Fuser%2FMS4wLjABAAAAtxsy7VmVkU3RN9oIX0vdkh_6-LlQAb0gwI-tDf-bYNg" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] 5 of these are **metered**: the price depends on how much the call reads. See [Credits](/docs/credits). 1 are **beta**: they return the source's own structure under our naming rules (snake\_case keys, string ids, ISO times where the source gives a timestamp) rather than a schema-2 entity, and their fields may change. See [Schema 2](/docs/schema-2). | Endpoint | Credits | What it returns | Key parameters | | ---------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | | [`/search`](/docs/api-reference/douyin/search) | 50–1250 | Returns Douyin videos matching a keyword: caption, creator, likes, comments, shares, saves, hashtags, music, cover image, and duration. | `query` | | [`/profile`](/docs/api-reference/douyin/profile) | 60 | Returns a Douyin creator's profile: nickname, Douyin ID, bio, avatar, follower and following counts, total likes received, video count, region, and verification. | `handle`, `url`, `id` | | [`/profile/posts`](/docs/api-reference/douyin/profile-posts) | 50–1250 | Returns a Douyin creator's recent videos, newest first: caption, likes, comments, shares, saves, hashtags, music, and cover image. | `handle`, `url`, `id` | | [`/post`](/docs/api-reference/douyin/post) | 100 | Returns one Douyin video: caption, creator, likes, comments, shares, saves, hashtags, music, duration, dimensions, cover image, and tagged place when set. | `url` | | [`/post/comments`](/docs/api-reference/douyin/post-comments) | 20–1000 | Returns top-level comments on a Douyin video: text, commenter, like count, reply count, IP region, and whether the creator liked it. | `url` | | [`/comment/replies`](/docs/api-reference/douyin/comment-replies) | 50–2500 | Returns the reply thread under one Douyin comment: text, replier, like count, IP region, and verification, each tagged with the parent comment id. | `url`, `comment_id` | | [`/search/users`](/docs/api-reference/douyin/search-users) | 50–1250 | Returns Douyin creators matching a keyword: nickname, Douyin ID, bio, follower count, total likes received, avatar, profile URL, and verification label. | `query` | | [`/trending`](/docs/api-reference/douyin/trending) | 250 | Returns the live Douyin hot-search board: roughly 50 trending topics with rank, hot value, view count, video count, and the time each topic broke. *Beta.* | — | --- # Facebook (/docs/platforms/facebook) Facebook API reference: 29 endpoints for pages, posts, comments, groups, Marketplace and the Meta Ad Library. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Pages and profiles, posts, comments, groups, reels, photos, events, Marketplace, search and the Meta Ad Library. New to the API? Read the [Facebook scraper API overview](/social-media-api/facebook) for what it returns and how it compares with the official API. Base path: `/v1/facebook/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/facebook/profile?url=https%3A%2F%2Fwww.facebook.com%2FMeta" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] 8 of these are **metered**: the price depends on how much the call reads. See [Credits](/docs/credits). 6 are **beta**: they return the source's own structure under our naming rules (snake\_case keys, string ids, ISO times where the source gives a timestamp) rather than a schema-2 entity, and their fields may change. See [Schema 2](/docs/schema-2). | Endpoint | Credits | What it returns | Key parameters | | ------------------------------------------------------------------------------------------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | | [`/profile`](/docs/api-reference/facebook/profile) | 10 | Returns a Facebook page's public profile: page id, display name, profile image, bio, follower count, and page-like count. | `url` | | [`/profile/posts`](/docs/api-reference/facebook/profile-posts) | 10–40 | Returns recent posts from a Facebook page or profile, each with the post text, like and comment counts, the per-reaction breakdown, media, and publish time. | `url`, `pageId` | | [`/post`](/docs/api-reference/facebook/post) | 10 | Returns one Facebook post in full: the post text, like, comment and share counts, a reactions breakdown, media attachments, and author info. | `url` | | [`/post/comments`](/docs/api-reference/facebook/post-comments) | 10 | Returns comments on a Facebook post, each with the commenter's name, comment text, like count, reply count, and creation time. | `url`, `feedback_id` | | [`/group/posts`](/docs/api-reference/facebook/group-posts) | 10 | Returns posts from a Facebook group, 3 to 4 per page, each with the post text, reaction count, comment count, the per-reaction breakdown, and author info. | `url`, `group_id` | | [`/group`](/docs/api-reference/facebook/group) | 10 | Returns a Facebook group's public record: name, description, member count, privacy, visibility, and creation date. | `url`, `group_id` | | [`/post/transcript`](/docs/api-reference/facebook/post-transcript) | 100 | Returns the spoken words of a Facebook video post as a transcript, using Facebook's auto-generated captions. | `url` | | [`/profile/photos`](/docs/api-reference/facebook/profile-photos) | 10–90 | Returns a page or profile's photos, each with a photo id, permalink, full-size image URL, thumbnail, and accessibility caption when available. | `url` | | [`/profile/reels`](/docs/api-reference/facebook/profile-reels) | 10 | Returns a page or profile's reels, each with a reel id, description, thumbnail, permalink, publish time, and a rounded public view count. | `url` | | [`/adlibrary/ad`](/docs/api-reference/facebook/adlibrary-ad) | 50 | Returns one ad from the Facebook Ad Library: creative, CTA and link, run dates, display format, and the advertiser's page URL, likes and categories. | `id`, `url` | | [`/adlibrary/company/ads`](/docs/api-reference/facebook/adlibrary-company-ads) | 50–350 | Returns the ads one company or page is running in the Facebook Ad Library, each with its creative, status, spend, and targeting info. | `pageId`, `companyName` | | [`/adlibrary/search/ads`](/docs/api-reference/facebook/adlibrary-search-ads) | 50 | Returns Facebook Ad Library ads matching a keyword, each with its creative text, images, sponsor info, and running status. *Beta.* | `query` | | [`/adlibrary/search/companies`](/docs/api-reference/facebook/adlibrary-search-companies) | 50 | Returns advertiser pages in the Facebook Ad Library matching a name, each with its page id, name, handle, verification badge and follower count. | `query` | | [`/profile/events`](/docs/api-reference/facebook/profile-events) | 10–90 | Returns a Facebook page's upcoming and past events, each with a title, link, the start time, the venue, the city, the rendered time line, and the cancelled, past and online flags. | `url` | | [`/post/comment/replies`](/docs/api-reference/facebook/post-comment-replies) | 10 | Returns the replies under a single Facebook comment, each with the author name, reply text, like count, and creation time. | `feedback_id`, `expansion_token` | | [`/marketplace/location/search`](/docs/api-reference/facebook/marketplace-location-search) | 10 | Returns Facebook Marketplace locations and cities matching a search term, each with its lat and lng coordinates. *Beta.* | `query` | | [`/marketplace/search`](/docs/api-reference/facebook/marketplace-search) | 10 | Returns Facebook Marketplace listings near a lat/lng, each with title, price, location, photo, delivery types, and sold or live flags. *Beta.* | `query`, `lat`, `lng` | | [`/marketplace/item`](/docs/api-reference/facebook/marketplace-item) | 10 | Returns one Facebook Marketplace listing: title, description, price, location, condition, photos, seller, and availability flags. | `id`, `url` | | [`/events/search`](/docs/api-reference/facebook/events-search) | 10 | Returns public Facebook events matching a keyword, each with name, date sentence, venue, cover photo, interested and going counts, and online and past flags. *Beta.* | `query` | | [`/events`](/docs/api-reference/facebook/events) | 10–130 | Returns the events listed on a Facebook city or region events page, each with a title, link, cover image, the start time, the venue, and the going and interested counts. | `url` | | [`/event/details`](/docs/api-reference/facebook/event-details) | 10 | Returns one Facebook event in full: title, description, start and end time, location, host, cover image, and RSVP counts when shown. | `id`, `url` | | [`/adlibrary/ad/transcript`](/docs/api-reference/facebook/adlibrary-ad-transcript) | 100 | Returns the spoken words of a Facebook Ad Library video ad, from Facebook's captions when available or transcribed from the video itself. | `id`, `url` | | [`/profile/full`](/docs/api-reference/facebook/profile-full) | 50 | Returns a page's profile plus its latest 3 posts in one call. *Beta.* | `url` | | [`/profile/reels/full`](/docs/api-reference/facebook/profile-reels-full) | 50–250 | Returns a page's reels with exact per-reel engagement merged in: views, likes, comments, and shares, plus each reel's id, thumbnail, and link. *Beta.* | `url` | | [`/search/posts`](/docs/api-reference/facebook/search-posts) | 10–50 | Returns public Facebook posts matching a keyword, each with text, permalink, publish time, like count, comment count, and author. | `query` | | [`/search/pages`](/docs/api-reference/facebook/search-pages) | 10 | Returns Facebook pages matching a keyword, each with page id, display name, permalink, avatar, and verified flag. | `query` | | [`/search/people`](/docs/api-reference/facebook/search-people) | 10 | Returns Facebook people matching a keyword, each with profile id, display name, permalink, avatar, and verified flag. | `query` | | [`/search/videos`](/docs/api-reference/facebook/search-videos) | 10 | Returns public Facebook videos matching a keyword, each with video id, caption, permalink, thumbnail, and author. | `query` | | [`/search/groups`](/docs/api-reference/facebook/search-groups) | 10–110 | Returns public Facebook groups matching your keywords, one row per group, with the group's name, id and URL in the same shape the group endpoints take. | `query` | --- # Instagram (/docs/platforms/instagram) Instagram API reference: 36 endpoints for profiles, posts, reels, comments, followers and hashtag search. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Profiles, posts, reels, comments, stories, highlights, followers and following, hashtag, location and music search, and transcripts. Pass handles without the leading `@`. New to the API? Read the [Instagram scraper API overview](/social-media-api/instagram) for what it returns and how it compares with the official API. Base path: `/v1/instagram/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/instagram/profile?handle=instagram" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] 14 of these are **metered**: the price depends on how much the call reads. See [Credits](/docs/credits). 10 are **beta**: they return the source's own structure under our naming rules (snake\_case keys, string ids, ISO times where the source gives a timestamp) rather than a schema-2 entity, and their fields may change. See [Schema 2](/docs/schema-2). | Endpoint | Credits | What it returns | Key parameters | | ----------------------------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | | [`/profile`](/docs/api-reference/instagram/profile) | 10 | Returns an Instagram account's public profile: bio, exact integer follower count on author.followers, following count, picture URL, verification, and author.ext.public\_email from the bio. | `handle` | | [`/profile/about`](/docs/api-reference/instagram/profile-about) | 10 | Returns Instagram's "About this account" details for a public account (country, month joined, published contact) plus its follower count and lifetime post count. | `handle` | | [`/profile/posts`](/docs/api-reference/instagram/profile-posts) | 10–130 | Returns a user's recent posts with caption, like and comment counts, media URL, media type, and timestamp; share counts are not included. | `handle` | | [`/post`](/docs/api-reference/instagram/post) | 10 | Returns one post's details: caption, view count on videos and reels, like and comment counts, media URLs, media type, the author, and any tagged users. | `url` | | [`/post/comments`](/docs/api-reference/instagram/post-comments) | 50–150 | Returns a post's comments, each with its text, author, like count, reply count, and time, ranked by popularity by default or newest first. | `url` | | [`/comment`](/docs/api-reference/instagram/comment) | 50–150 | Returns one comment's current text, like count, reply count, author, and timestamp, plus the post's total comment count for context. | `comment_url`, `post_url` | | [`/basic-profile`](/docs/api-reference/instagram/basic-profile) | 10 | Returns a minimal Instagram profile looked up by numeric user id: username, full name, and profile picture. | `userId` | | [`/profile/reels`](/docs/api-reference/instagram/profile-reels) | 10–140 | Returns a user's reels with view, like and comment counts and a thumbnail; share counts are not included here. | `user_id`, `handle` | | [`/highlights`](/docs/api-reference/instagram/highlights) | 10 | Returns a user's saved story highlight collections, each with its title, cover image, and the number of items inside. | `user_id`, `handle` | | [`/highlight/detail`](/docs/api-reference/instagram/highlight-detail) | 10 | Returns the individual stories saved inside one highlight, with their media URLs, timestamps, and interaction counts. | `id` | | [`/search/reels`](/docs/api-reference/instagram/search-reels) | 10–650 | Returns reels matching a keyword, each with its view count, like count, and author; share and save counts are not included on this endpoint. | `query` | | [`/media/transcript`](/docs/api-reference/instagram/media-transcript) | 100 | Returns the spoken words in an Instagram video or reel of up to 2 minutes as text. | `url` | | [`/media/screen-text`](/docs/api-reference/instagram/media-screen-text) | 50 | Returns the text shown on an Instagram post's images, read by AI: the photo, each carousel slide up to 10, or a reel's cover frame, with results per slide. *Beta.* | `url` | | [`/audio/reels`](/docs/api-reference/instagram/audio-reels) | 10 | Returns reels that use one specific audio track, each with its engagement counts and the account that posted it. | `audio_id` | | [`/search/hashtag`](/docs/api-reference/instagram/search-hashtag) | 50 | Returns public posts carrying a hashtag, each with shortcode, URL, caption, media URLs, engagement counts, and the author; share and save counts are not included. | `hashtag` | | [`/search/profiles`](/docs/api-reference/instagram/search-profiles) | 10–250 | Returns public profiles matching a keyword, each with username, display name, bio, follower and following counts, post count, and profile URL. | `query` | | [`/reels/trending`](/docs/api-reference/instagram/reels-trending) | 50 | Returns reels from Instagram's public trending page, each with shortcode, URL, caption, media URLs, engagement counts where shown, and the account. | — | | [`/followers`](/docs/api-reference/instagram/followers) | 50–100 | Returns the accounts that follow a user, each with username, display name, avatar URL, verification status, and profile URL. | `handle`, `user_id` | | [`/following`](/docs/api-reference/instagram/following) | 50–100 | Returns the accounts a user follows, each with username, display name, avatar URL, verification status, and profile URL. | `handle`, `user_id` | | [`/similar`](/docs/api-reference/instagram/similar) | 50–850 | Returns the accounts Instagram suggests as similar to a given user, each with username, display name, avatar URL, verification status, and URL. | `handle`, `user_id` | | [`/post/likers`](/docs/api-reference/instagram/post-likers) | 50 | Returns up to about 2,000 of the accounts that liked a post, each with username, display name, avatar, and verification status, plus the full like count, in one call with no further pages. | `url` | | [`/post/stats`](/docs/api-reference/instagram/post-stats) | 50–90 | Returns one post's engagement: likes, comments, and, on video posts only, the play count and the share count shown by the paper plane icon. | `url` | | [`/tagged`](/docs/api-reference/instagram/tagged) | 50 | Returns posts that tag a given user, each with its shortcode, URL, caption, media URLs, engagement counts, and the account that posted it. | `handle`, `user_id` | | [`/location/posts`](/docs/api-reference/instagram/location-posts) | 50 | Returns posts tagged at a place, newest first, about 60 a page, each with shortcode, URL, caption, media URLs, engagement counts, and the author. | `location_id` | | [`/engagement`](/docs/api-reference/instagram/engagement) | 50 | Returns an engagement report for an account: its engagement rate over the recent posts, follower count, total likes and comments, and a per-post breakdown. *Beta.* | `handle` | | [`/search/location`](/docs/api-reference/instagram/search-location) | 50 | Returns places matching a keyword, each with its location id, name, and coordinates, plus a display title and subtitle. *Beta.* | `query` | | [`/search`](/docs/api-reference/instagram/search) | 10 | Returns mixed Instagram search matches for a query: accounts, hashtags, and places in one payload. *Beta.* | `query` | | [`/search/popular`](/docs/api-reference/instagram/search-popular) | 10–130 | Returns popular Instagram posts matching a keyword, each with caption, play count, permalink, thumbnail, and the owner's username. | `query` | | [`/post/comment/replies`](/docs/api-reference/instagram/post-comment-replies) | 10 | Returns replies under one Instagram comment, each with text, like count, nested reply count, author, and timestamp. | `url`, `comment_id` | | [`/username-suggestions`](/docs/api-reference/instagram/username-suggestions) | 50 | Returns suggested available Instagram usernames built from a keyword you supply. *Beta.* | `query` | | [`/search/music`](/docs/api-reference/instagram/search-music) | 50 | Returns audio tracks matching a keyword, each with artist, title, audio and cover artwork URLs, duration, and usage details. *Beta.* | `query` | | [`/stories`](/docs/api-reference/instagram/stories) | 50 | Returns the stories a user has live right now, each with its id, image or video URLs, thumbnail, duration, capture time, and the author. | `handle`, `user_id` | | [`/story/download`](/docs/api-reference/instagram/story-download) | 50 | Returns the full resolution image or video URLs and metadata for one specific story. *Beta.* | `user_id`, `story_id` | | [`/profile/full`](/docs/api-reference/instagram/profile-full) | 50 | Returns the profile plus its latest 12 posts in one call. *Beta.* | `handle` | | [`/profile/reels/full`](/docs/api-reference/instagram/profile-reels-full) | 50–250 | Returns a user's reels with views, likes, comments, and a per-reel share count where the second source exposes one, plus a coverage figure. *Beta.* | `handle`, `user_id` | | [`/profile/posts/full`](/docs/api-reference/instagram/profile-posts-full) | 50–250 | Returns a user's recent posts with likes, comments, views, and a per-post share count where the second source exposes one, plus a coverage figure. *Beta.* | `handle`, `user_id` | --- # Kick (/docs/platforms/kick) Kick API reference: 1 endpoints for clips. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Clip details. Base path: `/v1/kick/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/kick/clip?url=https%3A%2F%2Fkick.com%2Fxqc%2Fclips%2Fclip_abc123" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] Every endpoint here has a fixed price. | Endpoint | Credits | What it returns | Key parameters | | ---------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------- | -------------- | | [`/clip`](/docs/api-reference/kick/clip) | 10 | Returns one Kick clip's title, view count, duration, category, creator name, channel name, thumbnail URL, and creation timestamp. | `url` | --- # Kwai (/docs/platforms/kwai) Kwai API reference: 3 endpoints for profiles and posts. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Profiles, posts and a user's posts. Base path: `/v1/kwai/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/kwai/profile?handle=KwaiBrasilOficial" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] Every endpoint here has a fixed price. | Endpoint | Credits | What it returns | Key parameters | | ---------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------- | --------------- | | [`/profile`](/docs/api-reference/kwai/profile) | 10 | Returns a Kwai user's public profile: username, bio, avatar, verification status, and follower, following, like, and post counts. | `handle`, `url` | | [`/user/posts`](/docs/api-reference/kwai/user-posts) | 10 | Returns a paginated list of a Kwai user's posts: captions, media URLs, covers, engagement counts, and author info. | `handle`, `url` | | [`/post`](/docs/api-reference/kwai/post) | 10 | Returns one Kwai post: caption, video and cover URLs, view/like/comment/share counts, author info, and music metadata. | — | --- # LinkedIn (/docs/platforms/linkedin) LinkedIn API reference: 67 endpoints for profiles, companies, posts, comments, reactions, jobs and search. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} People and their full profile sections, companies, posts, comments, reactions, articles, groups, jobs, ads and search. New to the API? Read the [LinkedIn scraper API overview](/social-media-api/linkedin) for what it returns and how it compares with the official API. Base path: `/v1/linkedin/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/linkedin/profile?url=https%3A%2F%2Fwww.linkedin.com%2Fin%2Fwilliamhgates%2F" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] 6 of these are **metered**: the price depends on how much the call reads. See [Credits](/docs/credits). 37 are **beta**: they return the source's own structure under our naming rules (snake\_case keys, string ids, ISO times where the source gives a timestamp) rather than a schema-2 entity, and their fields may change. See [Schema 2](/docs/schema-2). | Endpoint | Credits | What it returns | Key parameters | | ---------------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | | [`/profile`](/docs/api-reference/linkedin/profile) | 50 | Returns a LinkedIn member's public profile: full name, headline, location, follower and connection counts, profile picture, and profile URL. | `url` | | [`/company`](/docs/api-reference/linkedin/company) | 50 | Returns a LinkedIn company page: company name, description, follower count, headquarters location, website, and logo. | `url` | | [`/company/by-domain`](/docs/api-reference/linkedin/company-by-domain) | 50 | Returns the LinkedIn company page that owns a website domain: name, description, follower count, headquarters, website, logo, and numeric company id. | `domain` | | [`/profile/similar`](/docs/api-reference/linkedin/profile-similar) | 50 | Returns members LinkedIn groups with a given profile, each with handle, headline, profile URL, and picture. | `url` | | [`/search/companies`](/docs/api-reference/linkedin/search-companies) | 100 | Returns LinkedIn company pages matching a keyword, each with name, tagline, page URL, and logo. | `query` | | [`/profile/posted-jobs`](/docs/api-reference/linkedin/profile-posted-jobs) | 50 | Returns the jobs a LinkedIn member has posted, each with job id, title, hiring company, location, and posting date. *Beta.* | `url` | | [`/job/hiring-team`](/docs/api-reference/linkedin/job-hiring-team) | 50 | Returns the members LinkedIn shows as the hiring team on a job, each with handle, headline, profile URL, and picture. | `id`, `url` | | [`/profile/articles`](/docs/api-reference/linkedin/profile-articles) | 50 | Returns Pulse articles a LinkedIn member published, each with text, author, engagement, and date. | `url` | | [`/article`](/docs/api-reference/linkedin/article) | 50 | Returns one LinkedIn Pulse article: its text, author, like and comment counts, and publish time. | `url` | | [`/article/comments`](/docs/api-reference/linkedin/article-comments) | 50 | Returns comments on a LinkedIn Pulse article, with commenter identity, text, and timestamp. | `url` | | [`/article/reactions`](/docs/api-reference/linkedin/article-reactions) | 50 | Returns members who reacted to a LinkedIn Pulse article, each with handle, name, picture, and reaction type. | `url` | | [`/profile/activity`](/docs/api-reference/linkedin/profile-activity) | 50 | Returns when a LinkedIn member last showed public activity. *Beta.* | `url` | | [`/company/also-viewed`](/docs/api-reference/linkedin/company-also-viewed) | 50 | Returns company pages LinkedIn shows as people also viewed, each with name, page URL, and logo. | `company_id`, `url` | | [`/company/employees-count`](/docs/api-reference/linkedin/company-employees-count) | 50 | Returns how many LinkedIn members list a company as employer, optionally broken down by location. *Beta.* | `company_id` | | [`/profile/interests/schools`](/docs/api-reference/linkedin/profile-interests-schools) | 50 | Returns the school pages a LinkedIn member follows, each with name and page URL. *Beta.* | `url` | | [`/profile/interests/newsletters`](/docs/api-reference/linkedin/profile-interests-newsletters) | 50 | Returns the newsletters a LinkedIn member follows, each with name and page URL. *Beta.* | `url` | | [`/profile/interests/top-voices`](/docs/api-reference/linkedin/profile-interests-top-voices) | 50 | Returns the Top Voice profiles a LinkedIn member follows, each with name and profile URL. *Beta.* | `url` | | [`/profile/position-skills`](/docs/api-reference/linkedin/profile-position-skills) | 50 | Returns a member's positions with the skills listed on each role. *Beta.* | `url` | | [`/profile/top-position`](/docs/api-reference/linkedin/profile-top-position) | 50 | Returns the company page of the member's current top position: company id, name, website, headcount, industries and locations, not the job title. *Beta.* | `url` | | [`/search/people/by-url`](/docs/api-reference/linkedin/search-people-by-url) | 100 | Returns LinkedIn members from a people-search results URL, each with handle, headline, and profile URL. | `url` | | [`/profile/all`](/docs/api-reference/linkedin/profile-all) | 50 | Returns a member's entire public profile in one call: `author` with both counts, and `background` with About, experience, education, skills, recommendations, Featured and interests. *Beta.* | `url`, `urls` | | [`/profile/complete`](/docs/api-reference/linkedin/profile-complete) | 100 | Returns a member's full background: `author` in the /profile shape, and `background` with About, experiences, educations, skills and recommendations in the profile/\* item shapes. *Beta.* | `url` | | [`/profile/with-posts`](/docs/api-reference/linkedin/profile-with-posts) | 100 | Returns a member's profile with follower and connection counts in `author`, recent `posts` in the profile/posts item shape, and experiences, educations and skills under `background`. *Beta.* | `url` | | [`/post/with-comments`](/docs/api-reference/linkedin/post-with-comments) | 50 | Returns one LinkedIn post packed with its comments in a single payload. *Beta.* | `url` | | [`/post`](/docs/api-reference/linkedin/post) | 50 | Returns one LinkedIn post: its text, author name and picture, like, comment and share counts, attached media, and the publish time. | `url` | | [`/search/people`](/docs/api-reference/linkedin/search-people) | 100–500 | Returns LinkedIn members matching a name or filters, each with handle, headline, location, follower count, profile URL, and member id. | `query` | | [`/company/people`](/docs/api-reference/linkedin/company-people) | 100–500 | Returns the members who list a company as their employer, each with handle, headline, location, profile picture, and follower count. | `company_id` | | [`/post/comments`](/docs/api-reference/linkedin/post-comments) | 50 | Returns the comments on a LinkedIn post: commenter name, comment text, reaction counts, reply count, pinned and edited flags, and timestamps. | `url` | | [`/profile/posts`](/docs/api-reference/linkedin/profile-posts) | 50–2000 | Returns the posts a LinkedIn member published, each with text, author name, like, comment and share counts, media, and publish time. | `url` | | [`/profile/reactions`](/docs/api-reference/linkedin/profile-reactions) | 50 | Returns the posts a LinkedIn member reacted to, each with the post text, its original author, like, comment and share counts, and date. | `url` | | [`/post/reposts`](/docs/api-reference/linkedin/post-reposts) | 50 | Returns the reposts of a LinkedIn post, each with the resharer's name, any added commentary, engagement counts, and publish time. | `url` | | [`/group/posts`](/docs/api-reference/linkedin/group-posts) | 50 | Returns the posts inside a LinkedIn group, each with text, author name, like, comment and share counts, media, and publish time. | `group_id` | | [`/company/affiliated-pages`](/docs/api-reference/linkedin/company-affiliated-pages) | 50 | Returns a company's affiliated and showcase pages, each with page name, LinkedIn URL, speciality, follower count, and logo. | `company_id` | | [`/post/comments/replies`](/docs/api-reference/linkedin/post-comments-replies) | 50 | Returns the replies under one LinkedIn comment, each with the replier's name, reply text, reaction and reply counts, and timestamps. | `url`, `comment_id` | | [`/profile/experiences`](/docs/api-reference/linkedin/profile-experiences) | 50 | Returns a LinkedIn member's work history, with each role's job title, company, employment dates, location, and description. *Beta.* | `url` | | [`/profile/educations`](/docs/api-reference/linkedin/profile-educations) | 50 | Returns a LinkedIn member's education history, with each entry's school, degree, field of study, and the years attended. *Beta.* | `url` | | [`/profile/skills`](/docs/api-reference/linkedin/profile-skills) | 50 | Returns the skills listed on a LinkedIn member's profile, with each skill's name and the endorsements shown against it. *Beta.* | `url` | | [`/profile/honors`](/docs/api-reference/linkedin/profile-honors) | 50 | Returns the honors and awards on a LinkedIn member's profile, with each award's title, issuer, date, and description. *Beta.* | `url` | | [`/profile/certifications`](/docs/api-reference/linkedin/profile-certifications) | 50 | Returns the licenses and certifications on a LinkedIn member's profile, with each one's name, issuer, and issue or expiry dates. *Beta.* | `url` | | [`/profile/publications`](/docs/api-reference/linkedin/profile-publications) | 50 | Returns the publications on a LinkedIn member's profile, with each one's title, publisher, publication date, description, and link. *Beta.* | `url` | | [`/profile/volunteers`](/docs/api-reference/linkedin/profile-volunteers) | 50 | Returns the volunteer experience on a LinkedIn member's profile, with each entry's role, organisation, cause, and dates. *Beta.* | `url` | | [`/profile/recommendations`](/docs/api-reference/linkedin/profile-recommendations) | 50 | Returns the recommendations on a LinkedIn member's profile, with the recommender's name and headline, the recommendation text, and date. *Beta.* | `url` | | [`/profile/interests/companies`](/docs/api-reference/linkedin/profile-interests-companies) | 50 | Returns the companies a LinkedIn member follows, with each company's name, LinkedIn URL, follower count, and logo. *Beta.* | `url` | | [`/profile/interests/groups`](/docs/api-reference/linkedin/profile-interests-groups) | 50 | Returns the LinkedIn groups a member follows, with each group's name, URL, and the size of its membership. *Beta.* | `url` | | [`/profile/images`](/docs/api-reference/linkedin/profile-images) | 50 | Returns the image posts on a LinkedIn member's profile, with each post's text, image links, engagement counts, and publish time. | `url` | | [`/profile/videos`](/docs/api-reference/linkedin/profile-videos) | 50 | Returns the video posts on a LinkedIn member's profile, with each post's text, video and thumbnail links, engagement counts, and date. | `url` | | [`/profile/comments`](/docs/api-reference/linkedin/profile-comments) | 50 | Returns the comments a LinkedIn member left on other people's posts, with the comment text, the post it sits under, and the timestamp. *Beta.* | `url` | | [`/post/reactions`](/docs/api-reference/linkedin/post-reactions) | 100–500 | Returns the people who reacted to a LinkedIn post, each with their name, headline, profile URL, and the reaction they left. | `url` | | [`/company/jobs`](/docs/api-reference/linkedin/company-jobs) | 100 | Returns the jobs a company has posted, each with job id, title, location, hiring company details, posting date, and Easy Apply flag. *Beta.* | `company_id` | | [`/search/jobs`](/docs/api-reference/linkedin/search-jobs) | 100 | Returns LinkedIn job postings matching a keyword, each with job id, title, hiring company, location, posting date, and Easy Apply flag. *Beta.* | `query` | | [`/search/location`](/docs/api-reference/linkedin/search-location) | 10 | Returns LinkedIn locations matching a place name, each with the location's display name and the geocode id LinkedIn filters on. *Beta.* | `query` | | [`/search/schools`](/docs/api-reference/linkedin/search-schools) | 10 | Returns LinkedIn school pages matching a name, each with the school's name, page URL, and the id used in search filters. *Beta.* | `query` | | [`/search/industry`](/docs/api-reference/linkedin/search-industry) | 10 | Returns LinkedIn industries matching a keyword, each with the industry name and the id LinkedIn uses to identify it. *Beta.* | `query` | | [`/profile/about`](/docs/api-reference/linkedin/profile-about) | 50 | Returns the month a LinkedIn member joined and how recently contact details and the profile photo were updated, plus identity verification when LinkedIn shows it for that member. *Beta.* | `url` | | [`/profile/contact`](/docs/api-reference/linkedin/profile-contact) | 50 | Returns the contact details a LinkedIn member exposes publicly: websites, phone numbers, address, WeChat, and Twitter, alongside their name and profile urn. *Beta.* | `url` | | [`/profile/stats`](/docs/api-reference/linkedin/profile-stats) | 50 | Returns a LinkedIn member's audience size: their follower count and their connection count. *Beta.* | `url` | | [`/company/job-count`](/docs/api-reference/linkedin/company-job-count) | 50 | Returns the number of jobs a LinkedIn company currently has open. *Beta.* | `company_id` | | [`/company/insights`](/docs/api-reference/linkedin/company-insights) | 50 | Returns headcount breakdowns of a LinkedIn company's members as name and count pairs, covering locations, schools, job functions, skills, service categories, and fields of study. *Beta.* | `company_id` | | [`/group`](/docs/api-reference/linkedin/group) | 50 | Returns a LinkedIn group's profile: name, description, member count, posting rules, industries, owners, logo and cover images, and its public and active flags. *Beta.* | `group_id` | | [`/job`](/docs/api-reference/linkedin/job) | 50 | Returns one LinkedIn job posting in full: title, hiring company, location, posting date, the job description text, and the Easy Apply flag. *Beta.* | `id` | | [`/company/posts`](/docs/api-reference/linkedin/company-posts) | 50 | Returns the recent posts from a LinkedIn company page, each with text, author name, like, comment and share counts, media, and date. | `company_id` | | [`/ad`](/docs/api-reference/linkedin/ad) | 50 | Returns one LinkedIn ad from the Ad Library: the ad copy, creative images, the advertiser's name and logo, total impressions, and start date. | `url` | | [`/ads/search`](/docs/api-reference/linkedin/ads-search) | 50 | Returns ads from the LinkedIn Ad Library matching a company, keyword, country, or date range, with each ad's copy and the advertiser behind it. *Beta.* | `company`, `keyword`, `companyId` | | [`/profile/posts/archive`](/docs/api-reference/linkedin/profile-posts-archive) | 50–5000 | Returns a LinkedIn member's deep post archive, read from their own feed rather than a search index, with the exact publish timestamp, media and the full engagement breakdown including share counts. | `url` | | [`/search/posts`](/docs/api-reference/linkedin/search-posts) | 10–480 | Returns public LinkedIn posts and Pulse articles matching a keyword, with each result's text, author, media, like and comment counts, and date. | `query`, `from_member`, `from_company` | | [`/post/transcript`](/docs/api-reference/linkedin/post-transcript) | 100 | Returns the spoken text of a video in a LinkedIn post. | `url` | | [`/profile/full`](/docs/api-reference/linkedin/profile-full) | 50 | Returns a LinkedIn company page and its latest 10 posts in one call. *Beta.* | `url` | --- # Nextdoor (/docs/platforms/nextdoor) Nextdoor API reference: 2 endpoints for local businesses and their contact details. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Local business pages on Nextdoor, and businesses by city and trade, with contact details. Base path: `/v1/nextdoor/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/nextdoor/business?slug=radiant-plumbing-air-conditioning-austin-tx" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] 1 of these are **metered**: the price depends on how much the call reads. See [Credits](/docs/credits). | Endpoint | Credits | What it returns | Key parameters | | ---------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | | [`/business`](/docs/api-reference/nextdoor/business) | 30 | Returns one local business listed on Nextdoor: name, phone, email, website, street address, categories and how many neighbours recommend it. | `slug` | | [`/search`](/docs/api-reference/nextdoor/search) | 30–170 | Returns local businesses on Nextdoor for a city and trade, each with name, address, categories and, where listed, phone, email and website. | `city`, `query` | --- # Pinterest (/docs/platforms/pinterest) Pinterest API reference: 6 endpoints for pins, boards, user boards, trends and search. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Pins, boards, user boards, trends and search. New to the API? Read the [Pinterest scraper API overview](/social-media-api/pinterest) for what it returns and how it compares with the official API. Base path: `/v1/pinterest/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/pinterest/pin?url=https%3A%2F%2Fwww.pinterest.com%2Fpin%2F211174978421744%2F" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] 2 of these are **metered**: the price depends on how much the call reads. See [Credits](/docs/credits). 2 are **beta**: they return the source's own structure under our naming rules (snake\_case keys, string ids, ISO times where the source gives a timestamp) rather than a schema-2 entity, and their fields may change. See [Schema 2](/docs/schema-2). | Endpoint | Credits | What it returns | Key parameters | | ----------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | | [`/search`](/docs/api-reference/pinterest/search) | 10–260 | Returns Pinterest pins matching a keyword, each with its text, original image, pinner and pin date; include=engagement adds save, reaction, comment and share counts. | `query` | | [`/pin`](/docs/api-reference/pinterest/pin) | 10 | Returns one Pinterest pin: its text, original image, pinner, pin date, and its save, reaction, comment and share counts. | `url` | | [`/url-stats`](/docs/api-reference/pinterest/url-stats) | 10 | Returns how many times each of up to 10 external URLs has been saved to Pinterest through the Save Button. *Beta.* | `urls` | | [`/board`](/docs/api-reference/pinterest/board) | 10–160 | Returns the pins on a Pinterest board with text, image URL, author, and save, comment and share counts; include=engagement adds each pin's date and reaction count. | `url` | | [`/user/boards`](/docs/api-reference/pinterest/user-boards) | 10 | Returns the boards a Pinterest user has created, each with its title, description, pin count, and cover image. | `handle` | | [`/trends`](/docs/api-reference/pinterest/trends) | 100 | Returns the Pinterest Trends list for one country: ranked search terms with a relative search index, for growing, seasonal, monthly or yearly tables. *Beta.* | — | --- # Quora (/docs/platforms/quora) Quora API reference: 7 endpoints for questions, answers, profiles and spaces. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Questions, answers, space posts, profiles, spaces and topics search. Base path: `/v1/quora/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/quora/search?query=learn%20programming" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] Every endpoint here has a fixed price. 2 are **beta**: they return the source's own structure under our naming rules (snake\_case keys, string ids, ISO times where the source gives a timestamp) rather than a schema-2 entity, and their fields may change. See [Schema 2](/docs/schema-2). | Endpoint | Credits | What it returns | Key parameters | | ------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------- | -------------- | | [`/search`](/docs/api-reference/quora/search) | 50 | Returns Quora questions matching a keyword: title, URL, answer count, follower count, and publish time. | `query` | | [`/post`](/docs/api-reference/quora/post) | 50 | Returns one Quora question: title, body text, and published date. | `url` | | [`/answers`](/docs/api-reference/quora/answers) | 50 | Returns Quora answers matching a keyword: the answer text, author name and credential, upvotes, views, and the parent question title. | `query` | | [`/posts`](/docs/api-reference/quora/posts) | 50 | Returns posts from Quora Spaces matching a keyword: URL, author, upvotes, comments, and publish time. | `query` | | [`/profiles`](/docs/api-reference/quora/profiles) | 50 | Returns Quora profiles matching a keyword: display name, profile URL, credential, follower count, and avatar. | `query` | | [`/spaces`](/docs/api-reference/quora/spaces) | 50 | Returns Quora Spaces matching a keyword: name, URL, description, member count, and icon. *Beta.* | `query` | | [`/topics`](/docs/api-reference/quora/topics) | 50 | Returns Quora topics matching a keyword: name, URL, follower count, and topic photo. *Beta.* | `query` | --- # Reddit (/docs/platforms/reddit) Reddit API reference: 14 endpoints for subreddits, posts, comment threads, users and search. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Subreddits, posts, comments, user profiles, transcripts and search. New to the API? Read the [Reddit scraper API overview](/social-media-api/reddit) for what it returns and how it compares with the official API. Base path: `/v1/reddit/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/reddit/subreddit?subreddit=technology" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] 5 of these are **metered**: the price depends on how much the call reads. See [Credits](/docs/credits). 1 are **beta**: they return the source's own structure under our naming rules (snake\_case keys, string ids, ISO times where the source gives a timestamp) rather than a schema-2 entity, and their fields may change. See [Schema 2](/docs/schema-2). | Endpoint | Credits | What it returns | Key parameters | | -------------------------------------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | | [`/subreddit`](/docs/api-reference/reddit/subreddit) | 10 | Returns posts from a subreddit, each with title, body, score, comment count, author, permalink, and creation timestamp. | `subreddit` | | [`/subreddit/details`](/docs/api-reference/reddit/subreddit-details) | 10 | Returns a subreddit's own details: subscriber count, active user count, description, creation date, rules, and icon URL. | `subreddit`, `url` | | [`/search`](/docs/api-reference/reddit/search) | 10–300 | Returns posts matching a keyword from across Reddit, each with title, score, comment count, subreddit, permalink, and the body at ext.selftext. | `query` | | [`/post`](/docs/api-reference/reddit/post) | 10 | Returns one post from its URL including the body text, plus score, comment count, share count, author, author avatar, thumbnail, and creation timestamp. | `url` | | [`/post/comments`](/docs/api-reference/reddit/post-comments) | 50 | Returns the full comment tree for a post, each comment with author, body, score, direct-reply count, nesting depth, timestamp, and its nested replies. | `url` | | [`/subreddit/search`](/docs/api-reference/reddit/subreddit-search) | 10–260 | Returns posts matching a query inside one subreddit, each with title, score, comment count, and permalink. | `subreddit` | | [`/profile`](/docs/api-reference/reddit/profile) | 10 | Returns one Reddit account: total karma, the post/comment/award karma split, cake day, bio, avatar, banner, trophy count and profile title. | `handle` | | [`/profile/posts`](/docs/api-reference/reddit/profile-posts) | 10 | Returns the posts one account has submitted, newest first, with the body at ext.selftext, score, comment count, upvote ratio, flair, media and ext.subreddit. | `handle` | | [`/profile/comments`](/docs/api-reference/reddit/profile-comments) | 20–2000 | Returns an account's own comment history, newest first, each with the text, score, permalink, parent\_id, the post it sits under and ext.subreddit. | `handle` | | [`/search/comments`](/docs/api-reference/reddit/search-comments) | 10 | Returns comments matching a phrase from Reddit's comment index, each with its parent post inline: ext.post\_title, ext.subreddit, ext.post\_score and ext.post\_url. | `query` | | [`/subreddits/search`](/docs/api-reference/reddit/subreddits-search) | 10–260 | Returns up to 25 communities matching a topic, each with the name at author.id, subscriber count, description and icon. | `query` | | [`/search/media`](/docs/api-reference/reddit/search-media) | 10 | Returns up to 25 Reddit posts that carry an image, video or gallery, with the media at content.media\_urls plus title, score, comment count and subreddit. | `query` | | [`/post/transcript`](/docs/api-reference/reddit/post-transcript) | 100 | Returns the transcript of a Reddit video post, both the raw caption file and a plain-text version, when Reddit publishes captions for it. | `url` | | [`/omni-search`](/docs/api-reference/reddit/omni-search) | 50–90 | Returns threads from across Reddit for one keyword with their top comments inline at top\_comments, plus a roll-up of which subreddits are talking, their weekly active users and tone. *Beta.* | `query` | --- # Rumble (/docs/platforms/rumble) Rumble API reference: 5 endpoints for videos, comments, transcripts and search. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Videos, channel videos, comments, transcripts and search. Base path: `/v1/rumble/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/rumble/search?query=funny%20cats" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] Every endpoint here has a fixed price. | Endpoint | Credits | What it returns | Key parameters | | ------------------------------------------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | | [`/search`](/docs/api-reference/rumble/search) | 10 | Returns Rumble videos and shorts matching a keyword: title, URL, thumbnail, channel, published date, and view count. | `query` | | [`/channel/videos`](/docs/api-reference/rumble/channel-videos) | 10 | Returns a Rumble channel's metadata and a page of its videos and shorts. | `handle`, `url` | | [`/video`](/docs/api-reference/rumble/video) | 10 | Returns one Rumble video's title, description, thumbnail, channel, publish date, view count, likes, dislikes, captions, and media metadata. | `url` | | [`/video/transcript`](/docs/api-reference/rumble/video-transcript) | 100 | Returns the transcript of a Rumble video when captions are available. | `url` | | [`/video/comments`](/docs/api-reference/rumble/video-comments) | 10 | Returns the top-level comments on a Rumble video: comment text, author, created time, like and dislike counts, and reply count. | `url` | --- # Snapchat (/docs/platforms/snapchat) Snapchat API reference: 2 endpoints for profiles and Spotlight comments. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Public profiles and Spotlight comments. Base path: `/v1/snapchat/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/snapchat/profile?handle=djkhaled305" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] Every endpoint here has a fixed price. | Endpoint | Credits | What it returns | Key parameters | | ------------------------------------------------------------------------ | ------- | --------------------------------------------------------------------------------------------------------------------- | -------------- | | [`/profile`](/docs/api-reference/snapchat/profile) | 10 | Returns a Snapchat user's public profile: display name, Bitmoji avatar URL, subscriber count, and bio description. | `handle` | | [`/spotlight/comments`](/docs/api-reference/snapchat/spotlight-comments) | 10 | Returns comments on a Snapchat Spotlight snap: text, display name, reaction count, nested reply count, and timestamp. | `url` | --- # Substack (/docs/platforms/substack) Substack API reference: 1 endpoints for publication posts with full text. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} A Substack publication's posts, or one post, with full text and engagement. Base path: `/v1/substack/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/substack/posts?url=https%3A%2F%2Fwww.astralcodexten.com" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] 1 of these are **metered**: the price depends on how much the call reads. See [Credits](/docs/credits). | Endpoint | Credits | What it returns | Key parameters | | ---------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | | [`/posts`](/docs/api-reference/substack/posts) | 10–120 | Returns a publication’s recent posts, or one post: title, full text and Markdown, author, reactions, comments, restacks, word count and whether it is paid. | `url` | --- # Telegram (/docs/platforms/telegram) Telegram API reference: 3 endpoints for channel profiles and posts. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Public channel profiles and their posts. Base path: `/v1/telegram/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/telegram/profile?handle=telegram" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] Every endpoint here has a fixed price. | Endpoint | Credits | What it returns | Key parameters | | -------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | | [`/profile`](/docs/api-reference/telegram/profile) | 10 | Returns a public Telegram channel or group's profile: display name, description, avatar URL, verification status, and subscriber count. | `handle` | | [`/profile/posts`](/docs/api-reference/telegram/profile-posts) | 10 | Returns one page of recent public posts from a Telegram channel or group, each with its text, publish time, view count, reaction breakdown, and media URLs. | `handle` | | [`/post`](/docs/api-reference/telegram/post) | 10 | Returns one public Telegram post: text, publish time, view count, the per-emoji reaction breakdown, and direct media URLs. | `url` | --- # Threads (/docs/platforms/threads) Threads API reference: 6 endpoints for profiles, posts, replies and search. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Profiles, posts, replies and search on Threads. New to the API? Read the [Threads scraper API overview](/social-media-api/threads) for what it returns and how it compares with the official API. Base path: `/v1/threads/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/threads/profile?handle=zuck" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] 4 of these are **metered**: the price depends on how much the call reads. See [Credits](/docs/credits). | Endpoint | Credits | What it returns | Key parameters | | ------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------- | | [`/profile`](/docs/api-reference/threads/profile) | 10 | Returns a Threads account's public profile: bio, the external link set in that bio, follower count, profile picture URL, and verification status. | `handle` | | [`/user/posts`](/docs/api-reference/threads/user-posts) | 10–1650 | Returns a Threads user's most recent posts, usually about 15. | `handle` | | [`/post`](/docs/api-reference/threads/post) | 10 | Returns one Threads post's text, like, reply and repost counts, every carousel slide or video URL, author, creation time, and topic tag at post.ext.topic\_tag. | `url` | | [`/search`](/docs/api-reference/threads/search) | 10–340 | Returns Threads posts matching a keyword, each with its text, like count, author, creation time, and the topic tag it was filed under at post.ext.topic\_tag, plus a cursor for the next window. | `query` | | [`/post/comments`](/docs/api-reference/threads/post-comments) | 10–100 | Returns the replies Threads bundles with a post, usually about 20. | `url` | | [`/search/users`](/docs/api-reference/threads/search-users) | 10–130 | Returns Threads accounts matching a query, with handle, display name, avatar and verification status. | `query` | --- # TikTok (/docs/platforms/tiktok) TikTok API reference: 37 endpoints for profiles, videos, comments, followers, hashtags, search and transcripts. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Profiles, videos, comments, followers and following, hashtags, songs, playlists, trending and popular feeds, search, transcripts and the TikTok Ad Library. New to the API? Read the [TikTok scraper API overview](/social-media-api/tiktok) for what it returns and how it compares with the official API. Base path: `/v1/tiktok/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/tiktok/profile?handle=charlidamelio" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] 9 of these are **metered**: the price depends on how much the call reads. See [Credits](/docs/credits). 6 are **beta**: they return the source's own structure under our naming rules (snake\_case keys, string ids, ISO times where the source gives a timestamp) rather than a schema-2 entity, and their fields may change. See [Schema 2](/docs/schema-2). | Endpoint | Credits | What it returns | Key parameters | | ---------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------- | | [`/profile`](/docs/api-reference/tiktok/profile) | 10 | Returns a TikTok account's public profile: display name, bio, follower count on author.followers (or the rounded figure with followers\_approximate), likes, verification, and user id. | `handle`, `user_id` | | [`/profile/videos`](/docs/api-reference/tiktok/profile-videos) | 10 | Returns a page of an account's recent public videos, each with caption, view, like, comment and share counts, and a thumbnail URL. | `handle`, `user_id` | | [`/post`](/docs/api-reference/tiktok/post) | 10 | Returns one TikTok video in full: caption, engagement counts, author, sound, video metadata, and any on-screen text the creator typed with TikTok's text tool (post.ext.on\_screen\_texts). | `url` | | [`/post/comments`](/docs/api-reference/tiktok/post-comments) | 10–30 | Returns a page of comments on one TikTok video, each with the commenter's username, comment text, like count, reply count, and timestamp. | `url` | | [`/video/comment/replies`](/docs/api-reference/tiktok/video-comment-replies) | 10 | Returns the replies under a single TikTok comment, with each reply's text, author, likes, and timestamps. | `comment_id`, `url` | | [`/comment`](/docs/api-reference/tiktok/comment) | 20–60 | Returns one TikTok comment with its current like count, reply count, pinned flag, author, and timestamp, found by link, by id, or by its text. | `comment_url`, `post_url` | | [`/search`](/docs/api-reference/tiktok/search) | 10–370 | Returns TikTok videos matching a keyword, each with caption, view and like counts, author details, a thumbnail, and post.ext.region (the country the video is registered to). | `query` | | [`/trending`](/docs/api-reference/tiktok/trending) | 50 | Returns a page of popular TikTok videos with caption, view and like counts, author and thumbnail. | `region` | | [`/search/hashtag`](/docs/api-reference/tiktok/search-hashtag) | 10 | Returns TikTok videos posted under a given hashtag, each with caption, view, like, comment and share counts, author details, and a thumbnail. | `hashtag` | | [`/search/top`](/docs/api-reference/tiktok/search-top) | 10 | Returns the videos TikTok ranks highest for a keyword on its Top tab, each with caption, author, engagement counts, a music id, and post.ext.region (the country the video is registered to). | `query` | | [`/search/users`](/docs/api-reference/tiktok/search-users) | 10–1310 | Returns TikTok accounts for a search term with username, name, avatar, followers, verification. | `query` | | [`/similar`](/docs/api-reference/tiktok/similar) | 50 | Returns about 30 TikTok accounts similar to a given creator, each with username, display name, avatar, bio, verification, and follower, following, video and like counts. | `handle` | | [`/user/audience`](/docs/api-reference/tiktok/user-audience) | 50 | Returns where a creator's followers are: the top countries in their audience, with sampled follower counts and each country's share. *Beta.* | `handle` | | [`/user/followers`](/docs/api-reference/tiktok/user-followers) | 10 | Returns a page of accounts that follow a TikTok user, each with username, display name, avatar, and its own follower count. | `handle`, `user_id` | | [`/user/following`](/docs/api-reference/tiktok/user-following) | 10 | Returns a page of accounts a TikTok user follows, each with username, display name, avatar, and its own follower count. | `handle`, `user_id` | | [`/user/live`](/docs/api-reference/tiktok/user-live) | 10 | Returns a TikTok account's live room in the platform's own shape: cover image, title, start time, status code, viewer and entry counts, stream ids, and the host's profile. *Beta.* | `handle` | | [`/post/transcript`](/docs/api-reference/tiktok/post-transcript) | 100 | Returns the spoken text of a TikTok video from its captions, with an option to fall back to AI transcription when no captions exist. | `url` | | [`/video/screen-text`](/docs/api-reference/tiktok/video-screen-text) | 50 | Returns the text shown on the video itself: native text stickers plus AI OCR of the cover frame, so burned-in editor captions are caught too. *Beta.* | `url` | | [`/song`](/docs/api-reference/tiktok/song) | 10 | Returns one TikTok sound's details: title, artist, duration, how many videos use it, and its cover image. | `clipId` | | [`/song/videos`](/docs/api-reference/tiktok/song-videos) | 10 | Returns videos that use a given TikTok sound, each with caption, author details, and engagement counts. | `clipId` | | [`/hashtags/popular`](/docs/api-reference/tiktok/hashtags-popular) | 60–960 | Returns TikTok's trending-hashtag board for one of 27 markets over 7, 30 or 90 days: each hashtag's rank, window post and view counts, daily popularity curve, industry board and top creators. | — | | [`/videos/popular`](/docs/api-reference/tiktok/videos-popular) | 260–450 | Returns TikTok's Top Videos board for the US, Japan, Vietnam, Thailand or Indonesia over 7 or 30 days: each video's rank, lifetime and window views, organic views, engagement rate and creator. | — | | [`/profile/region`](/docs/api-reference/tiktok/profile-region) | 10 | Returns the two letter country code for a public TikTok profile, for example US or MX. *Beta.* | `handle` | | [`/adlibrary/search`](/docs/api-reference/tiktok/adlibrary-search) | 50–170 | Returns TikTok Ad Library ads matching a keyword or advertiser name, each with creative, title, advertiser, impressions and date; include=ad adds the brand, landing page and advertiser link. | `query`, `advertiser_name` | | [`/adlibrary/ad`](/docs/api-reference/tiktok/adlibrary-ad) | 50 | Returns one TikTok Ad Library ad: creative video, title, advertiser account, landing page, brand name, and first-shown date. | `ad_id`, `url` | | [`/search/suggestions`](/docs/api-reference/tiktok/search-suggestions) | 10 | Returns the autocomplete suggestions TikTok's own search box shows for a partial query, as a list of suggestion rows. *Beta.* | `query` | | [`/collection/videos`](/docs/api-reference/tiktok/collection-videos) | 10 | Returns the public videos inside a TikTok collection, each with caption, play like comment share and save counts, author, and a playable URL. | `url` | | [`/profile/playlists`](/docs/api-reference/tiktok/profile-playlists) | 10 | Returns the playlists a TikTok creator has published on their profile, each with its playlist id, its name, and the number of videos it holds. | `handle`, `user_id` | | [`/playlist/videos`](/docs/api-reference/tiktok/playlist-videos) | 10 | Returns the videos inside one of a creator's profile playlists, in playlist order, each with engagement counts, caption, author, and a playable URL. | `playlist_id`, `handle`, `user_id` | | [`/user/liked`](/docs/api-reference/tiktok/user-liked) | 10 | Returns the videos a TikTok account has liked, newest first, each carrying its own third party author, engagement counts, caption, and a playable URL. | `handle`, `user_id` | | [`/location/posts`](/docs/api-reference/tiktok/location-posts) | 10 | Returns public TikTok videos tagged at a place, each with engagement counts, caption, author, and a playable URL. | `location_id` | | [`/effects`](/docs/api-reference/tiktok/effects) | 10 | Returns TikTok camera effects by id, each with its name, its designer, the views of videos made with it, and how many such videos exist. | `ids` | | [`/effect/videos`](/docs/api-reference/tiktok/effect-videos) | 10 | Returns public TikTok videos created with a camera effect, each with engagement counts, caption, author, and a playable URL. | `effect_id` | | [`/search/music`](/docs/api-reference/tiktok/search-music) | 10 | Returns TikTok sounds matching a keyword, each with title, artist, duration, cover art, a preview audio URL, and how many videos use it. | `query` | | [`/hashtag`](/docs/api-reference/tiktok/hashtag) | 10 | Returns TikTok's own record for a hashtag, with its display name, its description where one exists, the live total views under the tag, and how many videos carry it. | `hashtag`, `hashtag_id` | | [`/ads/top`](/docs/api-reference/tiktok/ads-top) | 100–1000 | Returns TikTok's Creative Center leaderboard of top-performing ads for a market and time window, each with click-through rate, board rank, like count, industry, objective, and the ad video. | — | | [`/profile/full`](/docs/api-reference/tiktok/profile-full) | 50 | Returns a TikTok profile plus its latest 10 videos in one call. *Beta.* | `handle`, `user_id` | --- # TikTok Shop (/docs/platforms/tiktokshop) TikTok Shop API reference: 6 endpoints for products, reviews, shops and sales. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} TikTok Shop products, reviews, shop catalogues, creator showcases, product search and sales by market. Base path: `/v1/tiktokshop/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/tiktokshop/product?url=https%3A%2F%2Fwww.tiktok.com%2Fshop%2Fpdp%2Fgoli-ashwagandha-gummies-with-vitamin-d-ksm-66-vegan-non-gmo%2F1729587769570529799" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] 1 of these are **metered**: the price depends on how much the call reads. See [Credits](/docs/credits). 6 are **beta**: they return the source's own structure under our naming rules (snake\_case keys, string ids, ISO times where the source gives a timestamp) rather than a schema-2 entity, and their fields may change. See [Schema 2](/docs/schema-2). | Endpoint | Credits | What it returns | Key parameters | | -------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | | [`/product`](/docs/api-reference/tiktokshop/product) | 10 | Returns one TikTok Shop product in full: description, price and discount, rating, review count, brand, specs, stock, seller details, shipping estimate, and images. *Beta.* | `url`, `product_id` | | [`/product/reviews`](/docs/api-reference/tiktokshop/product-reviews) | 10 | Returns reviews on a TikTok Shop product: rating, text, author, buyer country, verified-purchase and incentivized-review flags, the purchased SKU, and a timestamp. *Beta.* | `url`, `product_id` | | [`/products`](/docs/api-reference/tiktokshop/products) | 10 | Returns the products listed on a TikTok Shop storefront: title, images, canonical URL, price and discount, sold count, review count, rating, SKU ID, and the shop's own profile. *Beta.* | `url` | | [`/search`](/docs/api-reference/tiktokshop/search) | 10 | Returns TikTok Shop products matching a keyword across every storefront, each with price, rating, sold count, seller trust label, ship-from location, and promotion badges. *Beta.* | `query` | | [`/user/showcase`](/docs/api-reference/tiktokshop/user-showcase) | 10 | Returns the products featured in a TikTok user's public showcase: title, price and discount, rating, sold count, images, and SKU ID. *Beta.* | `handle` | | [`/sales`](/docs/api-reference/tiktokshop/sales) | 0–80 | Returns, per market, listings found, their units sold added up, an estimated sales figure in local currency, and the top listings by units for a keyword. *Beta.* | `query` | --- # Truth Social (/docs/platforms/truthsocial) Truth Social API reference: 3 endpoints for profiles and posts. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Profiles, posts and a user's posts on Truth Social. Base path: `/v1/truthsocial/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/truthsocial/profile?handle=realDonaldTrump" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] Every endpoint here has a fixed price. | Endpoint | Credits | What it returns | Key parameters | | ----------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | | [`/profile`](/docs/api-reference/truthsocial/profile) | 10 | Returns a Truth Social user's public profile: display name, follower count, following count, truth count, bio, and profile image URL. | `handle` | | [`/user/posts`](/docs/api-reference/truthsocial/user-posts) | 10 | Returns a Truth Social user's recent truths: text content, like count, retruth count, reply count, media attachments, and creation timestamp. | `handle`, `user_id` | | [`/post`](/docs/api-reference/truthsocial/post) | 10 | Returns one Truth Social post: text content, like count, retruth count, reply count, media attachments, author info, and creation timestamp. | `url` | --- # Twitch (/docs/platforms/twitch) Twitch API reference: 4 endpoints for streamer profiles, videos, clips and schedules. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Streamer profiles, videos, clips and stream schedules. Base path: `/v1/twitch/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/twitch/profile?handle=ninja" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] Every endpoint here has a fixed price. 1 are **beta**: they return the source's own structure under our naming rules (snake\_case keys, string ids, ISO times where the source gives a timestamp) rather than a schema-2 entity, and their fields may change. See [Schema 2](/docs/schema-2). | Endpoint | Credits | What it returns | Key parameters | | ------------------------------------------------------------ | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | | [`/profile`](/docs/api-reference/twitch/profile) | 10 | Returns a Twitch streamer's public profile: display name, follower count, bio, profile image URL, broadcast language, and partner or affiliate status. | `handle` | | [`/clip`](/docs/api-reference/twitch/clip) | 10 | Returns one Twitch clip's title, view count, duration, creator name, broadcaster name, game name, thumbnail URL, and creation timestamp. | `url` | | [`/user/videos`](/docs/api-reference/twitch/user-videos) | 10 | Returns up to 100 of a Twitch user's videos: id, slug, URL, embed URL, title, view count, language, duration, game info, broadcaster details, thumbnail, and available qualities. | `handle` | | [`/user/schedule`](/docs/api-reference/twitch/user-schedule) | 10 | Returns a Twitch user's upcoming stream schedule, each entry with start time, end time, title, description, and thumbnail URL. *Beta.* | `handle` | --- # Twitter/X (/docs/platforms/twitter) Twitter/X API reference: 15 endpoints for profiles, tweets, replies, followers, communities and search. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Profiles, tweets, replies, retweeters, media, followers and following, communities and search on Twitter/X. New to the API? Read the [Twitter/X scraper API overview](/social-media-api/twitter) for what it returns and how it compares with the official API. Base path: `/v1/twitter/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/twitter/profile?handle=elonmusk" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] 1 of these are **metered**: the price depends on how much the call reads. See [Credits](/docs/credits). 2 are **beta**: they return the source's own structure under our naming rules (snake\_case keys, string ids, ISO times where the source gives a timestamp) rather than a schema-2 entity, and their fields may change. See [Schema 2](/docs/schema-2). | Endpoint | Credits | What it returns | Key parameters | | ------------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | | [`/profile`](/docs/api-reference/twitter/profile) | 10 | Returns an X (Twitter) account's public profile: follower count, following count, tweet count, bio, profile and banner image URLs, and verification status. | `handle` | | [`/user/tweets`](/docs/api-reference/twitter/user-tweets) | 10 | Returns an account's most recent tweets in descending order, with full text, like, retweet, reply, bookmark and view counts, media, and creation time. | `handle` | | [`/tweet`](/docs/api-reference/twitter/tweet) | 10 | Returns one tweet in full: its text, like, retweet, reply and quote counts, media attachments, author info, and creation timestamp. | `url` | | [`/community`](/docs/api-reference/twitter/community) | 10 | Returns an X (Twitter) community's details: name, description, member count, rules, and creation date. | `url` | | [`/community/tweets`](/docs/api-reference/twitter/community-tweets) | 10 | Returns recent tweets posted inside an X (Twitter) community, each with its text, engagement counts, and author info. | `url` | | [`/tweet/transcript`](/docs/api-reference/twitter/tweet-transcript) | 100 | Returns the transcript of a video attached to a tweet, including auto-generated captions. | `url` | | [`/ai-search`](/docs/api-reference/twitter/ai-search) | 50 | Returns a written answer to a plain-English question about X (Twitter), the X posts cited as sources, and how many searches the model ran. *Beta.* | `query` | | [`/search/tweets`](/docs/api-reference/twitter/search-tweets) | 10–50 | Returns tweets matching a keyword or phrase, each with the full text, like, retweet, reply, bookmark and view counts, media attachments, author info, and creation time. | `query` | | [`/tweet/replies`](/docs/api-reference/twitter/tweet-replies) | 10 | Returns the replies to a tweet, each with the reply text, author info, like and reply counts, and creation time. | `url` | | [`/user/media`](/docs/api-reference/twitter/user-media) | 10 | Returns the tweets on an account's Media tab, only those carrying a photo or video, with media URLs, engagement counts, and creation time. | `handle` | | [`/user/followers`](/docs/api-reference/twitter/user-followers) | 10 | Returns the accounts following an X (Twitter) user, each with the handle, display name, bio, follower and tweet counts, privacy flag, and join date. | `handle` | | [`/user/following`](/docs/api-reference/twitter/user-following) | 10 | Returns the accounts an X (Twitter) user follows, each with the handle, display name, bio, follower and tweet counts, privacy flag, and join date. | `handle` | | [`/tweet/retweeters`](/docs/api-reference/twitter/tweet-retweeters) | 10 | Returns the accounts that retweeted a tweet, each with the handle, display name, bio, follower and tweet counts, privacy flag, and join date. | `url` | | [`/search/users`](/docs/api-reference/twitter/search-users) | 10 | Returns X (Twitter) accounts matching a name, handle or keyword, with handle, display name, bio, avatar, follower, following and tweet counts, location, verification, join date, and a private flag. | `query` | | [`/profile/full`](/docs/api-reference/twitter/profile-full) | 50 | Returns an X account's profile and its recent tweets in one call. *Beta.* | `handle` | --- # Weibo (/docs/platforms/weibo) Weibo API reference: 6 endpoints for profiles, posts, comments, search and trending. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Weibo profiles, posts, a user's posts, comments, post search and the hot-search board. Base path: `/v1/weibo/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/weibo/post?post_id=5092682368025584" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] Every endpoint here has a fixed price. 1 are **beta**: they return the source's own structure under our naming rules (snake\_case keys, string ids, ISO times where the source gives a timestamp) rather than a schema-2 entity, and their fields may change. See [Schema 2](/docs/schema-2). | Endpoint | Credits | What it returns | Key parameters | | ----------------------------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------- | -------------- | | [`/post`](/docs/api-reference/weibo/post) | 10 | Returns one public Weibo post: its text, author, like, comment and repost counts, image count and publish time. | `post_id` | | [`/profile`](/docs/api-reference/weibo/profile) | 10 | Returns a public Weibo account: name, bio, avatar, follower, following and post counts, verification, location and website. | `user_id` | | [`/user/posts`](/docs/api-reference/weibo/user-posts) | 10 | Returns a page of an account’s recent public posts, each with text, engagement counts and publish time. | `user_id` | | [`/post/comments`](/docs/api-reference/weibo/post-comments) | 10 | Returns a page of first-level comments on a post, each with text, author, likes, reply count and time. | `post_id` | | [`/search`](/docs/api-reference/weibo/search) | 10 | Returns public Weibo posts matching a keyword, each with text, author, engagement counts and URL. | `query` | | [`/trending`](/docs/api-reference/weibo/trending) | 10 | Returns the current Weibo hot-search ranking: each topic’s keyword, rank, heat and label, pinned topics marked. *Beta.* | — | --- # Xiaohongshu (RedNote) (/docs/platforms/xiaohongshu) Xiaohongshu (RedNote) API reference: 6 endpoints for creators, notes, comments, search and trending. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Xiaohongshu (RedNote) creators, notes, comments, note search and the hot-search board. Base path: `/v1/xiaohongshu/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/xiaohongshu/profile?url=https%3A%2F%2Fwww.xiaohongshu.com%2Fuser%2Fprofile%2F6a59f257000000000f03b000" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] 4 of these are **metered**: the price depends on how much the call reads. See [Credits](/docs/credits). 1 are **beta**: they return the source's own structure under our naming rules (snake\_case keys, string ids, ISO times where the source gives a timestamp) rather than a schema-2 entity, and their fields may change. See [Schema 2](/docs/schema-2). | Endpoint | Credits | What it returns | Key parameters | | ----------------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | | [`/search`](/docs/api-reference/xiaohongshu/search) | 50–1000 | Returns Xiaohongshu notes matching a keyword: title, short summary, creator, likes, comments, saves, shares, and cover image. | `query` | | [`/trending`](/docs/api-reference/xiaohongshu/trending) | 50–1000 | Returns the live Xiaohongshu hot search board: ranked topics with a hot value. *Beta.* | — | | [`/profile`](/docs/api-reference/xiaohongshu/profile) | 50 | Returns a Xiaohongshu (RedNote) creator's profile: display name, RedNote ID, bio, avatar, followers, following, notes posted, likes received, and IP province. | `url`, `id` | | [`/profile/posts`](/docs/api-reference/xiaohongshu/profile-posts) | 50–1000 | Returns a Xiaohongshu creator's notes, newest first: title, short summary, engagement counts, and pinned flag. | `url`, `id` | | [`/post`](/docs/api-reference/xiaohongshu/post) | 50 | Returns one Xiaohongshu note in full: body text, images or video, creator, likes, comments, saves, shares, and the IP province. | `url` | | [`/post/comments`](/docs/api-reference/xiaohongshu/post-comments) | 50–1000 | Returns top-level comments on a Xiaohongshu note: text, commenter, likes, reply count, pin flag, and the commenter's IP province. | `url` | --- # YouTube (/docs/platforms/youtube) YouTube API reference: 26 endpoints for channels, videos, shorts, comments, transcripts and search. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Channels, videos, shorts, comments, playlists, community posts, transcripts and subtitles, trending and search. New to the API? Read the [YouTube scraper API overview](/social-media-api/youtube) for what it returns and how it compares with the official API. Base path: `/v1/youtube/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/youtube/channel?handle=MrBeast" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] 10 of these are **metered**: the price depends on how much the call reads. See [Credits](/docs/credits). 7 are **beta**: they return the source's own structure under our naming rules (snake\_case keys, string ids, ISO times where the source gives a timestamp) rather than a schema-2 entity, and their fields may change. See [Schema 2](/docs/schema-2). | Endpoint | Credits | What it returns | Key parameters | | --------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------- | | [`/channel`](/docs/api-reference/youtube/channel) | 10 | Returns a YouTube channel's public profile: subscriber count on author.followers (rounded above 1,000), video and view totals, description, banner and avatar URLs, and author.ext.public\_email. | `channelId`, `handle`, `url` | | [`/channel/about`](/docs/api-reference/youtube/channel-about) | 250 | Returns the address behind a channel's View email address control on author.ext.public\_email, its listed country, and the profile: id, handle, name, avatar, bio, URL, subscribers, join date, views. | `channelId`, `handle`, `url` | | [`/channel/videos`](/docs/api-reference/youtube/channel-videos) | 10–20 | Returns recent videos published by a channel, each with title, view count, duration, thumbnail, and publish date; pass includeExtras=true to add like and comment counts. | `channelId`, `handle` | | [`/video`](/docs/api-reference/youtube/video) | 10 | Returns full details for one video: title, view, like and comment counts, description, tags, duration, channel info, and publish date. | `url` | | [`/video/sponsors`](/docs/api-reference/youtube/video-sponsors) | 100 | Returns whether a video carries a paid-promotion disclosure plus the brands likely sponsoring it, each with supporting evidence and a confidence score. *Beta.* | `url` | | [`/video/comments`](/docs/api-reference/youtube/video-comments) | 10 | Returns comments on a video, each with the author name, comment text, like count, reply count, and publish timestamp. | `url` | | [`/video/comment/replies`](/docs/api-reference/youtube/video-comment-replies) | 10 | Returns the replies under one comment, each with the reply text, author details, like count, and publish date. | `continuationToken` | | [`/search`](/docs/api-reference/youtube/search) | 10–150 | Returns YouTube search results for a keyword: videos, channels, playlists, shorts, and live streams, each with title, thumbnail, views, and channel. | `query` | | [`/channel/shorts`](/docs/api-reference/youtube/channel-shorts) | 10–20 | Returns the Shorts published by a channel, each with title, view count, like count, and thumbnail. | `channelId`, `handle` | | [`/community-post`](/docs/api-reference/youtube/community-post) | 10 | Returns one YouTube community post: its text, like count, comment count, attached images, and author info. | `url` | | [`/playlist`](/docs/api-reference/youtube/playlist) | 10–110 | Returns the videos in a playlist in playlist order: video id, title, thumbnail, channel and publish date. | `playlist_id` | | [`/search/hashtag`](/docs/api-reference/youtube/search-hashtag) | 10–110 | Returns videos posted under a hashtag, each with view count, channel info, and publish date. | `hashtag` | | [`/shorts/trending`](/docs/api-reference/youtube/shorts-trending) | 50–150 | Returns the Shorts trending on YouTube right now, each with view count, like count, channel info, and thumbnail. | — | | [`/video/transcript`](/docs/api-reference/youtube/video-transcript) | 30 | Returns the spoken transcript of a video as timestamped segments, each with text, start time and duration, plus language, word count, and speech rate. | `url` | | [`/channel/playlists`](/docs/api-reference/youtube/channel-playlists) | 10 | Returns the playlists on a channel's Playlists tab, each with playlist id, title, thumbnail, video count, channel info, and playlist URL. | `channelId`, `handle` | | [`/channel/lives`](/docs/api-reference/youtube/channel-lives) | 10–70 | Returns the live and past streams on a channel's Live tab, each with title, URL, thumbnail, view count, publish time, and duration. | `channelId`, `handle` | | [`/channel/community-posts`](/docs/api-reference/youtube/channel-community-posts) | 10 | Returns the posts on a channel's Posts tab, each with post id, URL, text, images, attached video, like count, publish time, and channel info. | `channelId`, `handle` | | [`/videos/trending`](/docs/api-reference/youtube/videos-trending) | 10–60 | Returns the trending videos for a country and category, each with title, thumbnail, duration, view, like and comment counts, channel, and publish time. | — | | [`/playlist/items`](/docs/api-reference/youtube/playlist-items) | 10–110 | Returns the videos of a playlist in playlist order, each with video id, title, thumbnail, the video's own channel, its publish date, and its position and insertion time on post.ext. | `playlist_id` | | [`/search/advanced`](/docs/api-reference/youtube/search-advanced) | 10–110 | Returns video search results with the full filter set: sort order, length, live status, license, category, country, language, and publish date window. | `query` | | [`/search/suggestions`](/docs/api-reference/youtube/search-suggestions) | 10 | Returns the autocomplete suggestions YouTube's own search box shows for a partial query, as a plain list of strings. *Beta.* | `query` | | [`/video/audio`](/docs/api-reference/youtube/video-audio) | 50 | Returns the downloadable audio streams for a video, each with a direct media URL, mime type, bitrate, audio quality, sample rate, channels, and duration. *Beta.* | `url` | | [`/video/files`](/docs/api-reference/youtube/video-files) | 50 | Returns the downloadable video streams for a video, each with a direct media URL, mime type, resolution, quality label, frame rate, bitrate, and duration. *Beta.* | `url` | | [`/video/subtitles`](/docs/api-reference/youtube/video-subtitles) | 10 | Returns the caption track files for a video, each with a language code and name, file format, and a direct download URL, including auto-generated tracks. *Beta.* | `url` | | [`/video/thumbnails`](/docs/api-reference/youtube/video-thumbnails) | 10 | Returns a video's thumbnail images at every available size, each with a direct image URL, width, height, aspect ratio, and image format. *Beta.* | `url` | | [`/profile/full`](/docs/api-reference/youtube/profile-full) | 50 | Returns a channel's profile and up to 30 recent videos in one call. *Beta.* | `handle`, `channelId`, `url` | --- # Zhihu (/docs/platforms/zhihu) Zhihu API reference: 5 endpoints for profiles, questions, answers and articles. Per-endpoint credit prices and cURL examples. {/* GENERATED by `npm run docs:openapi` — edit scripts/docs-openapi.mts, not this file. */} Zhihu profiles, questions, answers and article search. Base path: `/v1/zhihu/...` ## Quickstart [#quickstart] ```bash title="cURL" curl "https://api.insightsocial.app/v1/zhihu/answer?answer_id=2054145988235880002" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## Endpoints [#endpoints] Every endpoint here has a fixed price. | Endpoint | Credits | What it returns | Key parameters | | ----------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------- | -------------- | | [`/answer`](/docs/api-reference/zhihu/answer) | 10 | Returns one Zhihu answer: its text, author, the question it answers and its publish and update times. | `answer_id` | | [`/question`](/docs/api-reference/zhihu/question) | 10 | Returns a Zhihu question: its title, views, comments, answer and follower counts, and when it was asked. | `question_id` | | [`/question/answers`](/docs/api-reference/zhihu/question-answers) | 10 | Returns up to 20 answers to a question, each with its text, author, votes, comments, saves and times. | `question_id` | | [`/profile`](/docs/api-reference/zhihu/profile) | 10 | Returns a public Zhihu account: name, headline, avatar, follower count, and how many answers and articles it wrote. | `handle` | | [`/search/articles`](/docs/api-reference/zhihu/search-articles) | 10 | Returns Zhihu column articles matching a keyword, each with title, text, author, votes and comments. | `query` | --- # Search Douyin videos by keyword (/docs/how-to/douyin-search) Find Douyin videos by keyword, with caption, creator, likes, comments, shares, saves, music and duration. 50 credits per video returned. To search Douyin videos by keyword, call `GET /v1/douyin/search` with a `query` in Chinese or English. One call returns up to 25 videos, 10 by default, each with its caption and hashtags, creator, like, comment, share and save counts, music ID, cover image, video URL and duration. It is metered at 50 to 1,250 credits per call: the range scales with `limit`, which works out to 50 credits per video, so the default 10 videos cost 500. 50 to 1,250 credits per call: 50 at `limit=1`, 1,250 at `limit=25`. The sample below used the default `limit` of 10 and was charged 500. When the call starts, the most it can cost with your `limit` is held; you are charged what it used and the rest is released. Add `dry_run=1` to get a free quote first. Asking the same search again within 1 hour is answered from the shared cache for 2 credits. Failed calls and empty results are free. ## Which parameters can I set? [#which-parameters-can-i-set] | Parameter | Values | | ----------- | ------------------------------------------------------------------------ | | `query` | Required. The search keywords, for example `美食` (food) or `coffee`. | | `limit` | Videos to return, 1 to 25. Default 10. | | `sort` | `relevance` (default), `most_liked` or `latest`. | | `published` | Recency window: `all` (default), `day`, `week` or `half_year`. | | `duration` | Video length: `all` (default), `under_1m`, `one_to_five_m` or `over_5m`. | ## Make the request [#make-the-request] ```bash title="cURL" curl -G "https://api.insightsocial.app/v1/douyin/search" \ --data-urlencode "query=美食" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os import requests res = requests.get( "https://api.insightsocial.app/v1/douyin/search", params={"query": "美食", "limit": 10, "sort": "most_liked"}, headers={"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}, timeout=120, ) for item in res.json()["data"]["items"]: post = item["post"] print(post["engagement"]["likes"], post["engagement"]["shares"], post["url"]) ``` ```ts title="Node.js" const url = new URL("https://api.insightsocial.app/v1/douyin/search"); url.searchParams.set("query", "美食"); url.searchParams.set("limit", "10"); url.searchParams.set("sort", "most_liked"); const body = await (await fetch(url, { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! } })).json(); for (const { post } of body.data.items) console.log(post.engagement.likes, post.engagement.shares, post.url); ``` ## What comes back [#what-comes-back] A real response from 2026-10-14, shortened: one video instead of 10, the caption and long URLs cut, and the creator replaced with a placeholder. ```json title="Response (shortened)" { "success": true, "endpoint": "/v1/douyin/search", "schema_version": "2", "data": { "items": [ { "post": { "id": "7644834211292280293", "url": "https://www.douyin.com/video/7644834211292280293", "kind": "video", "content": { "text": "简单几步教你做出五⭐️级酒店的酸辣柠檬虾 #酸辣柠檬虾 #柠檬虾做法 …", "media_urls": ["https://api-play.amemv.com/aweme/v1/play/?video_id=…"], "thumbnail_url": "https://p3-sign.douyinpic.com/tos-cn-p-0015c000-ce/…", "duration_seconds": 17.3 }, "author": { "id": "example_user_id", "username": "example_user", "display_name": "Example User", "verified": false }, "engagement": { "views": null, "likes": 3111106, "comments": 43865, "shares": 1765351, "saves": 2519903 }, "published_at": "2026-05-28T07:01:10.000Z", "ext": { "music_id": "7587240130655537202", "author_followers": 711275, "download_count": 34926 } } } ], "total": null }, "pagination": { "next_cursor": null, "has_more": false, "page_size": 10 }, "unavailable": [], "credits_used": 500 } ``` | Field | What it holds | | --------------------------- | -------------------------------------------------------------------- | | `data.items[].post` | One video in the shared [post shape](/docs/schema/post). | | `post.content.text` | The caption, hashtags included. | | `post.content.media_urls` | A playback URL for the video file. | | `post.engagement` | Likes, comments, shares and saves. | | `post.author` | The creator: numeric ID, Douyin ID, nickname, avatar, verified flag. | | `post.ext.author_followers` | The creator's follower count. | | `post.ext.music_id` | The sound the video uses. | | `post.ext.download_count` | How many times the video was downloaded. | ## What are the limits? [#what-are-the-limits] * **25 videos per search.** The endpoint does not paginate: `has_more` was `false` in the sample. Use different keywords, `sort` orders or `published` windows to reach more videos. * **No view counts.** `engagement.views` is `null` on every video in the sample, which means unknown, not zero. * **No total.** `data.total` is `null`: the search does not say how many videos match. * **Douyin ID can be missing.** `author.username` was `null` for 4 of the 10 creators in the sample. `author.id`, the numeric user ID, was always set. * **Short-lived media links.** The cover image URLs carry an expiry time, about two weeks after the call in the sample. Download what you need instead of storing the links. * **Price per video.** The cost grows with `limit`, so ask for the number of videos you need, not the maximum. ## What can I build with it? [#what-can-i-build-with-it] * **Market research in China.** Search a product or category with `sort=most_liked` to see which videos and hashtags lead it. * **Creator discovery.** Rank the authors in your results by `ext.author_followers`, then pass `author.id` as `id` to `GET /v1/douyin/profile` (60 credits) for the full profile. * **Trend checks.** Run the same keyword with `published=week` and `sort=latest` each week to see what is new. The other Douyin endpoints pick up where search stops: `search/users` (50 to 1,250 credits) finds creators instead of videos, `post` (100) reads one video, `post/comments` (20 to 1,000) and `comment/replies` (50 to 2,500) read its comments, `profile` (60) and `profile/posts` (50 to 1,250) cover a creator, and `trending` (250) returns the live hot-search board. ## Related [#related] * [`GET /v1/douyin/search` reference](/docs/api-reference/douyin/search): every parameter and field. * [`GET /v1/douyin/search/users`](/docs/api-reference/douyin/search-users): search creators instead of videos. * [`GET /v1/douyin/trending`](/docs/api-reference/douyin/trending): the live hot-search board. * [Douyin endpoints](/docs/platforms/douyin): everything the API covers on Douyin. --- # Search public Facebook posts by keyword (/docs/how-to/facebook-keyword-post-search) Find public Facebook posts that match a keyword, with text, permalink, author, publish time, likes, comments and reactions. 10 to 50 credits per page. To search public Facebook posts by keyword, call `GET /v1/facebook/search/posts` with a `query`. Each result carries the post's text, permalink, publish time, author, like and comment counts, and a breakdown of reactions by type. It is metered at 10 to 50 credits per page; the sample page below returned 5 posts and cost 10. Meta's public Graph API has no keyword search over public posts, and Meta Content Library, which has one, is limited to approved researchers. 10 credits per page. A plain call reads one page and costs 10 credits (the sample page held 5 posts). `max_pages` (1 to 5) walks up to five pages in one call: the call holds 10 credits per page when it starts, up to 50 at `max_pages=5`, and you're charged for the pages it actually read. Send `dry_run=1` for a free quote. Asking for the same page again within 1 hour is answered from the shared cache for 2 credits. Failed calls and empty results are free. ## How do I narrow the search? [#how-do-i-narrow-the-search] `query` is the only required input: a keyword or phrase, such as `NASA`. These optional parameters narrow it: | Parameter | What it does | | ------------------------ | ----------------------------------------------------------------------------------------- | | `recent_posts=true` | Ranks by recency instead of relevance. | | `start_date`, `end_date` | Only posts on or after / on or before a date, as `YYYY-MM-DD`. | | `max_age_days` | Keeps only posts published in the last N days, 1 to 3650. Posts with no date are dropped. | | `location_uid` | A Facebook location ID, to scope the search to a place. | | `max_pages` | Walks 1 to 5 pages in one call and returns the rows from all of them. | ## Make the request [#make-the-request] ```bash title="cURL" curl "https://api.insightsocial.app/v1/facebook/search/posts?query=NASA" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os import requests res = requests.get( "https://api.insightsocial.app/v1/facebook/search/posts", params={"query": "NASA"}, headers={"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}, timeout=120, ) body = res.json() for item in body["data"]["items"]: post = item["post"] print(post["author"]["display_name"], post["engagement"]["likes"], post["url"]) print("credits used:", body["credits_used"]) ``` ```ts title="Node.js" const url = new URL("https://api.insightsocial.app/v1/facebook/search/posts"); url.searchParams.set("query", "NASA"); const body = await (await fetch(url, { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! } })).json(); for (const { post } of body.data.items) console.log(post.author.display_name, post.engagement.likes, post.url); console.log("credits used:", body.credits_used); ``` ## What comes back [#what-comes-back] A real response from 2026-10-14 for `NASA`, shortened: one post instead of 5, long URLs cut, and the reaction list cut to the top two. ```json title="Response (shortened)" { "success": true, "endpoint": "/v1/facebook/search/posts", "schema_version": "2", "data": { "items": [ { "post": { "id": "1652244896270880", "url": "https://www.facebook.com/NASA/posts/pfbid0dX7KLT7wBEzj8kzTo2NQBYp…", "kind": null, "content": { "text": "Brewing up baby stars ⭐\n\nNASA's James Webb Space Telescope's unique view reveals many previously hidden stars in this nebula…", "media_urls": ["https://scontent.fada6-1.fna.fbcdn.net/…"], "thumbnail_url": null, "duration_seconds": null }, "author": { "id": "100044561550831", "username": "NASA", "display_name": "NASA - National Aeronautics and Space Administration", "avatar_url": null, "verified": null }, "engagement": { "views": null, "likes": 8319, "comments": 177, "shares": null, "saves": null }, "published_at": "2026-10-09T20:54:33.000Z", "ext": { "author_url": "https://www.facebook.com/NASA", "reaction_counts": [ { "type": "like", "count": 5012 }, { "type": "love", "count": 2985 } ] } } } ], "total": null, "truncated": null }, "pagination": { "next_cursor": "v2c.s.rRxQehfH6OjOtFbCKikKJw", "has_more": true, "page_size": 5 }, "unavailable": [ "items[].post.author.avatar_url", "items[].post.engagement.views", "items[].post.engagement.shares" ], "credits_used": 10 } ``` | Field | What it holds | | --------------------------------------- | -------------------------------------------------------------------------------------------------------------- | | `data.items[].post` | One post in the shared [post shape](/docs/schema/post): text, permalink, media URLs, publish time. | | `data.items[].post.author` | The page or profile that posted it: ID, username, display name. `ext.author_url` links to it. | | `data.items[].post.engagement.likes` | All reactions added up, not only Like. 8,319 here. | | `data.items[].post.ext.reaction_counts` | The same total split by type: like, love, care, wow, haha, sad, anger. | | `data.items[].post.engagement.comments` | The comment count. | | `unavailable` | Fields this endpoint cannot fill: views, shares and the author's avatar. Their `null` means unknown, not zero. | ## How do I get more results? [#how-do-i-get-more-results] Send `pagination.next_cursor` back as `cursor`, with the same `query` and filters, and stop when `pagination.has_more` is `false`. Each page is 10 to 50 credits. To fetch up to 5 pages in one request, pass `max_pages` instead. ```python title="Python: walk the first 5 pages" import os import requests BASE = "https://api.insightsocial.app/v1" HEADERS = {"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]} params = {"query": "NASA", "recent_posts": "true"} posts = [] for _ in range(5): body = requests.get(f"{BASE}/facebook/search/posts", params=params, headers=HEADERS, timeout=120).json() posts += body["data"]["items"] if not body["pagination"]["has_more"]: break params["cursor"] = body["pagination"]["next_cursor"] ``` See [Pagination](/docs/pagination) for the full rules. ## What are the limits? [#what-are-the-limits] * **Small pages.** The sample page held 5 posts. Plan on many pages for a broad keyword. * **Relevance order by default.** The sample page mixed posts from October 9 and October 14. Pass `recent_posts=true` for recency, or a date window. * **No views or shares.** `engagement.views` and `engagement.shares` are listed in `unavailable`. `author.avatar_url` and `author.verified` came back `null` too. * **Public posts only.** The search covers public Facebook posts. ## Can I search Facebook for other things? [#can-i-search-facebook-for-other-things] Yes. Four sibling endpoints take a keyword too: * `GET /v1/facebook/search/pages` (10 credits): pages by name, with page ID, display name, permalink, avatar and verified flag. Follower counts are not on this card. * `GET /v1/facebook/search/people` (10 credits): people by name. The ID is often a `pfbid` token, not a numeric user ID. * `GET /v1/facebook/search/groups` (10 to 110 credits): public groups, in the shape the group endpoints take. Add `include=details` for member counts, descriptions and privacy. * `GET /v1/facebook/search/videos` (10 credits): public videos by phrase. View counts there are a display string, not a number. ## What can I build with it? [#what-can-i-build-with-it] * **Brand and topic monitoring.** Run your brand name daily with `recent_posts=true` and store new posts with their reaction counts. The [brand mention monitoring](/docs/recipes/brand-mention-monitoring) recipe shows the daily dedupe-and-alert loop; add this endpoint as one more source. * **News and event tracking.** Bound a keyword with `start_date` and `end_date` to see who posted about an event and how much reaction each post drew. * **Finding pages to follow.** The authors of the top results are the pages already posting about a topic. ## Related [#related] * [`GET /v1/facebook/search/posts` reference](/docs/api-reference/facebook/search-posts): every parameter and field. * [`GET /v1/facebook/search/pages`](/docs/api-reference/facebook/search-pages) and [`GET /v1/facebook/search/groups`](/docs/api-reference/facebook/search-groups): find pages and groups by keyword. * [Facebook endpoints](/docs/platforms/facebook): everything the API covers on Facebook. --- # How-to guides (/docs/how-to) Short answers to "how do I get this data by API?" for 21 jobs other APIs don't cover, from Instagram post likers to Douyin search, each with code and its price. Each guide answers one question: how to get one kind of public social data through the InsightSocial API. It names the endpoint, shows a working request in cURL, Python and Node.js, shows a real response (shortened, with private accounts replaced by placeholders), and states the price in credits and the limits before you spend anything. These are jobs most social data APIs, and the platforms' own APIs, don't offer. For full builds that combine several endpoints, see [Recipes](/docs/recipes). ## Instagram [#instagram] | Guide | Endpoint | Credits | | -------------------------------------------------------------------------------------------------------- | --------------------------------- | ----------- | | [Get the accounts that liked an Instagram post](/docs/how-to/instagram-post-likers) | `GET /v1/instagram/post/likers` | 50 | | [Get Instagram reels that use a specific audio](/docs/how-to/instagram-reels-by-audio) | `GET /v1/instagram/audio/reels` | 10 per page | | [Get an Instagram account's country and join date](/docs/how-to/instagram-account-country-and-join-date) | `GET /v1/instagram/profile/about` | 10 | | [Get Instagram posts an account is tagged in](/docs/how-to/instagram-tagged-posts) | `GET /v1/instagram/tagged` | 50 per page | | [Get an Instagram account's current stories](/docs/how-to/instagram-stories) | `GET /v1/instagram/stories` | 50 | | [Find Instagram accounts similar to another account](/docs/how-to/instagram-similar-accounts) | `GET /v1/instagram/similar` | 50 and up | ## TikTok [#tiktok] | Guide | Endpoint | Credits | | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ----------- | | [Get the videos a TikTok user has liked](/docs/how-to/tiktok-videos-a-user-liked) | `GET /v1/tiktok/user/liked` | 10 per page | | [Find TikTok creators similar to one you know](/docs/how-to/tiktok-similar-creators) | `GET /v1/tiktok/similar` | 50 | | [Get TikTok videos tagged at a location](/docs/how-to/tiktok-videos-by-location) | `GET /v1/tiktok/location/posts` | 10 per page | | [Read on-screen text from TikTok and Instagram posts](/docs/how-to/read-on-screen-text-from-videos) | `GET /v1/tiktok/video/screen-text`, `GET /v1/instagram/media/screen-text` | 50 | ## LinkedIn [#linkedin] | Guide | Endpoint | Credits | | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ----------------- | | [Get the employees of a LinkedIn company](/docs/how-to/linkedin-company-employees) | `GET /v1/linkedin/company/people` | 100 per page | | [Get the people who reacted to or reposted a LinkedIn post](/docs/how-to/linkedin-post-reactions-and-reposts) | `GET /v1/linkedin/post/reactions`, `GET /v1/linkedin/post/reposts` | 100 / 50 per page | | [Check when a LinkedIn member was last active](/docs/how-to/linkedin-last-active) | `GET /v1/linkedin/profile/activity` | 50 | | [Get the posts in a LinkedIn group](/docs/how-to/linkedin-group-posts) | `GET /v1/linkedin/group/posts` | 50 per page | ## Facebook, YouTube, Reddit and Pinterest [#facebook-youtube-reddit-and-pinterest] | Guide | Endpoint | Credits | | ------------------------------------------------------------------------------------ | -------------------------------- | ----------- | | [Search public Facebook posts by keyword](/docs/how-to/facebook-keyword-post-search) | `GET /v1/facebook/search/posts` | 10–50 | | [Get a YouTube channel's business email](/docs/how-to/youtube-channel-email) | `GET /v1/youtube/channel/about` | 250 | | [Search Reddit comments by keyword](/docs/how-to/reddit-comment-search) | `GET /v1/reddit/search/comments` | 10 per page | | [Get Pinterest Trends for a country](/docs/how-to/pinterest-trends) | `GET /v1/pinterest/trends` | 100 | | [Get Pinterest save counts for any URL](/docs/how-to/pinterest-save-counts-for-urls) | `GET /v1/pinterest/url-stats` | 10 | ## Chinese platforms [#chinese-platforms] | Guide | Endpoint | Credits | | ---------------------------------------------------------------------------------------- | ---------------------------- | ------------ | | [Search Xiaohongshu (RedNote) notes by keyword](/docs/how-to/xiaohongshu-rednote-search) | `GET /v1/xiaohongshu/search` | 50 per note | | [Search Douyin videos by keyword](/docs/how-to/douyin-search) | `GET /v1/douyin/search` | 50 per video | ## How is a call priced? [#how-is-a-call-priced] Every guide states its price. Failed calls, empty results and `dry_run=1` quotes are free. Asking the same thing again soon after is answered from the shared cache for 2 credits. Where a price is a range, the endpoint is metered: the most the call can cost is held when it starts and you pay what it used. Every new account gets its first 10 calls free. See [Credits](/docs/credits). --- # Get an Instagram account's country and join date (/docs/how-to/instagram-account-country-and-join-date) Read the "About this account" details of a public Instagram account (country, month joined) plus follower and post counts. 10 credits per call. To find which country an Instagram account is based in and when it was created, call `GET /v1/instagram/profile/about` with the account's `handle`. It returns Instagram's "About this account" details (country, month joined, published contact) plus the follower count and lifetime post count. It costs 10 credits. Meta's Instagram Graph API does not expose another account's "About this account" details. 10 credits per call. Checking 100 accounts costs 1,000 credits. Asking for the same account again within 24 hours is answered from the shared cache for 2 credits. Failed calls and empty results are free. ## Make the request [#make-the-request] The `handle` is the username without the `@`: `https://www.instagram.com/instagram/` has the handle `instagram`. ```bash title="cURL" curl "https://api.insightsocial.app/v1/instagram/profile/about?handle=instagram" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os import requests res = requests.get( "https://api.insightsocial.app/v1/instagram/profile/about", params={"handle": "instagram"}, headers={"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}, timeout=120, ) author = res.json()["data"]["author"] about = author["ext"] print(author["username"], about["country"], about["account_created"]) print(author["followers"], "followers,", author["posts_count"], "posts") ``` ```ts title="Node.js" const url = new URL("https://api.insightsocial.app/v1/instagram/profile/about"); url.searchParams.set("handle", "instagram"); const body = await (await fetch(url, { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! } })).json(); const { author } = body.data; console.log(author.username, author.ext.country, author.ext.account_created); console.log(`${author.followers} followers, ${author.posts_count} posts`); ``` ## What comes back [#what-comes-back] A real response from 2026-10-14, shortened: long URLs cut. ```json title="Response (shortened)" { "success": true, "endpoint": "/v1/instagram/profile/about", "schema_version": "2", "data": { "author": { "id": "25025320", "username": "instagram", "display_name": "Instagram", "avatar_url": "https://scontent-iad6-1.cdninstagram.com/v/t51.82787-19/…", "bio": "Discover what's new on Instagram 🔎✨", "verified": true, "followers": 686584045, "following": 308, "posts_count": 8618, "url": "https://www.instagram.com/instagram/", "private": false, "joined_at": null, "ext": { "account_created": "February 2012", "country": "United States", "public_email": null, "public_phone": null, "business_category": null, "hd_avatar_url": "https://scontent-iad6-1.cdninstagram.com/v/t51.82787-19/…" } } }, "unavailable": [], "credits_used": 10 } ``` | Field | What it holds | | ---------------------------------------------- | ---------------------------------------------------------------------------------------------- | | `data.author.ext.country` | The country Instagram lists under "About this account". `United States` here. | | `data.author.ext.account_created` | The month the account joined, as Instagram writes it: `February 2012`. It is text, not a date. | | `data.author.followers` | The exact follower count. 686,584,045 here. | | `data.author.posts_count` | Lifetime post count. 8,618 here. | | `data.author.ext.public_email`, `public_phone` | Contact details the account publishes, when it publishes any. `null` here. | | `data.author.id` | The numeric user ID, which other Instagram endpoints accept as `user_id`. | ## What are the limits? [#what-are-the-limits] * **Month, not day.** `account_created` holds a month and a year as text, such as `February 2012`. `joined_at` stays `null`, so parse the text yourself if you need a date. * **Country as Instagram lists it.** `country` is the value Instagram shows in "About this account", not a location we work out. * **Public accounts only.** The endpoint reads the details of a public account. * **Contact is often empty.** `public_email` and `public_phone` are `null` when Instagram shows none. `null` means unknown, not that the account has no email. ## What can I build with it? [#what-can-i-build-with-it] * **Region filters for creator lists.** Keep or drop creators by `country` before outreach, for example to run a campaign in one market. * **Account age checks.** Flag accounts created recently in a list of followers, commenters or applicants. * **Profile totals.** Read an account's lifetime post count and exact follower count in one 10-credit call. ## Related [#related] * [`GET /v1/instagram/profile/about` reference](/docs/api-reference/instagram/profile-about): every parameter and field. * [`GET /v1/instagram/profile`](/docs/api-reference/instagram/profile): the public profile, with bio, follower counts and the email in the bio. * [Instagram endpoints](/docs/platforms/instagram): everything the API covers on Instagram. --- # Get the accounts that liked an Instagram post (/docs/how-to/instagram-post-likers) See who liked an Instagram post, up to about 2,000 accounts in one call, with username, name, avatar and verified badge, plus the full like count. 50 credits. To see who liked an Instagram post, call `GET /v1/instagram/post/likers` with the post's `url`. One call returns the accounts that liked it, each with username, display name, avatar and verified badge, plus the post's full like count. It costs 50 credits. A post with a few hundred likes comes back whole. A bigger one stops at about 1,000 to 2,000 likers however many likes it has, with no further pages: in our tests on 2026-10-14, a post with 1,309,613 likes returned 1,999 likers. Meta's Instagram Graph API has no endpoint that lists who liked a post. 50 credits per call. There are no further pages, so one post costs 50 credits. Asking for the same post again within 6 hours is answered from the shared cache for 2 credits. Failed calls and empty results are free. ## How do I find the post URL? [#how-do-i-find-the-post-url] Use the post's own link, in either form: `https://www.instagram.com/p/CnpPou9hWqq/` or `https://www.instagram.com/reel/…/`. In the app, tap the share icon on the post and copy the link. If you want the likers of an account's recent posts, `GET /v1/instagram/profile/posts` lists them first, and each post it returns carries its `url`. ## Make the request [#make-the-request] ```bash title="cURL" curl -G "https://api.insightsocial.app/v1/instagram/post/likers" \ --data-urlencode "url=https://www.instagram.com/p/CnpPou9hWqq/" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os import requests res = requests.get( "https://api.insightsocial.app/v1/instagram/post/likers", params={"url": "https://www.instagram.com/p/CnpPou9hWqq/"}, headers={"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}, timeout=120, ) body = res.json() likers = body["data"]["items"] print(len(likers), "likers returned of", body["data"]["total"], "likes") for item in likers: author = item["author"] print(author["username"], author["display_name"], author["verified"]) ``` ```ts title="Node.js" const url = new URL("https://api.insightsocial.app/v1/instagram/post/likers"); url.searchParams.set("url", "https://www.instagram.com/p/CnpPou9hWqq/"); const body = await (await fetch(url, { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! } })).json(); console.log(`${body.data.items.length} likers returned of ${body.data.total} likes`); for (const { author } of body.data.items) console.log(author.username, author.display_name, author.verified); ``` ## What comes back [#what-comes-back] A real response from 2026-10-14, shortened: one liker instead of 903, long URLs cut, and the liker's account replaced with a placeholder. ```json title="Response (shortened)" { "success": true, "endpoint": "/v1/instagram/post/likers", "schema_version": "2", "data": { "items": [ { "author": { "id": "61219643578", "username": "example_user", "display_name": "Example User", "avatar_url": "https://scontent-dfw5-2.cdninstagram.com/v/t51.82787-19/…", "verified": false, "followers": null, "following": null, "posts_count": null, "private": null, "url": "https://www.instagram.com/example_user/" } } ], "total": 1390077, "truncated": true }, "pagination": { "next_cursor": null, "has_more": false, "page_size": 903 }, "unavailable": [ "items[].author.followers", "items[].author.following", "items[].author.posts_count", "items[].author.private" ], "credits_used": 50 } ``` | Field | What it holds | | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | `data.items[].author` | One account that liked the post in the shared [author shape](/docs/schema/author): numeric ID, username, display name, avatar, verified badge, profile URL. | | `data.total` | The post's full like count. 1,390,077 here, against 903 likers returned. | | `data.truncated` | `true` when fewer likers came back than the post has likes, so the list was cut short. | | `pagination` | `has_more` is `false` and `next_cursor` is `null`: everything you can get comes in one call. `page_size` is how many likers came back. | | `unavailable` | Fields this endpoint cannot fill: `followers`, `following`, `posts_count` and `private` are `null`, which means unknown, not zero. | ## What are the limits? [#what-are-the-limits] * **The full list only for small posts.** Past a few hundred likes you get about 1,000 to 2,000 likers, and there is no cursor to ask for more, so you cannot walk the full list of a popular post. An older post can return fewer: the 2023 post in the example returned 903 likers, and 737 on a second call the same day. What we measured on 2026-10-14: | Likes on the post | Likers returned | | ----------------------- | --------------- | | 164 | 148 | | 547 | 547 | | 1,132 | 999 | | 5,967 | 999 | | 59,804 | 1,000 | | 88,488 | 1,997 | | 455,993 | 1,999 | | 1,309,613 | 1,999 | | 1,390,071 (posted 2023) | 903, then 737 | * **No profile counts.** Follower, following and post counts, and whether the account is private, are `null`. Pass a username to `GET /v1/instagram/profile` (10 credits) when you need them. * **Display names can be missing.** In our test, `display_name` was `null` on 71 of the 903 likers. Use the username as the key. * **Absence proves nothing.** On a post with more likes than came back, an account missing from the list may still have liked it. ## What can I build with it? [#what-can-i-build-with-it] * **Audience snapshots.** Look at who engages with a post: how many likers are verified, which accounts recur across a creator's posts. * **Lead and creator discovery.** Likers of a niche post are people interested in that topic. Enrich the ones you care about with `GET /v1/instagram/profile`. * **Engagement checks.** Compare the likers you get against the like count and the comment section to judge whether engagement looks organic. ## Related [#related] * [`GET /v1/instagram/post/likers` reference](/docs/api-reference/instagram/post-likers): every parameter and field. * [`GET /v1/instagram/post/comments`](/docs/api-reference/instagram/post-comments): the people who commented on a post, with paging. * [Instagram endpoints](/docs/platforms/instagram): everything the API covers on Instagram. --- # Get Instagram reels that use a specific audio (/docs/how-to/instagram-reels-by-audio) List the reels that use one Instagram sound, with views, likes, comments and who posted each, by audio ID. 10 credits per page of 12 reels. To list the reels that use one Instagram sound, call `GET /v1/instagram/audio/reels` with the track's `audio_id`. Each page returns 12 reels with their view, like and comment counts and the account that posted them, plus the track's title, artist and the total number of reels that use it. It costs 10 credits a page. Meta's Instagram Graph API has no way to list reels by audio, which is why this endpoint exists. 10 credits per page of 12 reels. Walking 10 pages (120 reels) costs 100 credits. Asking for the same page again within 6 hours is answered from the shared cache for 2 credits. Failed calls and empty results are free. ## How do I find the audio ID? [#how-do-i-find-the-audio-id] The `audio_id` is the number in the sound's page URL: `https://www.instagram.com/reels/audio/1392969992841787/` has the ID `1392969992841787`. Open a reel, tap the sound name, and copy the URL. If you only know the song's name, `GET /v1/instagram/search/music` (50 credits) looks it up by keyword. Every reel this endpoint returns also carries the ID in `post.ext.audio_cluster_id`, so you can hop from one reel to everything else that uses its sound. ## Make the request [#make-the-request] ```bash title="cURL" curl "https://api.insightsocial.app/v1/instagram/audio/reels?audio_id=1392969992841787" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os import requests res = requests.get( "https://api.insightsocial.app/v1/instagram/audio/reels", params={"audio_id": "1392969992841787"}, headers={"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}, timeout=120, ) body = res.json() sound = body["data"]["ext"]["sound"] print(sound["track_title"], "by", sound["artist"], "-", body["data"]["total"], "reels") for item in body["data"]["items"]: post = item["post"] print(post["author"]["username"], post["engagement"]["views"], post["url"]) ``` ```ts title="Node.js" const url = new URL("https://api.insightsocial.app/v1/instagram/audio/reels"); url.searchParams.set("audio_id", "1392969992841787"); const body = await (await fetch(url, { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! } })).json(); const { sound } = body.data.ext; console.log(`${sound.track_title} by ${sound.artist}: ${body.data.total} reels`); for (const { post } of body.data.items) console.log(post.author.username, post.engagement.views, post.url); ``` ## What comes back [#what-comes-back] A real response from 2026-10-14, shortened: one reel instead of 12, long URLs cut, and the poster's account replaced with a placeholder. ```json title="Response (shortened)" { "success": true, "endpoint": "/v1/instagram/audio/reels", "schema_version": "2", "data": { "items": [ { "post": { "id": "3916378119790708418", "url": "https://www.instagram.com/reel/DZZxSZsPMbC/", "kind": "short", "content": { "text": "O processo é um tanto delicado…", "duration_seconds": 16.415 }, "author": { "id": null, "username": "example_creator", "display_name": "Example Creator", "verified": false }, "engagement": { "views": 145694, "likes": 5733, "comments": 83, "shares": null, "saves": null }, "published_at": "2026-06-10T10:52:09.000Z", "ext": { "media_type": "video", "audio_cluster_id": "1392969992841787", "coauthors": [] } } } ], "total": 280258, "ext": { "is_trending": true, "sound": { "track_title": "Freakin’ Out", "artist": "Dexter and The Moonrocks", "is_original": false, "duration_seconds": 217.897, "is_explicit": false, "spotify_track_id": "2LSltDwOzq2yoBliFUnY41" } } }, "pagination": { "next_cursor": "v2c.tNvG_cl…", "has_more": true, "page_size": 12 }, "unavailable": ["items[].post.author.id"], "credits_used": 10 } ``` | Field | What it holds | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | `data.items[].post` | One reel in the shared [post shape](/docs/schema/post): caption, video and thumbnail URLs, duration, views, likes, comments, publish time. | | `data.items[].post.author` | The account that posted the reel: username, display name, avatar, verified badge. | | `data.total` | How many reels use the sound, as Instagram counts them. 280,258 here. | | `data.ext.sound` | The track: title, artist, whether it is an original sound, length, and its Spotify track ID when Instagram links one. | | `data.ext.is_trending` | Whether Instagram marks the sound as trending. | | `unavailable` | Fields this response could not fill. Here `author.id` is always `null`; use the username. | ## How do I get more than 12 reels? [#how-do-i-get-more-than-12-reels] Send `pagination.next_cursor` back as `cursor`, with the same `audio_id`, and stop when `pagination.has_more` is `false`. Each page is 10 credits. ```python title="Python: walk the first 5 pages" import os import requests BASE = "https://api.insightsocial.app/v1" HEADERS = {"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]} params = {"audio_id": "1392969992841787"} reels = [] for _ in range(5): body = requests.get(f"{BASE}/instagram/audio/reels", params=params, headers=HEADERS, timeout=120).json() reels += body["data"]["items"] if not body["pagination"]["has_more"]: break params["cursor"] = body["pagination"]["next_cursor"] ``` A popular sound can have hundreds of thousands of reels, so set a page limit before you start. See [Pagination](/docs/pagination) for the full rules. ## What are the limits? [#what-are-the-limits] * **Order.** Reels are not sorted by date or by views: one page mixes reels from June and October. Sort in your own code if you need to. * **Shares and saves.** Instagram does not show them on this surface, so `engagement.shares` and `engagement.saves` are `null`, which means unknown, not zero. * **Author ID.** `post.author.id` is `null` on this endpoint. Pass the username to `GET /v1/instagram/profile` if you need the full profile. * **Public reels only.** Reels from private accounts never appear. ## What can I build with it? [#what-can-i-build-with-it] * **Sound trend tracking.** Store `data.total` per sound each day; a fast-rising count is a sound taking off. The [music trend detection](/docs/recipes/music-trend-detection) recipe combines it with TikTok's sound pages. * **Creator discovery.** The authors of the top reels on a sound are creators already working in that niche. * **Campaign tracking.** If your brand launched a sound, the reel list and `total` show who used it and how far it spread. ## Related [#related] * [`GET /v1/instagram/audio/reels` reference](/docs/api-reference/instagram/audio-reels): every parameter and field. * [`GET /v1/tiktok/song/videos`](/docs/api-reference/tiktok/song-videos): the same question on TikTok. * [Instagram endpoints](/docs/platforms/instagram): everything the API covers on Instagram. --- # Find Instagram accounts similar to another account (/docs/how-to/instagram-similar-accounts) Get the accounts Instagram suggests as similar to a user, with username, name, avatar and verified badge. 50 credits; more with profile details. To find Instagram accounts similar to one you know, call `GET /v1/instagram/similar` with its `handle` or `user_id`. It returns the accounts Instagram suggests as similar, each with username, display name, avatar URL, verified badge and profile URL. A plain call costs 50 credits: in our test on `mrbeast` it returned 80 accounts for 50 credits. Add `include=profile` to also get follower counts and bios, which costs more. Metered, 50 to 850 credits. The most your parameters can cost is held when the call starts, and you are charged what it used. A plain call holds 50, with or without `limit`. `include=profile` holds up to 250 for its default 20 accounts, and up to 850 with `limit=80`: up to 10 credits more for each account it fills in. Add `dry_run=1` to get the quote for your parameters for free. Asking for the same list again within 6 hours is answered from the shared cache for 2 credits. Failed calls and empty results are free. ## Make the request [#make-the-request] The `handle` is the username without the `@`: `https://www.instagram.com/mrbeast/` has the handle `mrbeast`. You can send the numeric `user_id` instead for faster responses. ```bash title="cURL" curl "https://api.insightsocial.app/v1/instagram/similar?handle=mrbeast" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os import requests res = requests.get( "https://api.insightsocial.app/v1/instagram/similar", params={"handle": "mrbeast"}, headers={"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}, timeout=120, ) body = res.json() print(len(body["data"]["items"]), "accounts for", body["credits_used"], "credits") for item in body["data"]["items"]: author = item["author"] print(author["username"], author["display_name"], author["verified"]) ``` ```ts title="Node.js" const url = new URL("https://api.insightsocial.app/v1/instagram/similar"); url.searchParams.set("handle", "mrbeast"); const body = await (await fetch(url, { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! } })).json(); console.log(`${body.data.items.length} accounts for ${body.credits_used} credits`); for (const { author } of body.data.items) console.log(author.username, author.display_name, author.verified); ``` To get follower counts and bios in the same call, add `include=profile`. It fills in the top 20 accounts unless you set `limit` (1 to 80): ```bash title="cURL: with profile details for the top 40" curl "https://api.insightsocial.app/v1/instagram/similar?handle=mrbeast&include=profile&limit=40" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ## What comes back [#what-comes-back] A real response from 2026-10-14 for a plain call, shortened: one account instead of 80, long URLs cut. ```json title="Response (shortened)" { "success": true, "endpoint": "/v1/instagram/similar", "schema_version": "2", "data": { "items": [ { "author": { "id": "1541770582", "username": "ishowspeed", "display_name": "IShowSpeed", "avatar_url": "https://instagram.fkiv3-1.fna.fbcdn.net/v/t51.2885-19/…", "bio": null, "verified": true, "followers": null, "following": null, "posts_count": null, "private": null, "url": "https://www.instagram.com/ishowspeed/" } } ], "total": null, "truncated": null }, "pagination": { "next_cursor": null, "has_more": false, "page_size": 80 }, "unavailable": [ "items[].author.followers", "items[].author.following", "items[].author.posts_count", "items[].author.private" ], "credits_used": 50 } ``` | Field | What it holds | | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | `data.items[].author` | One suggested account in the shared [author shape](/docs/schema/author): numeric ID, username, display name, avatar, verified badge, profile URL. | | `data.items[].author.id` | The numeric user ID, which you can pass as `user_id` to other Instagram endpoints. | | `data.items[].author.followers`, `following`, `bio` | `null` on a plain call. Filled for the hydrated accounts when you send `include=profile`. | | `pagination` | `has_more` is `false`: the list comes in one call and cannot be paged. | | `unavailable` | Fields a plain call cannot fill. `null` means unknown, not zero. | ## What are the limits? [#what-are-the-limits] * **One fixed list.** The endpoint returns a single list and cannot be paged. Our test returned 80 accounts. `limit` keeps the top N in Instagram's order. * **Mixed kinds of accounts.** The list is Instagram's suggestion, not a peer list. For `mrbeast` it mixed creators (`ishowspeed`, `airrack`), brands (`feastables`, `crocs`) and fan accounts. 63 of the 80 were verified. * **No counts without `include=profile`.** Followers, following, post counts and bios are `null` on a plain call. With `include=profile`, `posts_count` is filled only where the lookup has an exact total. * **Cost grows with profile details.** Each account `include=profile` fills in can add up to 10 credits. Quote with `dry_run=1` first. ## What can I build with it? [#what-can-i-build-with-it] * **Lookalike creator discovery.** Start from a creator who worked well for you and get the accounts Instagram places next to them. * **Competitor maps.** Run it on your own brand and on competitors to see which accounts Instagram groups together. * **Seed expansion.** Run it again on the best results to widen a list, filtering by follower count with `include=profile`. Each call is charged. ## Related [#related] * [`GET /v1/instagram/similar` reference](/docs/api-reference/instagram/similar): every parameter and field. * [`GET /v1/tiktok/similar`](/docs/api-reference/tiktok/similar): the same question on TikTok. * [`GET /v1/instagram/profile`](/docs/api-reference/instagram/profile): the full profile of one account. * [Instagram endpoints](/docs/platforms/instagram): everything the API covers on Instagram. --- # Get an Instagram account's current stories (/docs/how-to/instagram-stories) List the stories a public Instagram account has live right now, with image or video URLs, duration and post time. 50 credits per call. To get the stories an Instagram account has live right now, call `GET /v1/instagram/stories` with the account's `handle` or `user_id`. It returns each live story with its ID, image or video URLs, thumbnail, duration, post time and author. It costs 50 credits. An account with nothing live returns an empty list rather than an error, and empty results are free. 50 credits per call, for the whole story tray. Asking for the same account again within 6 hours is answered from the shared cache for 2 credits; add `fresh=1` to fetch the tray as it is now, at the full price. Failed calls and empty results are free. ## Make the request [#make-the-request] The `handle` is the username without the `@`: `https://www.instagram.com/natgeo/` has the handle `natgeo`. You can send the numeric `user_id` instead for faster responses. ```bash title="cURL" curl "https://api.insightsocial.app/v1/instagram/stories?handle=natgeo" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os import requests res = requests.get( "https://api.insightsocial.app/v1/instagram/stories", params={"handle": "natgeo"}, headers={"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}, timeout=120, ) stories = res.json()["data"]["items"] if not stories: print("Nothing live right now") for item in stories: story = item["post"] print(story["id"], story["kind"], story["published_at"], story["content"]["media_urls"][0]) ``` ```ts title="Node.js" const url = new URL("https://api.insightsocial.app/v1/instagram/stories"); url.searchParams.set("handle", "natgeo"); const body = await (await fetch(url, { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! } })).json(); if (body.data.items.length === 0) console.log("Nothing live right now"); for (const { post } of body.data.items) { console.log(post.id, post.kind, post.published_at, post.content.media_urls[0]); } ``` ## What comes back [#what-comes-back] A real response from 2026-10-14, shortened: long URLs cut. National Geographic had one story live. ```json title="Response (shortened)" { "success": true, "endpoint": "/v1/instagram/stories", "schema_version": "2", "data": { "items": [ { "post": { "id": "4007131251999054397", "url": "https://www.instagram.com/p/DecMKSbxRo9/", "kind": "image", "content": { "text": null, "media_urls": ["https://scontent-iad6-1.cdninstagram.com/v/t51.82787-15/…"], "thumbnail_url": null, "duration_seconds": null }, "author": { "id": null, "username": "natgeo", "display_name": "National Geographic", "verified": true }, "engagement": { "views": null, "likes": null, "comments": null, "shares": null, "saves": null }, "published_at": "2026-10-13T16:01:34.000Z", "ext": { "media_type": "image", "coauthors": [] } } } ], "total": null, "truncated": null }, "pagination": { "next_cursor": null, "has_more": false, "page_size": 1 }, "unavailable": [ "items[].post.author.id", "items[].post.engagement.views", "items[].post.engagement.likes", "items[].post.engagement.comments" ], "credits_used": 50 } ``` | Field | What it holds | | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | `data.items[].post.id` | The story's ID. Pass it as `story_id` to the download endpoint below. | | `data.items[].post.content.media_urls` | The image or video file of the story. | | `data.items[].post.content.thumbnail_url`, `duration_seconds` | A video story's thumbnail and length. Both are `null` on this image story. | | `data.items[].post.kind`, `ext.media_type` | Whether the story is an image or a video. | | `data.items[].post.published_at` | When the story was posted. | | `unavailable` | Fields this endpoint cannot fill: the author's `id` and the view, like and comment counts are `null`, which means unknown, not zero. | ## How do I download one story at full resolution? [#how-do-i-download-one-story-at-full-resolution] Call `GET /v1/instagram/story/download` with the author's `user_id` and the story's `story_id` (the `post.id` above). It returns that one story's full-resolution image or video URLs and metadata, for 50 credits. `author.id` is `null` on the stories response, so get the numeric user ID from `GET /v1/instagram/profile/about` (10 credits), which returns it as `data.author.id`. ## What are the limits? [#what-are-the-limits] * **Live stories only.** The endpoint returns what is in the tray at the time of the call. For stories an account saved to its profile, use `GET /v1/instagram/highlights`. * **No counts.** Story views, likes and comments are `null`. * **No author ID.** `author.id` is `null`; use the username, or look up the ID as shown above. * **Empty is normal.** An account with no live story returns an empty `items` list, which costs nothing. ## What can I build with it? [#what-can-i-build-with-it] * **Story archives.** Check a set of brand or competitor accounts on a schedule and save the media of each new story ID. * **Campaign checks.** Confirm that a creator posted the story agreed in a deal, with the time it went up. * **Competitor monitoring.** Track how often competitors post stories and what kind (image or video). ## Related [#related] * [`GET /v1/instagram/stories` reference](/docs/api-reference/instagram/stories): every parameter and field. * [`GET /v1/instagram/story/download`](/docs/api-reference/instagram/story-download): one story's full-resolution media. * [`GET /v1/instagram/highlights`](/docs/api-reference/instagram/highlights): stories an account saved to its profile. * [Instagram endpoints](/docs/platforms/instagram): everything the API covers on Instagram. --- # Get Instagram posts an account is tagged in (/docs/how-to/instagram-tagged-posts) List the posts other people tagged an Instagram account in, with caption, media URLs, engagement counts and who posted each. 50 credits per page. To list the posts other people tagged an Instagram account in, call `GET /v1/instagram/tagged` with the account's `handle` or `user_id`. Each page returns tagged posts with their caption, media URLs, view, like and comment counts, and the account that posted them. It costs 50 credits a page; the first page in our test held 21 posts. `GET /v1/instagram/profile/posts` returns only what the account posted itself, so this is how you see what others posted about it. 50 credits per page. Walking 10 pages costs 500 credits. Asking for the same page again within 6 hours is answered from the shared cache for 2 credits. Failed calls and empty results are free. ## How do I find the handle or user ID? [#how-do-i-find-the-handle-or-user-id] The `handle` is the username without the `@`: `https://www.instagram.com/mrbeast/` has the handle `mrbeast`. Send `user_id` instead for faster responses. `GET /v1/instagram/profile/about` (10 credits) returns it as `data.author.id`. ## Make the request [#make-the-request] ```bash title="cURL" curl "https://api.insightsocial.app/v1/instagram/tagged?handle=mrbeast" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os import requests res = requests.get( "https://api.insightsocial.app/v1/instagram/tagged", params={"handle": "mrbeast"}, headers={"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}, timeout=120, ) body = res.json() for item in body["data"]["items"]: post = item["post"] print(post["author"]["username"], post["published_at"], post["engagement"]["views"], post["url"]) ``` ```ts title="Node.js" const url = new URL("https://api.insightsocial.app/v1/instagram/tagged"); url.searchParams.set("handle", "mrbeast"); const body = await (await fetch(url, { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! } })).json(); for (const { post } of body.data.items) { console.log(post.author.username, post.published_at, post.engagement.views, post.url); } ``` ## What comes back [#what-comes-back] A real response from 2026-10-14, shortened: one post instead of 21, long URLs cut, the caption shortened, and the poster's account replaced with a placeholder. ```json title="Response (shortened)" { "success": true, "endpoint": "/v1/instagram/tagged", "schema_version": "2", "data": { "items": [ { "post": { "id": "4007852954603258964", "url": "https://www.instagram.com/reel/DeewQcXIIRU/", "kind": "short", "content": { "text": "Short clip… #viralvideos", "media_urls": ["https://scontent-hel3-1.cdninstagram.com/o1/v/t2/f2/m86/….mp4?…"], "thumbnail_url": "https://scontent-hel3-1.cdninstagram.com/v/t51.71878-15/…", "duration_seconds": 15.603 }, "author": { "id": null, "username": "example_user", "display_name": null, "verified": false }, "engagement": { "views": 0, "likes": 0, "comments": 0, "shares": null, "saves": null }, "flags": { "pinned": false, "deleted": false, "likes_hidden": null }, "published_at": "2026-10-14T15:56:39.000Z", "ext": { "media_type": "video", "coauthors": [] } } } ], "total": 990416, "truncated": null }, "pagination": { "next_cursor": "v2c.hukrcTcxA8iy…", "has_more": true, "page_size": 21 }, "unavailable": ["items[].post.author.id", "items[].post.author.display_name"], "credits_used": 50 } ``` | Field | What it holds | | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | | `data.items[].post` | One tagged post in the shared [post shape](/docs/schema/post): caption, media and thumbnail URLs, duration, counts, publish time. | | `data.items[].post.kind` | `short` for a reel, `image` for a photo. | | `data.items[].post.author` | The account that posted it and tagged yours: username, avatar, verified badge. | | `data.items[].post.flags.likes_hidden` | `true` when the poster hid the like count; `engagement.likes` is then `null`. | | `data.total` | The total Instagram reports for the tagged list. 990,416 for mrbeast. | | `unavailable` | Fields this endpoint cannot fill: the poster's `id` and `display_name` are always `null`. Use the username. | ## How do I get more than one page? [#how-do-i-get-more-than-one-page] Send `pagination.next_cursor` back as `cursor`, with the same `handle`, and stop when `pagination.has_more` is `false`. Each page is 50 credits. ```python title="Python: walk the first 5 pages" import os import requests BASE = "https://api.insightsocial.app/v1" HEADERS = {"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]} params = {"handle": "mrbeast"} posts = [] for _ in range(5): body = requests.get(f"{BASE}/instagram/tagged", params=params, headers=HEADERS, timeout=120).json() posts += body["data"]["items"] if not body["pagination"]["has_more"]: break params["cursor"] = body["pagination"]["next_cursor"] ``` A large account can be tagged in hundreds of thousands of posts, so set a page limit before you start. See [Pagination](/docs/pagination) for the full rules. ## What are the limits? [#what-are-the-limits] * **Recent first, not strictly ordered.** In our test, all 21 posts on the first page were published within 42 minutes of each other on the day of the call, mostly newest first. For a large account, one page covers a short window. * **Hidden likes.** 10 of the 21 posts had `likes_hidden: true` and `engagement.likes: null`. `null` means unknown, not zero. * **Shares and saves.** `engagement.shares` and `engagement.saves` are `null` on this endpoint. * **Poster ID and name.** `author.id` and `author.display_name` are always `null`. Pass the username to `GET /v1/instagram/profile` for the full profile. * **Mostly low-reach posts.** Anyone can tag a large account. In our test, 15 of the 19 reels on the first page had under 20 views, so filter by engagement before you act on the list. ## What can I build with it? [#what-can-i-build-with-it] * **Brand mention monitoring.** Collect the posts that tag your brand each day. The [brand mention monitoring](/docs/recipes/brand-mention-monitoring) recipe shows the full loop. * **Creator discovery.** The accounts that tag a brand are fans and creators already posting about it. * **Campaign tracking.** Check which creators in a campaign tagged the brand, and how their posts did. ## Related [#related] * [`GET /v1/instagram/tagged` reference](/docs/api-reference/instagram/tagged): every parameter and field. * [`GET /v1/instagram/profile/posts`](/docs/api-reference/instagram/profile-posts): the posts the account published itself. * [Instagram endpoints](/docs/platforms/instagram): everything the API covers on Instagram. --- # Get the employees of a LinkedIn company (/docs/how-to/linkedin-company-employees) List the LinkedIn members who work at a company, with headline, location and profile URL, by company ID. 100 credits per page of 10. To list a company's employees on LinkedIn, call `GET /v1/linkedin/company/people` with the company's numeric `company_id`. It returns the members who list the company as their employer, 10 a page, each with name, headline, location, profile picture, profile URL and premium and Top Voice flags, plus the total number of such members. A plain page costs 100 credits. Metered at 100–500 credits a page. A plain page of 10 costs 100 credits, so 10 pages (100 people) cost 1,000 credits. Adding `include=profile` joins each row to the member's profile, so the call holds up to 500 credits when it starts and you're charged what it used. Send `dry_run=1` for a free quote. Asking for the same page again within 6 hours is answered from the shared cache for 2 credits. Failed calls and empty results are free. ## How do I find the company ID? [#how-do-i-find-the-company-id] Call `GET /v1/linkedin/company` (50 credits) with the company page URL, for example `https://www.linkedin.com/company/microsoft/`. It returns the numeric company ID that this endpoint needs. Microsoft's is `1035`. If you only know the company's website, `GET /v1/linkedin/company/by-domain` looks the page up from the domain. ## Make the request [#make-the-request] ```bash title="cURL" curl "https://api.insightsocial.app/v1/linkedin/company/people?company_id=1035" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os import requests res = requests.get( "https://api.insightsocial.app/v1/linkedin/company/people", params={"company_id": "1035"}, headers={"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}, timeout=120, ) body = res.json() print(body["data"]["total"], "members list this company") for item in body["data"]["items"]: person = item["author"] print(person["display_name"], "|", person["bio"], "|", person["location"], person["url"]) ``` ```ts title="Node.js" const url = new URL("https://api.insightsocial.app/v1/linkedin/company/people"); url.searchParams.set("company_id", "1035"); const body = await (await fetch(url, { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! } })).json(); console.log(`${body.data.total} members list this company`); for (const { author } of body.data.items) console.log(author.display_name, "|", author.bio, "|", author.location, author.url); ``` ## What comes back [#what-comes-back] A real response from 2026-10-14 for Microsoft (`company_id=1035`), shortened: one person instead of 10, long URLs and the cursor cut, and the member replaced with a placeholder. ```json title="Response (shortened)" { "success": true, "endpoint": "/v1/linkedin/company/people", "schema_version": "2", "data": { "items": [ { "author": { "id": "ACoAAAF7Yz…", "username": "example_user", "display_name": "Example User", "avatar_url": "https://media.licdn.com/dms/image/v2/…", "bio": "Software Engineer @ Microsoft", "verified": false, "followers": null, "followers_approximate": null, "following": null, "url": "https://www.linkedin.com/in/example_user", "location": "United States", "joined_at": null, "ext": { "urn": "ACoAAAF7Yz…", "is_top_voice": false, "is_premium": true } } } ], "total": 89216, "truncated": null }, "pagination": { "next_cursor": "v2c.s9XmZtLY…", "has_more": true, "page_size": 10 }, "unavailable": ["items[].author.following", "items[].author.joined_at"], "credits_used": 100 } ``` | Field | What it holds | | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | | `data.items[].author` | One member in the shared [author shape](/docs/schema/author): name, handle, avatar, profile URL. | | `author.bio` | The member's LinkedIn headline, usually their role and employer. | | `author.location` | Where the member says they are, such as `United States` or `Seattle, WA`. | | `author.followers` | Follower count. Filled on only a few rows, and then approximate (`followers_approximate: true`, for example `9000`). | | `author.ext.is_premium`, `is_top_voice` | Whether the member has LinkedIn Premium and the Top Voice badge. | | `data.total` | How many members list the company. 89,216 for Microsoft here. | ## How do I get more than 10 people? [#how-do-i-get-more-than-10-people] Send `pagination.next_cursor` back as `cursor`, with the same `company_id`, and stop when `pagination.has_more` is `false`. Each plain page is 100 credits. ```python title="Python: read up to 10 pages" import os import requests BASE = "https://api.insightsocial.app/v1" HEADERS = {"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]} params = {"company_id": "1035"} people = [] for _ in range(10): body = requests.get(f"{BASE}/linkedin/company/people", params=params, headers=HEADERS, timeout=120).json() people += [item["author"] for item in body["data"]["items"]] if not body["pagination"]["has_more"]: break params["cursor"] = body["pagination"]["next_cursor"] ``` A large company has tens of thousands of members, so set a page limit before you start. See [Pagination](/docs/pagination) for the full rules. ## What are the limits? [#what-are-the-limits] * **Self-reported employer.** The list is members who list the company on their profile. Headlines are the member's own text and can name a different employer: one row in our Microsoft test had a headline naming another company. Check `GET /v1/linkedin/profile/experiences` when current employment matters. * **Follower counts are sparse.** `followers` was `null` on 8 of 10 rows in our test, and approximate on the other 2. `null` means unknown, not zero. Add `include=profile` to get the exact follower count and the connection count (`following`) for every row, at a higher price. * **No join date.** `following` and `joined_at` are `null` on a plain call. * **`limit` skips rows.** `limit` (1 to 10) keeps the top N rows of a page, but the cursor still moves past all 10, so the skipped rows are not on the next page. * **No filters.** You can't filter by title or location here. Use `GET /v1/linkedin/search/people` with `current_company` and `title` to narrow the list. ## What can I build with it? [#what-can-i-build-with-it] * **Account-based prospecting.** List the people at a target account, then filter headlines for the roles you sell to. * **Hiring and talent research.** See which roles a company's members list and where they are based. * **Org snapshots.** Store `data.total` per company each month to track how many members list it, alongside `GET /v1/linkedin/company/employees-count`. ## Related [#related] * [`GET /v1/linkedin/company/people` reference](/docs/api-reference/linkedin/company-people): every parameter and field. * [`GET /v1/linkedin/company`](/docs/api-reference/linkedin/company): look up a company and get its `company_id`. * [`GET /v1/linkedin/search/people`](/docs/api-reference/linkedin/search-people): search members across LinkedIn with filters. * [LinkedIn endpoints](/docs/platforms/linkedin): everything the API covers on LinkedIn. --- # Get the posts in a LinkedIn group (/docs/how-to/linkedin-group-posts) Read a LinkedIn group's discussion feed: post text, author, headline, likes, comments, shares and publish time, by group ID. 50 credits per page. To read the posts in a LinkedIn group, call `GET /v1/linkedin/group/posts` with the group's numeric `group_id`. Each post comes back with its text, the author's name and headline, like, comment and share counts with a breakdown by reaction type, attached media, and when it was published. It costs 50 credits a page of up to 10 posts. LinkedIn's own Groups API is open only to approved partners, so for most developers this is the way to read a group feed. 50 credits per page of up to 10 posts. Reading 5 pages (50 posts) costs 250 credits. Asking for the same page again within 6 hours is answered from the shared cache for 2 credits. Failed calls and empty results are free. ## How do I find the group ID? [#how-do-i-find-the-group-id] The `group_id` is the number in the group's URL: `https://www.linkedin.com/groups/62438/` has the ID `62438`. Open the group on LinkedIn and copy the number after `/groups/`. To check a group before reading its feed, `GET /v1/linkedin/group` (50 credits) returns its name, description, member count, posting rules, owners, and whether it is public. ## Make the request [#make-the-request] ```bash title="cURL" curl "https://api.insightsocial.app/v1/linkedin/group/posts?group_id=62438" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os import requests res = requests.get( "https://api.insightsocial.app/v1/linkedin/group/posts", params={"group_id": "62438"}, headers={"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}, timeout=120, ) for item in res.json()["data"]["items"]: post = item["post"] print(post["published_at"], post["author"]["display_name"], post["engagement"]["likes"], post["url"]) ``` ```ts title="Node.js" const url = new URL("https://api.insightsocial.app/v1/linkedin/group/posts"); url.searchParams.set("group_id", "62438"); const body = await (await fetch(url, { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! } })).json(); for (const { post } of body.data.items) { console.log(post.published_at, post.author.display_name, post.engagement.likes, post.url); } ``` ## What comes back [#what-comes-back] A real response from 2026-10-14, shortened: one post instead of 10, long URLs cut, and the author replaced with a placeholder. ```json title="Response (shortened)" { "success": true, "endpoint": "/v1/linkedin/group/posts", "schema_version": "2", "data": { "items": [ { "post": { "id": "urn:li:groupPost:62438-7515763090810978304", "url": "https://www.linkedin.com/feed/update/urn:li:activity:7515763091653943296", "content": { "text": "This article examines the risks of autonomous AI agents…", "media_urls": null }, "author": { "id": "ACoAAACQPQ…", "username": "example_user", "display_name": "Example User", "avatar_url": "https://media.licdn.com/dms/image/v2/…" }, "engagement": { "views": null, "likes": 2, "comments": 0, "shares": 0, "saves": null }, "published_at": "2026-10-13T00:00:00.000Z", "ext": { "reaction_counts": [{ "count": 2, "type": "LIKE" }], "post_type": "ugc", "published_at_precision": "day", "author_headline": "Founder at an analytics consultancy", "author_type": "user" } } } ], "total": 10, "truncated": null }, "pagination": { "next_cursor": null, "has_more": false, "page_size": 10 }, "unavailable": [], "credits_used": 50 } ``` | Field | What it holds | | --------------------------------- | -------------------------------------------------------------------------------------------------------- | | `data.items[].post` | One group post in the shared [post shape](/docs/schema/post): text, media, author, counts, publish time. | | `post.id` | The group post's ID (`urn:li:groupPost:…`). | | `post.url` | The post's activity URL. Pass it to `post/comments` or `post/reactions` to read the discussion under it. | | `post.ext.reaction_counts` | Reactions split by type: `LIKE`, `PRAISE` and so on. | | `post.ext.author_headline` | The author's LinkedIn headline, so you see role and company without a profile call. | | `post.ext.published_at_precision` | How exact `published_at` is: `hour` or `day`. LinkedIn shows relative times, so the time is rounded. | ## How do I get older posts? [#how-do-i-get-older-posts] Send `pagination.next_cursor` back as `cursor`, with the same `group_id`, and stop when `pagination.has_more` is `false`. Each page is 50 credits. ```python title="Python: read up to 5 pages" import os import requests BASE = "https://api.insightsocial.app/v1" HEADERS = {"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]} params = {"group_id": "62438"} posts = [] for _ in range(5): body = requests.get(f"{BASE}/linkedin/group/posts", params=params, headers=HEADERS, timeout=120).json() posts += body["data"]["items"] if not body["pagination"]["has_more"]: break params["cursor"] = body["pagination"]["next_cursor"] ``` In our test on 2026-10-14 the group returned one page of 10 posts with `has_more: false`, so don't count on a deep history for every group. See [Pagination](/docs/pagination) for the full rules. ## What are the limits? [#what-are-the-limits] * **Rounded times.** `published_at` is rounded to the hour or the day; check `ext.published_at_precision` before comparing times. * **Order.** Posts came back mostly newest first, but not strictly: in our test the first post was a day older than the nine after it. Sort by `published_at` yourself if order matters. * **Feed depth.** The test group gave one page of 10 posts and no cursor. How far back a group's feed goes is not guaranteed. * **No view or save counts.** `engagement.views` and `engagement.saves` are `null`, which means unknown, not zero. * **Comments are a separate call.** Comment counts are included; the comments themselves come from `GET /v1/linkedin/post/comments` with the post's `url`. ## What can I build with it? [#what-can-i-build-with-it] * **Community monitoring.** Poll the groups in your industry daily and alert on posts that mention your brand, a competitor, or a problem you solve. * **Topic research.** Collect a group's posts and the reaction counts to see which topics its members respond to. * **Finding active members.** The authors and their headlines show who posts in a group, a starting list for partnerships or outreach. ## Related [#related] * [`GET /v1/linkedin/group/posts` reference](/docs/api-reference/linkedin/group-posts): every parameter and field. * [`GET /v1/linkedin/group`](/docs/api-reference/linkedin/group): the group's name, rules, member count and owners. * [`GET /v1/linkedin/post/comments`](/docs/api-reference/linkedin/post-comments): the comments under one group post. * [LinkedIn endpoints](/docs/platforms/linkedin): everything the API covers on LinkedIn. --- # Check when a LinkedIn member was last active (/docs/how-to/linkedin-last-active) Find when a LinkedIn member last showed public activity, such as a post, with an exact timestamp, by profile URL. 50 credits per call. To check when a LinkedIn member was last active, call `GET /v1/linkedin/profile/activity` with their profile `url`. It returns the time of their most recent public activity, what kind of activity it was (for example `post`), and LinkedIn's short "how long ago" label such as `2w`. It costs 50 credits a call. It answers "is this person still active on LinkedIn?" without pulling their whole feed. 50 credits per call. Checking 100 profiles costs 5,000 credits. Asking for the same profile again within 24 hours is answered from the shared cache for 2 credits. Failed calls and empty results are free. ## How do I find the profile URL? [#how-do-i-find-the-profile-url] Use the member's public profile address, the one in the browser bar on their profile: `https://www.linkedin.com/in/adamselipsky/`. If you have a name but no URL, `GET /v1/linkedin/search/people` finds members by keyword, and every person row other endpoints return carries the profile link in `author.url`. ## Make the request [#make-the-request] ```bash title="cURL" curl -G "https://api.insightsocial.app/v1/linkedin/profile/activity" \ --data-urlencode "url=https://www.linkedin.com/in/adamselipsky/" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os from datetime import datetime, timezone import requests res = requests.get( "https://api.insightsocial.app/v1/linkedin/profile/activity", params={"url": "https://www.linkedin.com/in/adamselipsky/"}, headers={"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}, timeout=120, ) data = res.json()["data"] last = datetime.fromisoformat(data["timestamp"].replace("Z", "+00:00")) days = (datetime.now(timezone.utc) - last).days print(f"Last {data['type']} {days} days ago ({data['recent_activity']})") ``` ```ts title="Node.js" const url = new URL("https://api.insightsocial.app/v1/linkedin/profile/activity"); url.searchParams.set("url", "https://www.linkedin.com/in/adamselipsky/"); const { data } = await (await fetch(url, { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! } })).json(); const days = Math.floor((Date.now() - Date.parse(data.timestamp)) / 86_400_000); console.log(`Last ${data.type} ${days} days ago (${data.recent_activity})`); ``` ## What comes back [#what-comes-back] A real response from 2026-10-14, complete apart from the billing fields at the end. ```json title="Response" { "success": true, "endpoint": "/v1/linkedin/profile/activity", "schema_version": "2", "data": { "date": "2026-09-29 00:28:48.785 +0000 UTC", "recent_activity": "2w", "timestamp": "2026-09-29T00:28:48.785Z", "type": "post" }, "unavailable": [], "credits_used": 50 } ``` | Field | What it holds | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | `data.timestamp` | When the most recent public activity happened, as an ISO 8601 UTC time. Use this one for date math. | | `data.date` | The same moment as a plain text string. | | `data.type` | What the activity was. Here `post`: the member's latest public activity was a post. | | `data.recent_activity` | LinkedIn's relative label for how long ago it was. `2w` here: two weeks before the call, which matches the timestamp (15 days). | ## What are the limits? [#what-are-the-limits] * **Public activity, not logins.** The endpoint reports the latest activity LinkedIn shows publicly, such as a post. It does not tell you when someone last logged in, read their feed or viewed a profile. A member who reads LinkedIn daily but never posts can look inactive. * **One event, not a feed.** You get the time and type of the latest activity only, not its content. Use `GET /v1/linkedin/profile/posts` for the posts themselves. * **Coarse label.** `recent_activity` is a rounded label like `2w`. Compute recency from `timestamp` if you need days. * **Cached for up to 24 hours.** A call inside that window can be answered from the shared cache, so very recent activity may not show yet. Add `fresh=1` to skip the cache and pay the full 50 credits. See [Caching](/docs/caching). ## What can I build with it? [#what-can-i-build-with-it] * **Lead list hygiene.** Before outreach, check each prospect and drop or deprioritize the ones with no public activity in months. A post this month means a message is more likely to be seen. * **Engagement timing.** Contact people shortly after they post, while they are checking their notifications. * **Account monitoring.** Track the last-active date of a competitor's executives or key customers' champions across weeks to notice who has gone quiet. ## Related [#related] * [`GET /v1/linkedin/profile/activity` reference](/docs/api-reference/linkedin/profile-activity): every parameter and field. * [`GET /v1/linkedin/profile/posts`](/docs/api-reference/linkedin/profile-posts): the member's posts, once you know they are active. * [`GET /v1/linkedin/profile`](/docs/api-reference/linkedin/profile): their name, headline, location and follower count. * [LinkedIn endpoints](/docs/platforms/linkedin): everything the API covers on LinkedIn. --- # Get the people who reacted to or reposted a LinkedIn post (/docs/how-to/linkedin-post-reactions-and-reposts) List who reacted to a LinkedIn post, with each reaction type, and who reposted it, by post URL. 100 credits a page of reactors, 50 a page of reposts. To see who engaged with a LinkedIn post, call `GET /v1/linkedin/post/reactions` with the post's `url` for the people who reacted, and `GET /v1/linkedin/post/reposts` with the same `url` for the people who reposted it. Reactions return each person's name, headline, profile URL and the reaction they left (like, praise, empathy and so on). Reposts return each reshare with the resharer and any text they added. A page of reactors costs 100 credits; a page of reposts costs 50. For the people who commented, use `GET /v1/linkedin/post/comments` (50 credits a page). `post/reactions` is metered at 100–500 credits a page. A plain page costs 100 credits. Adding `include=profile` joins each row to the member's profile, so the call holds up to 500 credits when it starts and you're charged what it used. Send `dry_run=1` for a free quote. `post/reposts` is 50 credits per page of 10. Asking for the same page again within 6 hours is answered from the shared cache for 2 credits. Failed calls and empty results are free. ## How do I find the post URL? [#how-do-i-find-the-post-url] The `url` has to carry the post's activity ID, in one of two forms: * `https://www.linkedin.com/feed/update/urn:li:activity:7501466755261820928` * `https://www.linkedin.com/posts/-activity--` On LinkedIn, open the post's menu and choose "Copy link to post". If you got the post from another endpoint (`profile/posts`, `company/posts`, `search/posts`), copy its `post.url` field. Don't rebuild the URL from `post.id`, which is not always the activity ID. ## Make the request [#make-the-request] ```bash title="cURL" POST_URL="https://www.linkedin.com/feed/update/urn:li:activity:7501466755261820928" curl -G "https://api.insightsocial.app/v1/linkedin/post/reactions" \ --data-urlencode "url=$POST_URL" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" curl -G "https://api.insightsocial.app/v1/linkedin/post/reposts" \ --data-urlencode "url=$POST_URL" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os import requests BASE = "https://api.insightsocial.app/v1" HEADERS = {"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]} POST_URL = "https://www.linkedin.com/feed/update/urn:li:activity:7501466755261820928" reactions = requests.get(f"{BASE}/linkedin/post/reactions", params={"url": POST_URL}, headers=HEADERS, timeout=120).json() for item in reactions["data"]["items"]: person = item["author"] print(person["ext"]["reaction_type"], person["display_name"], person["url"]) reposts = requests.get(f"{BASE}/linkedin/post/reposts", params={"url": POST_URL}, headers=HEADERS, timeout=120).json() print(reposts["data"]["total"], "reposts") for item in reposts["data"]["items"]: post = item["post"] print(post["author"]["display_name"], "added text" if post["ext"]["is_repost_quote"] else "plain repost") ``` ```ts title="Node.js" const BASE = "https://api.insightsocial.app/v1"; const headers = { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! }; const postUrl = "https://www.linkedin.com/feed/update/urn:li:activity:7501466755261820928"; const get = async (path: string) => { const url = new URL(`${BASE}${path}`); url.searchParams.set("url", postUrl); return (await fetch(url, { headers })).json(); }; const reactions = await get("/linkedin/post/reactions"); for (const { author } of reactions.data.items) console.log(author.ext.reaction_type, author.display_name, author.url); const reposts = await get("/linkedin/post/reposts"); console.log(`${reposts.data.total} reposts`); for (const { post } of reposts.data.items) console.log(post.author.display_name, post.ext.is_repost_quote); ``` ## What comes back [#what-comes-back] Real responses from 2026-10-14, shortened: one or two rows instead of a full page, long URLs and cursors cut, and private members replaced with placeholders. ```json title="post/reactions response (shortened)" { "success": true, "endpoint": "/v1/linkedin/post/reactions", "schema_version": "2", "data": { "items": [ { "author": { "id": "ACoAAD7nmw…", "username": null, "display_name": "Example User", "avatar_url": "https://media.licdn.com/dms/image/v2/…", "bio": "Data analyst | Business intelligence", "verified": null, "followers": null, "url": "https://www.linkedin.com/in/ACoAAD7nmw…", "ext": { "urn": "ACoAAD7nmw…", "reaction_type": "LIKE", "member_id": "1055365895" } } } ], "total": null, "truncated": null }, "pagination": { "next_cursor": "v2c.BLUP7GMD…", "has_more": true, "page_size": 44 }, "unavailable": [ "items[].author.verified", "items[].author.followers", "items[].author.following", "items[].author.joined_at" ], "credits_used": 100 } ``` ```json title="post/reposts response (shortened)" { "success": true, "endpoint": "/v1/linkedin/post/reposts", "schema_version": "2", "data": { "items": [ { "post": { "id": "7511864524241833985", "url": "https://www.linkedin.com/feed/update/urn:li:activity:7511864524241833985", "content": { "text": null }, "author": { "id": "ACoAACZmjG…", "username": "example_user", "display_name": "Example User" }, "engagement": { "likes": 3758, "comments": 349, "shares": 318 }, "published_at": null, "ext": { "author_type": "user", "is_repost_quote": false } } }, { "post": { "id": "7509225672431562753", "url": "https://www.linkedin.com/feed/update/urn:li:activity:7509225672431562753", "content": { "text": "This long and invasive pathways belongs to the past. Today, a simple blood test…" }, "author": { "id": "28438218", "username": "roche-belux", "display_name": "Roche Belgium & Luxembourg" }, "engagement": { "likes": 5, "comments": 0, "shares": 0 }, "published_at": null, "ext": { "author_followers": 15816, "author_type": "company", "is_repost_quote": true, "published_at_precision": "week" } } } ], "total": 318, "truncated": null }, "pagination": { "next_cursor": "v2c.6dxQa3CY…", "has_more": true, "page_size": 10 }, "unavailable": ["items[].post.published_at"], "credits_used": 50 } ``` | Field | What it holds | | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | `reactions: data.items[].author` | One person or page that reacted, in the shared [author shape](/docs/schema/author): name, headline (`bio`), avatar, profile URL. | | `reactions: author.ext.reaction_type` | The reaction they left: `LIKE`, `PRAISE`, `EMPATHY`, `APPRECIATION`, `INTEREST` and others. | | `reactions: author.ext.member_id` | LinkedIn's numeric member ID. Company pages that react have no `member_id`; their `url` is the company page. | | `reposts: data.items[].post` | One reshare in the shared [post shape](/docs/schema/post), with the resharer in `post.author`. | | `reposts: post.ext.is_repost_quote` | `true` when the resharer added their own text. Then `content.text` is that text and `engagement` counts their reshare. | | `reposts: data.total` | How many reposts the post has. 318 here, the same as the original post's share count. | | `reposts: post.ext.author_type` | `user` for a member, `company` for a company page. | ## How do I get every reactor and reposter? [#how-do-i-get-every-reactor-and-reposter] Both endpoints paginate. Send `pagination.next_cursor` back as `cursor`, with the same `url`, and stop when `pagination.has_more` is `false`. Each reactions page costs 100 credits (more with `include=profile`); each reposts page costs 50. ```python title="Python: walk every reactor, up to 20 pages" import os import requests BASE = "https://api.insightsocial.app/v1" HEADERS = {"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]} params = {"url": "https://www.linkedin.com/feed/update/urn:li:activity:7501466755261820928"} people = [] for _ in range(20): body = requests.get(f"{BASE}/linkedin/post/reactions", params=params, headers=HEADERS, timeout=120).json() people += [item["author"] for item in body["data"]["items"]] if not body["pagination"]["has_more"]: break params["cursor"] = body["pagination"]["next_cursor"] ``` To list only one kind of reaction, add `type` (`like`, `praise`, `empathy`, `appreciation`, `interest`, or `all`). See [Pagination](/docs/pagination) for the full rules. ## What are the limits? [#what-are-the-limits] * **Reactor handles.** Most reactor rows have `username: null`, and `url` points to the member's ID-based profile link (`/in/ACoAA…`) rather than their vanity handle. The link still opens the profile. * **Follower counts.** `followers`, `following`, `verified` and `joined_at` are `null` on reactor rows, which means unknown, not zero. Add `include=profile` to fill the exact follower count and connection count from each member's profile, at a higher price. * **No reactions total.** `data.total` is `null` on `post/reactions`. The post's like, comment and share counts are on `GET /v1/linkedin/post`. * **Plain reposts carry the original's numbers.** On a repost without added text, `content.text` is `null` and `engagement` repeats the original post's likes, comments and shares. Only quote reposts (`is_repost_quote: true`) have their own text and counts. * **No repost dates.** `published_at` is `null` on every repost row. * **Repeat reposters.** The same member can appear more than once if they reshared the post several times. Deduplicate on `post.author.id` if you want people, not reshares. ## What can I build with it? [#what-can-i-build-with-it] * **Lead lists from engagement.** The people who react to a competitor's or a thought leader's post are an audience already interested in the topic. Their headlines tell you role and company. * **Campaign reach reports.** For your own post, count reactions by `reaction_type` and list the company pages and members who reshared it with their own commentary. * **Advocate tracking.** Run the reposts endpoint across a company's recent posts to see which employees and partners reshare most often. ## Related [#related] * [`GET /v1/linkedin/post/reactions` reference](/docs/api-reference/linkedin/post-reactions): every parameter and field. * [`GET /v1/linkedin/post/reposts` reference](/docs/api-reference/linkedin/post-reposts): every parameter and field. * [`GET /v1/linkedin/post/comments`](/docs/api-reference/linkedin/post-comments): the people who commented on the post. * [LinkedIn endpoints](/docs/platforms/linkedin): everything the API covers on LinkedIn. --- # Get Pinterest save counts for any URL (/docs/how-to/pinterest-save-counts-for-urls) See how many times up to 10 web pages have been saved to Pinterest through the Save button, in one call. 10 credits per call. To see how many times a web page has been saved to Pinterest, call `GET /v1/pinterest/url-stats` with `urls`, a comma-separated list of 1 to 10 page URLs. It returns each URL with its save count, the number of times it was saved to Pinterest through the Save button. It costs 10 credits per call, whether you send one URL or 10. 10 credits per call of up to 10 URLs. Checking 100 pages takes 10 calls and costs 100 credits. Asking for the same URLs again within 6 hours is answered from the shared cache for 2 credits. Failed calls and empty results are free. ## Which URL do I pass? [#which-url-do-i-pass] Pass the full address, starting with `http://` or `https://`, exactly as people share it. The URLs go to Pinterest verbatim, and Pinterest counts each variant separately: `http` and `https`, with and without a trailing slash, and with and without a query string are different URLs with different counts. If you are not sure which form gets pinned, send the variants together in one call; they all fit in the same 10 credits. ## Make the request [#make-the-request] ```bash title="cURL" curl -G "https://api.insightsocial.app/v1/pinterest/url-stats" \ --data-urlencode "urls=https://www.allrecipes.com/recipe/10813/best-chocolate-chip-cookies/" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os import requests urls = [ "https://www.allrecipes.com/recipe/10813/best-chocolate-chip-cookies/", "https://www.allrecipes.com/recipe/10813/best-chocolate-chip-cookies", ] res = requests.get( "https://api.insightsocial.app/v1/pinterest/url-stats", params={"urls": ",".join(urls)}, headers={"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}, timeout=120, ) for item in res.json()["data"]["items"]: print(item["saves"], item["url"]) ``` ```ts title="Node.js" const urls = [ "https://www.allrecipes.com/recipe/10813/best-chocolate-chip-cookies/", "https://www.allrecipes.com/recipe/10813/best-chocolate-chip-cookies", ]; const url = new URL("https://api.insightsocial.app/v1/pinterest/url-stats"); url.searchParams.set("urls", urls.join(",")); const body = await (await fetch(url, { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! } })).json(); for (const item of body.data.items) console.log(item.saves, item.url); ``` ## What comes back [#what-comes-back] A real response from 2026-10-14 for one URL. ```json title="Response" { "success": true, "endpoint": "/v1/pinterest/url-stats", "schema_version": "2", "data": { "items": [ { "url": "https://www.allrecipes.com/recipe/10813/best-chocolate-chip-cookies/", "saves": 35108, "pinterest": { "url": "https://www.allrecipes.com/recipe/10813/best-chocolate-chip-cookies/", "count": 35108 } } ], "total": 1 }, "pagination": { "next_cursor": null, "has_more": false, "page_size": 1 }, "unavailable": [], "credits_used": 10 } ``` | Field | What it holds | | ------------------------ | ----------------------------------------------------------------------------------------------- | | `data.items[].url` | The URL you sent. | | `data.items[].saves` | How many times that exact URL has been saved to Pinterest through the Save button. 35,108 here. | | `data.items[].pinterest` | The raw pair Pinterest returned: the URL it counted and its `count`. | | `data.total` | How many URLs the response covers. | ## What are the limits? [#what-are-the-limits] * **10 URLs per call.** Split longer lists into batches of 10. The endpoint does not paginate. * **Exact URLs only.** Each variant of a page has its own count. Add up the variants yourself if you want one number per page. * **Save button saves.** The count is what Pinterest reports for saves of the URL through its Save button. It says nothing about who saved it or when. * **Absolute URLs.** Each entry must start with `http://` or `https://`. ## What can I build with it? [#what-can-i-build-with-it] * **Content audits.** Rank a site's recipes, articles or product pages by Pinterest saves to see which ones travel on Pinterest. * **Competitor benchmarking.** Compare save counts for your pages and a competitor's pages on the same topic. * **Tracking over time.** Store the counts weekly for your key pages and watch which ones keep gaining saves. ## Related [#related] * [`GET /v1/pinterest/url-stats` reference](/docs/api-reference/pinterest/url-stats): every parameter and field. * [`GET /v1/pinterest/search`](/docs/api-reference/pinterest/search): find the pins about a topic; `include=engagement` adds each pin's save count. * [Pinterest endpoints](/docs/platforms/pinterest): everything the API covers on Pinterest. --- # Get Pinterest Trends for a country (/docs/how-to/pinterest-trends) List the top Pinterest search terms for one of 23 countries, with rank, search index and change, growing or seasonal. 100 credits per call. To see what a market is searching for on Pinterest, call `GET /v1/pinterest/trends` with a `country` code and a `type`. It returns up to 50 ranked search terms from Pinterest Trends, each with a relative search index, its weekly, monthly and yearly change, a seasonality score and the date of the list. It costs 100 credits per call, whether you ask for 5 terms or 50. You need no keyword to start from. 100 credits per call, at any `limit` from 1 to 50. Pulling all four tables for one country costs 400 credits. Asking for the same list again within 6 hours is answered from the shared cache for 2 credits. Failed calls and empty results are free, and so is a request for an unsupported country (a 400). ## Which parameters can I set? [#which-parameters-can-i-set] | Parameter | Values | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `country` | `AR`, `AU`, `BR`, `CA`, `CO`, `DE`, `EG`, `ES`, `FR`, `GB`, `ID`, `IE`, `IN`, `IT`, `KR`, `MX`, `MY`, `NZ`, `PH`, `SA`, `TH`, `TR`, `US`, in any case. Defaults to `US`. | | `type` | Which Trends table: `growing` (the default), `seasonal`, `top_monthly` or `top_yearly`. | | `limit` | How many terms to return from the top, 1 to 50. Defaults to 25. | | `include` | Keep only terms that contain one of these keywords, comma-separated, up to 10 (for example `nails,wedding`). | | `exclude` | Drop terms that contain any of these keywords, comma-separated, up to 10 (for example `fall`). | Use `growing` for terms picking up now, `seasonal` for terms that return at this time of year, and `top_monthly` or `top_yearly` for the most searched terms over a month or a year. ## Make the request [#make-the-request] ```bash title="cURL" curl "https://api.insightsocial.app/v1/pinterest/trends?country=US&type=growing&limit=10" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os import requests res = requests.get( "https://api.insightsocial.app/v1/pinterest/trends", params={"country": "US", "type": "growing", "limit": 10}, headers={"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}, timeout=120, ) for item in res.json()["data"]["items"]: print(item["rank"], item["term"], item["search_index"], item["as_of"]) ``` ```ts title="Node.js" const url = new URL("https://api.insightsocial.app/v1/pinterest/trends"); url.searchParams.set("country", "US"); url.searchParams.set("type", "growing"); url.searchParams.set("limit", "10"); const body = await (await fetch(url, { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! } })).json(); for (const t of body.data.items) console.log(t.rank, t.term, t.search_index, t.as_of); ``` ## What comes back [#what-comes-back] A real response from 2026-10-14 (`country=US`, `type=growing`, `limit=10`), shortened to two of the 10 terms. ```json title="Response (shortened)" { "success": true, "endpoint": "/v1/pinterest/trends", "schema_version": "2", "data": { "items": [ { "rank": 3, "term": "october nails ideas 2026", "country": "US", "market": "US", "type": "growing", "search_index": 37, "weekly_change": 0.06, "monthly_change": 100.01, "yearly_change": 100.01, "seasonality": 0.32323614, "url": "https://trends.pinterest.com/detail/?terms=october+nails+ideas+2026&country=US", "as_of": "2026-10-05" }, { "rank": 6, "term": "happy october", "country": "US", "market": "US", "type": "growing", "search_index": 39, "weekly_change": 55, "monthly_change": 100.01, "yearly_change": -0.08, "seasonality": 0.8605102, "url": "https://trends.pinterest.com/detail/?terms=happy+october&country=US", "as_of": "2026-10-05" } ], "total": 10 }, "pagination": { "next_cursor": null, "has_more": false, "page_size": 10 }, "unavailable": [], "credits_used": 100 } ``` | Field | What it holds | | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | `rank` | The term's position in the table. | | `term` | The search term. | | `market` | Which Pinterest list the term came from. It can differ from `country`: `IE` reads the GB+IE list, `AU` and `NZ` read the AU+NZ list. | | `search_index` | Pinterest's relative search index for the term. In this response the highest value is 100. | | `weekly_change`, `monthly_change`, `yearly_change` | The change Pinterest reports for the term over each period. | | `seasonality` | Pinterest's seasonality score. Here the October greetings score about 0.86 to 0.88; most other terms score about 0.32. | | `url` | The term's page on Pinterest Trends. | | `as_of` | The date of the Trends list. | ## What are the limits? [#what-are-the-limits] * **23 countries.** Pinterest publishes no list for other countries; asking for one returns a free 400. * **Shared lists.** `IE` returns Pinterest's GB+IE list and `AU` and `NZ` return the AU+NZ list. `GB` alone has its own list. * **50 terms at most.** `limit` goes from 1 to 50. The endpoint does not paginate. * **Not real time.** The list is dated: the response captured on 2026-10-14 carried `as_of: "2026-10-05"`. * **Read the change fields with care.** No unit is documented, and in the sample most growing terms show the same `monthly_change` of 100.01. Compare terms by `rank` and `search_index` first. ## What can I build with it? [#what-can-i-build-with-it] * **Content calendars.** Pull the `seasonal` table each week and plan pins and blog posts around the terms that are coming back. * **Product and niche research.** Filter with `include` (for example `nails,wedding`) to watch only the terms in your category, across several countries. * **Trend alerts.** Store the `growing` table daily and flag terms that enter it or climb in `rank`. ## Related [#related] * [`GET /v1/pinterest/trends` reference](/docs/api-reference/pinterest/trends): every parameter and field. * [`GET /v1/pinterest/search`](/docs/api-reference/pinterest/search): the pins behind a trending term. * [Pinterest endpoints](/docs/platforms/pinterest): everything the API covers on Pinterest. --- # Read on-screen text from TikTok and Instagram posts (/docs/how-to/read-on-screen-text-from-videos) Get the text burned into a TikTok video or shown on an Instagram post's images, read by OCR, from the post URL. 50 credits per post. To read the text shown on a TikTok video, call `GET /v1/tiktok/video/screen-text` with the video's `url`: it returns TikTok's native text stickers plus AI OCR of the cover frame, so captions burned in with an editor are caught too. For Instagram, call `GET /v1/instagram/media/screen-text` with the post's `url`: it reads the photo, each carousel slide up to 10, or a reel's cover frame, and returns the text per slide. Both cost 50 credits per post. 50 credits per post on either platform. Reading 100 posts costs 5,000 credits. Asking for the same URL again within 6 hours is answered from the shared cache for 2 credits. Failed calls and empty results are free. ## Which text does each endpoint read? [#which-text-does-each-endpoint-read] A post carries text in three places, and each has its own endpoint. | Text | TikTok | Instagram | | -------------- | ----------------------------------------------- | -------------------------------------------------- | | Caption | `GET /v1/tiktok/post` (10 credits) | `GET /v1/instagram/post` (10 credits) | | Spoken words | `GET /v1/tiktok/post/transcript` (100 credits) | `GET /v1/instagram/media/transcript` (100 credits) | | Text on screen | `GET /v1/tiktok/video/screen-text` (50 credits) | `GET /v1/instagram/media/screen-text` (50 credits) | On TikTok, text typed with TikTok's own text tool is also on `GET /v1/tiktok/post` as `post.ext.on_screen_texts`. Use screen-text when you also need text burned into the video by an outside editor, which only OCR can see. ## How do I find the URL? [#how-do-i-find-the-url] Copy the post's link from the app or browser. * **TikTok:** a `/video/` or `/photo/` link, such as `https://www.tiktok.com/@stoolpresidente/video/7499229683859426602`. * **Instagram:** a `/p/`, `/reel/`, `/reels/` or `/tv/` link, such as `https://www.instagram.com/p/DdEqEl5Dl-S/`. ## Make the request [#make-the-request] ```bash title="cURL" # TikTok video curl "https://api.insightsocial.app/v1/tiktok/video/screen-text?url=https://www.tiktok.com/@stoolpresidente/video/7499229683859426602" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" # Instagram post or carousel curl "https://api.insightsocial.app/v1/instagram/media/screen-text?url=https://www.instagram.com/p/DdEqEl5Dl-S/" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os import requests BASE = "https://api.insightsocial.app/v1" HEADERS = {"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]} tiktok = requests.get( f"{BASE}/tiktok/video/screen-text", params={"url": "https://www.tiktok.com/@stoolpresidente/video/7499229683859426602"}, headers=HEADERS, timeout=120, ).json() print("TikTok:", tiktok["data"]["texts"]) instagram = requests.get( f"{BASE}/instagram/media/screen-text", params={"url": "https://www.instagram.com/p/DdEqEl5Dl-S/"}, headers=HEADERS, timeout=120, ).json() for slide in instagram["data"]["images"]: print("Slide", slide["index"], slide["texts"]) ``` ```ts title="Node.js" const BASE = "https://api.insightsocial.app/v1"; const headers = { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! }; const tt = new URL(`${BASE}/tiktok/video/screen-text`); tt.searchParams.set("url", "https://www.tiktok.com/@stoolpresidente/video/7499229683859426602"); const tiktok = await (await fetch(tt, { headers })).json(); console.log("TikTok:", tiktok.data.texts); const ig = new URL(`${BASE}/instagram/media/screen-text`); ig.searchParams.set("url", "https://www.instagram.com/p/DdEqEl5Dl-S/"); const instagram = await (await fetch(ig, { headers })).json(); for (const slide of instagram.data.images) console.log("Slide", slide.index, slide.texts); ``` ## What comes back [#what-comes-back] Real responses from 2026-10-14. The TikTok one is complete: ```json title="TikTok response" { "success": true, "endpoint": "/v1/tiktok/video/screen-text", "schema_version": "2", "data": { "video_id": "7499229683859426602", "url": "https://www.tiktok.com/@stoolpresidente/video/7499229683859426602", "texts": ["Delicious Pizza"], "native_texts": null, "ocr_texts": ["Delicious Pizza"], "source": "ocr", "media_type": "video", "frame": "cover" }, "unavailable": [], "credits_used": 50 } ``` | Field | What it holds | | ------------------- | ----------------------------------------------------------------------------- | | `data.texts` | All the text found on the video, native and OCR combined. | | `data.native_texts` | Text added with TikTok's text tool. `null` here, so everything came from OCR. | | `data.ocr_texts` | Text read by AI from the image. | | `data.source` | Where the text came from: `ocr` in this response. | | `data.frame` | Which frame OCR read: `cover`. | The Instagram one, a 20-slide carousel, shortened: 2 of the 10 slides it read, and image URLs cut. ```json title="Instagram response (shortened)" { "success": true, "endpoint": "/v1/instagram/media/screen-text", "schema_version": "2", "data": { "post_id": "3982493004019425170", "shortcode": "DdEqEl5Dl-S", "url": "https://www.instagram.com/p/DdEqEl5Dl-S/", "media_type": "carousel", "texts": [ "\"Ana, dein Speicherplatz ist voll.", "Du musst Fotos löschen\"", "...", "Ce KLAUSE", "Happy\nBirth Day", "Notfall: 81", "AK: 5122", "OA: 5140" ], "images": [ { "index": 0, "image_url": "https://scontent-ord5-1.cdninstagram.com/…", "texts": ["\"Ana, dein Speicherplatz ist voll.", "Du musst Fotos löschen\""] }, { "index": 2, "image_url": "https://scontent-ord5-2.cdninstagram.com/…", "texts": [] } ], "source": "ocr", "frame": null, "slide_count": 20 }, "unavailable": [], "credits_used": 50 } ``` | Field | What it holds | | ------------------ | ----------------------------------------------------------------------------------------------------------------- | | `data.texts` | The text from every slide read, in slide order. | | `data.images[]` | One entry per slide read: its `index`, `image_url` and the `texts` on it. A slide with no text has an empty list. | | `data.slide_count` | How many slides the post has: 20 here, of which the first 10 were read. | | `data.media_type` | The kind of post: `carousel` here. | | `data.frame` | `null` for a carousel; for a reel, the frame read is the cover. | ## What are the limits? [#what-are-the-limits] * **Videos: the cover frame only.** On both platforms OCR reads only the cover frame of a video, so burned-in text that appears later in a TikTok or a reel is not read. * **Carousels: the first 10 slides.** The sample post has 20 slides; `images` holds 10. * **Lines, not sentences.** OCR returns text in the lines it sees, so one sentence can arrive as two entries, and an entry can contain a line break (`"Happy\nBirth Day"`). Join them yourself if you need full sentences. * **Not the caption, not the audio.** Use the post and transcript endpoints in the table above for those. ## What can I build with it? [#what-can-i-build-with-it] * **Hook analysis.** Collect the text on the cover frame of top-performing videos in a niche and compare which hooks get the most views. * **Carousel research.** Pull every slide's headline from a competitor's carousels to see what topics and formats they publish. * **Searchable archive.** Store caption, transcript and on-screen text together so a search finds a post by any words it shows. ## Related [#related] * [`GET /v1/tiktok/video/screen-text` reference](/docs/api-reference/tiktok/video-screen-text) and [`GET /v1/instagram/media/screen-text` reference](/docs/api-reference/instagram/media-screen-text): every parameter and field. * [`GET /v1/tiktok/post/transcript`](/docs/api-reference/tiktok/post-transcript) and [`GET /v1/instagram/media/transcript`](/docs/api-reference/instagram/media-transcript): the spoken words instead. * [Video transcription recipe](/docs/recipes/video-transcription-api): transcripts across platforms. * [TikTok endpoints](/docs/platforms/tiktok) and [Instagram endpoints](/docs/platforms/instagram). --- # Search Reddit comments by keyword (/docs/how-to/reddit-comment-search) Search comments across all of Reddit by keyword, author or subreddit, each with its post title, subreddit and score. 10 credits per page. To search Reddit comments by keyword, call `GET /v1/reddit/search/comments` with a `query`. Each match comes back with the comment's text, author, score and time, plus the post it sits under: the post's title, URL, score, comment count and subreddit. It costs 10 credits a page. Reddit's search operators work in the query, so `author:username` lists one account's comments and `subreddit:name terms` keeps the search inside one community. 10 credits per page (24 comments in our test). Reading 10 pages costs 100 credits. Asking for the same page again within 1 hour is answered from the shared cache for 2 credits. Failed calls and empty results are free. ## How do I write the query? [#how-do-i-write-the-query] The `query` is the same text you would type in Reddit's search box, with three operators: | Query | Finds | | ---------------------------------- | ----------------------------------------------- | | `best project management software` | Comments that match these words | | `"project management software"` | Comments with the exact phrase | | `subreddit:projectmanagement jira` | Comments about Jira in r/projectmanagement only | | `author:example_user` | Comments by one account | Add `sort` to choose the order: `relevance`, `top` or `new`. ## Make the request [#make-the-request] ```bash title="cURL" curl -G "https://api.insightsocial.app/v1/reddit/search/comments" \ --data-urlencode "query=best project management software" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os import requests res = requests.get( "https://api.insightsocial.app/v1/reddit/search/comments", params={"query": "best project management software"}, headers={"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}, timeout=120, ) for item in res.json()["data"]["items"]: comment = item["comment"] print(f"r/{comment['ext']['subreddit']}", comment["engagement"]["likes"], comment["text"][:80], comment["url"]) ``` ```ts title="Node.js" const url = new URL("https://api.insightsocial.app/v1/reddit/search/comments"); url.searchParams.set("query", "best project management software"); const body = await (await fetch(url, { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! } })).json(); for (const { comment } of body.data.items) { console.log(`r/${comment.ext.subreddit}`, comment.engagement.likes, comment.text.slice(0, 80), comment.url); } ``` ## What comes back [#what-comes-back] A real response from 2026-10-14, shortened: one comment instead of 24, the text and post title shortened, long URLs and the cursor cut, and Reddit usernames replaced with placeholders. ```json title="Response (shortened)" { "success": true, "endpoint": "/v1/reddit/search/comments", "schema_version": "2", "data": { "items": [ { "comment": { "id": "pfc2aq6", "url": "https://www.reddit.com/r/BetterOffline/comments/1x3l1fn/comment/pfc2aq6/", "parent_id": null, "post_id": "1x3l1fn", "text": "I look forward to people rediscovering software engineering methods from 20 years ago…", "author": { "id": "t2_…", "username": "example_user", "display_name": null, "avatar_url": "https://styles.redditmedia.com/…" }, "engagement": { "likes": 174, "replies": null }, "flags": { "pinned": null, "deleted": false }, "published_at": "2026-10-11T22:18:34.073Z", "language": "en", "ext": { "post_title": "The people who doubted me now ask me to clean up their LLM mess…", "post_url": "https://www.reddit.com/r/BetterOffline/comments/t3_1x3l1fn/", "subreddit": "BetterOffline", "subreddit_subscribers": 58872, "post_score": 523, "post_comment_count": 156, "post_author": "example_poster", "post_published_at": "2026-10-11T22:11:46.852Z" } } } ], "total": null, "truncated": null }, "pagination": { "next_cursor": "v2c.yQV705tt…", "has_more": true, "page_size": 24 }, "unavailable": ["items[].comment.engagement.replies"], "credits_used": 10 } ``` | Field | What it holds | | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- | | `data.items[].comment` | One matching comment in the shared [comment shape](/docs/schema/comment): text, author, score, time, link. | | `comment.engagement.likes` | The comment's score as Reddit shows it. | | `comment.post_id` | The ID of the post the comment is under. | | `comment.ext.post_title`, `post_url`, `post_score`, `post_comment_count` | The parent post, inline, so you don't need a second call to see what thread the comment is in. | | `comment.ext.subreddit`, `subreddit_subscribers` | The community and its size. | | `comment.language` | The detected language, or `und` when it could not be determined. | ## How do I get more than one page? [#how-do-i-get-more-than-one-page] Send `pagination.next_cursor` back as `cursor`, with the same `query` and `sort`, and stop when `pagination.has_more` is `false`. Each page is 10 credits. ```python title="Python: read up to 10 pages" import os import requests BASE = "https://api.insightsocial.app/v1" HEADERS = {"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]} params = {"query": "best project management software", "sort": "new"} comments = [] for _ in range(10): body = requests.get(f"{BASE}/reddit/search/comments", params=params, headers=HEADERS, timeout=120).json() comments += [item["comment"] for item in body["data"]["items"]] if not body["pagination"]["has_more"]: break params["cursor"] = body["pagination"]["next_cursor"] ``` See [Pagination](/docs/pagination) for the full rules. ## What are the limits? [#what-are-the-limits] * **No thread position.** `parent_id` is `null` on every row: you know which post a comment is under, not which comment it replied to. Use `GET /v1/reddit/post/comments` with the post's URL to see the whole thread. * **No reply counts.** `engagement.replies` is `null`, which means unknown, not zero. * **Relevance is loose.** Without quotes, matches share some of the words rather than the whole phrase: our test for "best project management software" also returned comments about software careers and game sales. Quote the phrase or add `subreddit:` to tighten it. * **No total.** `data.total` is `null`, so you can't know the number of matches up front. ## What can I build with it? [#what-can-i-build-with-it] * **Brand and competitor mentions.** Many product recommendations live in replies, not post titles. Search your brand and competitors' names with `sort=new` every day to catch new mentions. The [brand mention monitoring](/docs/recipes/brand-mention-monitoring) recipe shows the full loop. * **Customer research.** Search a problem phrase ("switching from Jira", "too expensive for a small team") and read what people say, grouped by `ext.subreddit`. * **Account history.** `author:username` lists what one account has said across Reddit, useful for checking a prospect or a reviewer. ## Related [#related] * [`GET /v1/reddit/search/comments` reference](/docs/api-reference/reddit/search-comments): every parameter and field. * [`GET /v1/reddit/search`](/docs/api-reference/reddit/search): search Reddit posts instead of comments. * [`GET /v1/reddit/post/comments`](/docs/api-reference/reddit/post-comments): every comment in one thread. * [Reddit endpoints](/docs/platforms/reddit): everything the API covers on Reddit. --- # Find TikTok creators similar to one you know (/docs/how-to/tiktok-similar-creators) Get about 30 TikTok accounts similar to a seed creator, with bio, verified badge and follower, video and like counts. 50 credits per call. To find TikTok creators similar to one you already know, call `GET /v1/tiktok/similar` with the seed creator's `handle`. It returns about 30 accounts, each with username, display name, avatar, bio, verified badge, and follower, following, video and like counts. It costs 50 credits per call. The list is fixed: there are no further pages, so to go wider you call it again on one of the results. 50 credits per call for about 30 accounts. Going one level wider, by calling it on each of those 30 accounts, costs 30 × 50 = 1,500 credits more. Asking for the same handle again within 6 hours is answered from the shared cache for 2 credits. Failed calls and empty results are free. ## How do I find the handle? [#how-do-i-find-the-handle] The handle is the name after the `@` in the profile URL: `https://www.tiktok.com/@gordonramsayofficial` has the handle `gordonramsayofficial`. Send it without the `@`. ## Make the request [#make-the-request] ```bash title="cURL" curl "https://api.insightsocial.app/v1/tiktok/similar?handle=gordonramsayofficial" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os import requests res = requests.get( "https://api.insightsocial.app/v1/tiktok/similar", params={"handle": "gordonramsayofficial"}, headers={"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}, timeout=120, ) for item in res.json()["data"]["items"]: a = item["author"] print(a["username"], a["followers"], a["verified"], a["url"]) ``` ```ts title="Node.js" const url = new URL("https://api.insightsocial.app/v1/tiktok/similar"); url.searchParams.set("handle", "gordonramsayofficial"); const body = await (await fetch(url, { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! } })).json(); for (const { author } of body.data.items) console.log(author.username, author.followers, author.verified, author.url); ``` ## What comes back [#what-comes-back] A real response from 2026-10-14 for `@gordonramsayofficial`, shortened: two accounts instead of 30 and long URLs cut. ```json title="Response (shortened)" { "success": true, "endpoint": "/v1/tiktok/similar", "schema_version": "2", "data": { "items": [ { "author": { "id": "14598641", "username": "tillyramsay", "display_name": "tillyramsay", "avatar_url": "https://p19-common-sign.tiktokcdn-us.com/…", "bio": "Official TikTok of Tilly Ramsay", "verified": true, "followers": 9816700, "followers_approximate": null, "following": 107, "posts_count": 84, "likes_count": 126856718, "url": "https://www.tiktok.com/@tillyramsay", "location": null, "external_url": null, "private": false, "joined_at": "2015-11-03T00:00:00.000Z", "last_post_at": null } }, { "author": { "id": "6614519312189947909", "username": "mrbeast", "display_name": "MrBeast", "bio": "Checkout My New Book!👇", "verified": true, "followers": 143936169, "following": 346, "posts_count": 471, "likes_count": 1532891512, "url": "https://www.tiktok.com/@mrbeast", "private": false, "joined_at": "2018-10-20T00:00:00.000Z" } } ], "total": 30, "truncated": null }, "pagination": { "next_cursor": null, "has_more": false, "page_size": 30 }, "unavailable": [], "credits_used": 50 } ``` | Field | What it holds | | --------------------------------------- | ---------------------------------------------------------------------- | | `data.items[].author` | One similar account in the shared [author shape](/docs/schema/author). | | `author.followers` / `author.following` | Follower and following counts. | | `author.posts_count` | How many videos the account has posted. | | `author.likes_count` | Total likes across the account's videos. | | `author.bio` / `author.verified` | Profile bio and verified badge. | | `author.joined_at` | The day the account was created. | | `data.total` | How many accounts this call returned: 30 here. | | `pagination.has_more` | Always `false`: the list is one fixed set. | ## How do I go wider than 30 accounts? [#how-do-i-go-wider-than-30-accounts] Call the endpoint again with a result's `username` as the new `handle`, and drop accounts you have already seen by `author.id`. Each call is 50 credits, so set a budget first. ```python title="Python: two levels, deduplicated" import os import requests BASE = "https://api.insightsocial.app/v1" HEADERS = {"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]} def similar(handle): body = requests.get(f"{BASE}/tiktok/similar", params={"handle": handle}, headers=HEADERS, timeout=120).json() return [item["author"] for item in body["data"]["items"]] seen = {} first = similar("gordonramsayofficial") for a in first: seen[a["id"]] = a for a in first[:5]: # 5 more calls = 250 credits for b in similar(a["username"]): seen.setdefault(b["id"], b) print(len(seen), "unique accounts") ``` ## What are the limits? [#what-are-the-limits] * **About 30 accounts, one fixed list.** There is no cursor; `has_more` is `false`. Repeat the call on a result to go wider. * **Similarity is TikTok's, not topical.** For `@gordonramsayofficial` the list held Tilly Ramsay and kitchen clip accounts, but also MrBeast, Megan Thee Stallion and the Denver Broncos. Filter by bio or follower count in your own code. * **Mixed sizes.** The same list ran from under 3,000 followers to over 143 million. * **Some fields come back empty.** `location`, `external_url` and `last_post_at` were `null` on every account in the sample. `null` means unknown, not empty. ## What can I build with it? [#what-can-i-build-with-it] * **Influencer lookalikes.** Start from a creator who performed well for you and get a shortlist of similar accounts with their reach. * **Competitor mapping.** Seed with a competitor's brand account to see which accounts TikTok groups with it. * **Creator database seeding.** Walk two levels out from a few seeds and store every account with its counts. ## Related [#related] * [`GET /v1/tiktok/similar` reference](/docs/api-reference/tiktok/similar): every parameter and field. * [`GET /v1/tiktok/search/users`](/docs/api-reference/tiktok/search-users): find TikTok accounts by keyword instead of by seed. * [`GET /v1/instagram/similar`](/docs/api-reference/instagram/similar): the same question on Instagram. * [TikTok endpoints](/docs/platforms/tiktok): everything the API covers on TikTok. --- # Get the videos a TikTok user has liked (/docs/how-to/tiktok-videos-a-user-liked) List the videos a TikTok account has liked, with each video's author, views, likes and caption, by handle. 10 credits per page of 30 videos. To list the videos a TikTok account has liked, call `GET /v1/tiktok/user/liked` with the account's `handle` (or its numeric `user_id`). Each page returns 30 videos, newest first, each with its own author, view, like, comment, share and save counts, caption and a playable video URL. It costs 10 credits a page. Most accounts hide this list; a hidden list comes back as a not-found and costs nothing. TikTok's official API only offers liked-videos data through its Research API, which is limited to approved academic researchers. 10 credits per page of 30 videos. Walking 10 pages (300 videos) costs 100 credits. Asking for the same page again within 6 hours is answered from the shared cache for 2 credits. Failed calls, empty results and hidden lists are free. ## Why do most accounts return not found? [#why-do-most-accounts-return-not-found] Most TikTok accounts hide the videos they have liked. When the list is hidden, the endpoint answers with a 404 [not found](/docs/errors) and the call is refunded, so it costs 0 credits. Retrying the same account returns the same answer, so record it and move on. A 404 does not tell you whether the list is hidden or the account does not exist. If you need to know which, look the account up with `GET /v1/tiktok/profile` (10 credits). ## How do I find the handle? [#how-do-i-find-the-handle] The handle is the name after the `@` in the profile URL: `https://www.tiktok.com/@chipotle` has the handle `chipotle`. Send it without the `@`. If you already have the account's numeric ID, send it as `user_id` instead, which gives faster responses. `GET /v1/tiktok/profile` returns the ID for a handle. ## Make the request [#make-the-request] ```bash title="cURL" curl "https://api.insightsocial.app/v1/tiktok/user/liked?handle=chipotle" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os import requests res = requests.get( "https://api.insightsocial.app/v1/tiktok/user/liked", params={"handle": "chipotle"}, headers={"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}, timeout=120, ) if res.status_code == 404: print("Liked list is hidden or the account does not exist (not charged)") else: for item in res.json()["data"]["items"]: post = item["post"] print(post["author"]["username"], post["engagement"]["views"], post["url"]) ``` ```ts title="Node.js" const url = new URL("https://api.insightsocial.app/v1/tiktok/user/liked"); url.searchParams.set("handle", "chipotle"); const res = await fetch(url, { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! } }); if (res.status === 404) { console.log("Liked list is hidden or the account does not exist (not charged)"); } else { const body = await res.json(); for (const { post } of body.data.items) console.log(post.author.username, post.engagement.views, post.url); } ``` ## What comes back [#what-comes-back] A real response from 2026-10-14 for `@chipotle`, shortened: one video instead of 30, long URLs cut, and the video's creator replaced with a placeholder. ```json title="Response (shortened)" { "success": true, "endpoint": "/v1/tiktok/user/liked", "schema_version": "2", "data": { "items": [ { "post": { "id": "7694937734091263262", "url": "https://www.tiktok.com/@example_creator/video/7694937734091263262", "kind": "short", "content": { "text": "whew I truly love you… #chipotle #margarita #newyork", "media_urls": ["https://v45.tiktokcdn-eu.com/…"], "thumbnail_url": "https://p19-common-sign.tiktokcdn-eu.com/…", "duration_seconds": 13.281 }, "author": { "id": "…", "username": "example_creator", "display_name": "Example Creator", "verified": false }, "engagement": { "views": 7822, "likes": 260, "comments": 9, "shares": 58, "saves": 8 }, "published_at": "2026-10-10T07:29:21.000Z", "ext": { "music_id": "7602834704501508112", "download_count": 0, "region": "US", "on_screen_texts": ["trying the chipotle margarita 😍"] } } } ], "total": null, "truncated": null }, "pagination": { "next_cursor": "v2c.Frx-WnqEJaeYmKVE…", "has_more": true, "page_size": 30 }, "unavailable": [], "credits_used": 10 } ``` | Field | What it holds | | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | `data.items[].post` | One liked video in the shared [post shape](/docs/schema/post): caption, video and thumbnail URLs, duration, views, likes, comments, shares, saves. | | `data.items[].post.author` | The account that posted the video, not the account whose likes you asked for. | | `data.items[].post.published_at` | When the video was posted, not when it was liked. | | `data.items[].post.ext.on_screen_texts` | Text the creator typed onto the video with TikTok's text tool. Present on 16 of the 30 videos in this sample. | | `data.items[].post.ext.music_id` | The sound the video uses. | | `data.total` | `null`: TikTok does not say how many videos the account has liked. | ## How do I get more than 30 videos? [#how-do-i-get-more-than-30-videos] Send `pagination.next_cursor` back as `cursor`, with the same `handle`, and stop when `pagination.has_more` is `false`. Each page is 10 credits. ```python title="Python: walk the first 5 pages" import os import requests BASE = "https://api.insightsocial.app/v1" HEADERS = {"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]} params = {"handle": "chipotle"} liked = [] for _ in range(5): body = requests.get(f"{BASE}/tiktok/user/liked", params=params, headers=HEADERS, timeout=120).json() liked += body["data"]["items"] if not body["pagination"]["has_more"]: break params["cursor"] = body["pagination"]["next_cursor"] ``` See [Pagination](/docs/pagination) for the full rules. ## What are the limits? [#what-are-the-limits] * **Hidden lists.** Most accounts hide their liked videos. A hidden list is a 404 not-found and is refunded. * **One direction only.** This is what one account liked. TikTok has no list of who liked a given video, so the API cannot return one. * **Order.** The list comes newest first, but `published_at` is the video's post date, so publish dates on a page are not in order: one page mixed videos posted between October 4 and October 12. * **No total.** `data.total` is `null`; you find the end of the list when `has_more` is `false`. * **Other people's videos.** Every item is a video by another creator, with that creator's own counts. ## What can I build with it? [#what-can-i-build-with-it] * **Brand affinity.** A brand's liked list shows which creator videos it engages with. All 30 videos on the first page of Chipotle's list mention Chipotle in the caption or on-screen text. * **Creator research.** What a creator likes shows the niches and peers they follow, which helps when you pitch them. * **UGC sourcing.** Videos a brand already liked are a short list of user content to ask permission to reuse. ## Related [#related] * [`GET /v1/tiktok/user/liked` reference](/docs/api-reference/tiktok/user-liked): every parameter and field. * [`GET /v1/tiktok/user/following`](/docs/api-reference/tiktok/user-following): the accounts a TikTok user follows. * [`GET /v1/tiktok/profile`](/docs/api-reference/tiktok/profile): check an account and get its user ID. * [TikTok endpoints](/docs/platforms/tiktok): everything the API covers on TikTok. --- # Get TikTok videos tagged at a location (/docs/how-to/tiktok-videos-by-location) List public TikTok videos tagged at a place, with views, likes, captions and authors, by TikTok place ID. 10 credits per page of 20 videos. To list the TikTok videos tagged at a place, call `GET /v1/tiktok/location/posts` with TikTok's `location_id` for that place. Each page returns 20 public videos with their view, like, comment, share and save counts, caption, author and a playable video URL. It costs 10 credits a page. 10 credits per page of 20 videos. Walking 10 pages (200 videos) costs 100 credits. Asking for the same page again within 6 hours is answered from the shared cache for 2 credits. Failed calls and empty results are free, and so is an unknown place ID. ## How do I find the location ID? [#how-do-i-find-the-location-id] The `location_id` is TikTok's own place ID, the long number in the URL of that place's page on TikTok. Open a video that has a location tag, tap the place name, and copy the number from the URL: a place page whose URL ends in `22535865202815278` has that ID. The API has no TikTok place search, so you cannot look up an ID by name; take it from the place page. `GET /v1/instagram/search/location` returns Instagram's location IDs, which are a different system. The optional `region` parameter takes a two-letter country code (`US`, `GB`, …) and fetches the videos as seen from that country. It defaults to `US`. ## Make the request [#make-the-request] ```bash title="cURL" curl "https://api.insightsocial.app/v1/tiktok/location/posts?location_id=22535865202815278" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os import requests res = requests.get( "https://api.insightsocial.app/v1/tiktok/location/posts", params={"location_id": "22535865202815278"}, headers={"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}, timeout=120, ) for item in res.json()["data"]["items"]: post = item["post"] print(post["published_at"], post["engagement"]["views"], post["url"]) ``` ```ts title="Node.js" const url = new URL("https://api.insightsocial.app/v1/tiktok/location/posts"); url.searchParams.set("location_id", "22535865202815278"); const body = await (await fetch(url, { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! } })).json(); for (const { post } of body.data.items) console.log(post.published_at, post.engagement.views, post.url); ``` ## What comes back [#what-comes-back] A real response from 2026-10-14 for the place ID above; its videos are from Seoul. Shortened: one video instead of 20, long URLs cut, and the poster's account replaced with a placeholder. ```json title="Response (shortened)" { "success": true, "endpoint": "/v1/tiktok/location/posts", "schema_version": "2", "data": { "items": [ { "post": { "id": "7695743564919213364", "url": "https://www.tiktok.com/@example_creator/video/7695743564919213364", "kind": "short", "content": { "text": "✨ Myeongdong Street 명동 거리 — Seoul’s Vibrant Heart 🇰🇷 …", "media_urls": ["https://v16m.tiktokcdn-us.com/…"], "thumbnail_url": "https://p19-common-sign.tiktokcdn-us.com/…", "duration_seconds": 21.034 }, "author": { "id": "…", "username": "example_creator", "display_name": "Example Creator", "verified": false }, "engagement": { "views": 4951, "likes": 324, "comments": 6, "shares": 12, "saves": 50 }, "published_at": "2026-10-12T11:35:36.000Z", "ext": { "music_id": "7695743622973131528", "download_count": 7, "region": "KR" } } } ], "total": null, "truncated": null }, "pagination": { "next_cursor": "v2c.sscG5h7wUiZWtLX_…", "has_more": true, "page_size": 20 }, "unavailable": [], "credits_used": 10 } ``` | Field | What it holds | | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | `data.items[].post` | One video in the shared [post shape](/docs/schema/post): caption, video and thumbnail URLs, duration, views, likes, comments, shares, saves, publish time. | | `data.items[].post.author` | The account that posted the video. | | `data.items[].post.ext.region` | The region TikTok assigns to the video, not the place. One Seoul page carried KR, US, IT, DE, KE, VN and TH. | | `data.items[].post.ext.on_screen_texts` | Text typed onto the video with TikTok's text tool, when there is any. | | `data.total` | `null`: TikTok does not say how many videos are tagged at the place. | ## How do I get more than 20 videos? [#how-do-i-get-more-than-20-videos] Send `pagination.next_cursor` back as `cursor`, with the same `location_id` and `region`, and stop when `pagination.has_more` is `false`. Each page is 10 credits. ```python title="Python: walk the first 5 pages" import os import requests BASE = "https://api.insightsocial.app/v1" HEADERS = {"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]} params = {"location_id": "22535865202815278"} videos = [] for _ in range(5): body = requests.get(f"{BASE}/tiktok/location/posts", params=params, headers=HEADERS, timeout=120).json() videos += body["data"]["items"] if not body["pagination"]["has_more"]: break params["cursor"] = body["pagination"]["next_cursor"] ``` See [Pagination](/docs/pagination) for the full rules. ## What are the limits? [#what-are-the-limits] * **Place ID only.** There is no TikTok place search in the API. Copy the ID from the place page URL. * **Unknown place versus quiet place.** An ID TikTok does not know is a 404 not-found and is refunded. A real place with nothing recent returns an empty list, which is also free. * **Order.** Videos are not sorted by date: one page mixed videos posted between January 30 and October 14, 2026. Sort in your own code if you need to. * **Public videos only.** Videos from private accounts never appear. * **No total.** `data.total` is `null`; you find the end when `has_more` is `false`. ## What can I build with it? [#what-can-i-build-with-it] * **Venue and destination monitoring.** Track views and new videos at a hotel, restaurant, venue or city over time. * **Local creator discovery.** The authors of the most-viewed videos at a place are creators already covering it. * **Event coverage.** Pull the videos tagged at a stadium or festival ground in the days after an event. ## Related [#related] * [`GET /v1/tiktok/location/posts` reference](/docs/api-reference/tiktok/location-posts): every parameter and field. * [`GET /v1/instagram/location/posts`](/docs/api-reference/instagram/location-posts): the same question on Instagram, with [`GET /v1/instagram/search/location`](/docs/api-reference/instagram/search-location) to find the ID by name. * [TikTok endpoints](/docs/platforms/tiktok): everything the API covers on TikTok. --- # Search Xiaohongshu (RedNote) notes by keyword (/docs/how-to/xiaohongshu-rednote-search) Find Xiaohongshu (RedNote) notes by keyword, with title, summary, creator, likes, comments, saves and shares. 50 credits per note returned. To search Xiaohongshu (RedNote) notes by keyword, call `GET /v1/xiaohongshu/search` with a `query` in Chinese or English. Each call returns up to 20 notes, 10 by default, with the note's title, a short summary, its creator, its like, comment, save and share counts, a cover image and the publish time. It is metered at 50 to 1,000 credits per call: the range scales with `limit`, which works out to 50 credits per note, so the default 10 notes cost 500. 50 to 1,000 credits per call: 50 at `limit=1`, 1,000 at `limit=20`. The sample below used the default `limit` of 10 and was charged 500. When the call starts, the most it can cost with your `limit` is held; you are charged what it used and the rest is released. Add `dry_run=1` to get a free quote first. Each further page is charged the same way. Asking the same search again within 1 hour is answered from the shared cache for 2 credits. Failed calls and empty results are free. ## Which parameters can I set? [#which-parameters-can-i-set] | Parameter | Values | | ----------- | ------------------------------------------------------------------------------------ | | `query` | Required. The search keywords, for example `护肤` (skincare) or `camping`. | | `limit` | Notes per call, 1 to 20. Default 10. | | `sort` | `relevance` (default), `latest`, `most_liked`, `most_commented` or `most_collected`. | | `type` | `all` (default), `image` or `video`. | | `published` | Recency window: `all` (default), `day`, `week` or `half_year`. | | `cursor` | The `pagination.next_cursor` from the previous page. Omit it for the first page. | ## Make the request [#make-the-request] ```bash title="cURL" curl -G "https://api.insightsocial.app/v1/xiaohongshu/search" \ --data-urlencode "query=护肤" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os import requests res = requests.get( "https://api.insightsocial.app/v1/xiaohongshu/search", params={"query": "护肤", "limit": 10}, headers={"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}, timeout=120, ) for item in res.json()["data"]["items"]: post = item["post"] print(post["ext"]["title"], post["engagement"]["likes"], post["engagement"]["saves"], post["url"]) ``` ```ts title="Node.js" const url = new URL("https://api.insightsocial.app/v1/xiaohongshu/search"); url.searchParams.set("query", "护肤"); url.searchParams.set("limit", "10"); const body = await (await fetch(url, { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! } })).json(); for (const { post } of body.data.items) console.log(post.ext.title, post.engagement.likes, post.engagement.saves, post.url); ``` ## What comes back [#what-comes-back] A real response from 2026-10-14 for `query=护肤`, shortened: two notes instead of 10, summaries and long URLs cut, and the creators replaced with placeholders. ```json title="Response (shortened)" { "success": true, "endpoint": "/v1/xiaohongshu/search", "schema_version": "2", "data": { "items": [ { "post": { "id": "6a5f4af70000000011007275", "url": "https://www.xiaohongshu.com/explore/6a5f4af70000000011007275", "kind": "image", "content": { "text": "P1就是随手拍的照片…", "media_urls": null, "thumbnail_url": "https://sns-img-qc.xhscdn.com/notes_pre_post/…", "duration_seconds": null }, "author": { "id": "example_user_id", "username": "example_user", "display_name": "Example User", "verified": null }, "engagement": { "views": null, "likes": 5217, "comments": 63, "shares": 198, "saves": 2657 }, "published_at": "2026-07-21T10:33:27.000Z", "ext": { "title": "戒糖+内调1年,素颜已达next level", "media_type": "image", "text_truncated": true } } }, { "post": { "id": "6ab9051e000000000202b634", "url": "https://www.xiaohongshu.com/explore/6ab9051e000000000202b634", "kind": "video", "content": { "text": "一期无广的晚间护肤分享…", "media_urls": ["https://sns-video-bd.xhscdn.com/stream/…mp4"], "thumbnail_url": "https://sns-img-qc.xhscdn.com/…", "duration_seconds": 242.666 }, "author": { "id": "example_user_id_2", "username": "example_user_2", "display_name": "Example User 2", "verified": null }, "engagement": { "views": null, "likes": 841, "comments": 27, "shares": 34, "saves": 145 }, "published_at": "2026-09-27T11:59:26.000Z", "ext": { "title": "我的晚间护肤流程", "media_type": "video", "text_truncated": true } } } ], "total": null }, "pagination": { "next_cursor": "v2c.aucAu1l_…", "has_more": true, "page_size": 10 }, "unavailable": [], "credits_used": 500 } ``` | Field | What it holds | | ------------------------- | ---------------------------------------------------------------------------- | | `data.items[].post` | One note in the shared [post shape](/docs/schema/post). | | `post.ext.title` | The note's title. | | `post.content.text` | A short summary of the note body. `ext.text_truncated: true` says it is cut. | | `post.kind` | `image` for picture notes, `video` for video notes. | | `post.content.media_urls` | The video file for video notes; `null` for image notes in search results. | | `post.engagement` | Likes, comments, shares and saves (Xiaohongshu calls saves "collects"). | | `post.author` | The creator: ID, username, display name, avatar. | | `pagination.next_cursor` | Pass it back as `cursor` for the next page. | ## How do I get more notes? [#how-do-i-get-more-notes] Send `pagination.next_cursor` back as `cursor`, with the same `query` and filters, and stop when `pagination.has_more` is `false`. Each page is charged like the first: 50 credits per note, so 500 for a page of 10. ```python title="Python: walk the first 3 pages" import os import requests BASE = "https://api.insightsocial.app/v1" HEADERS = {"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]} params = {"query": "护肤", "limit": 10, "sort": "most_liked"} notes = [] for _ in range(3): body = requests.get(f"{BASE}/xiaohongshu/search", params=params, headers=HEADERS, timeout=120).json() notes += body["data"]["items"] if not body["pagination"]["has_more"]: break params["cursor"] = body["pagination"]["next_cursor"] ``` Three pages of 10 notes cost 1,500 credits, so set a page limit before you start. See [Pagination](/docs/pagination) for the full rules. ## What are the limits? [#what-are-the-limits] * **Summaries, not full notes.** Search returns a short summary of each note. Pass the note's ID to `GET /v1/xiaohongshu/post` (50 credits) for the full body, images or video. * **No view counts.** `engagement.views` is `null` on every note in the sample, which means unknown, not zero. * **No total.** `data.total` is `null`: the search does not say how many notes match. * **No verified badge.** `author.verified` is `null` in search results. * **Price per note.** The cost grows with `limit`, so ask for the number of notes you need, not the maximum. ## What can I build with it? [#what-can-i-build-with-it] * **Market research in China.** Search a product category such as `护肤` with `sort=most_collected` to see which notes people save, and which brands and claims they mention. * **Creator discovery.** The authors of the most-liked notes on a keyword are creators already working in that niche. Pass `post.author.id` as `id` to `GET /v1/xiaohongshu/profile` for their follower counts. * **Brand monitoring.** Search your brand name with `sort=latest` and `published=week` on a schedule to catch new notes about it. The other Xiaohongshu endpoints work with what search returns: `post` (50 credits) for a note's full body, `post/comments` (50 to 1,000) for its comments, `profile` (50) and `profile/posts` (50 to 1,000) for a creator, and `trending` (50 to 1,000) for the live hot-search board. ## Related [#related] * [`GET /v1/xiaohongshu/search` reference](/docs/api-reference/xiaohongshu/search): every parameter and field. * [`GET /v1/xiaohongshu/post`](/docs/api-reference/xiaohongshu/post): one note in full. * [`GET /v1/xiaohongshu/trending`](/docs/api-reference/xiaohongshu/trending): what is trending on Xiaohongshu now. * [Xiaohongshu endpoints](/docs/platforms/xiaohongshu): everything the API covers on Xiaohongshu (RedNote). --- # Get a YouTube channel's business email (/docs/how-to/youtube-channel-email) Read the address behind a YouTube channel's "View email address" button, plus its country and profile, by handle. 250 credits per channel. To get the business email a YouTube channel publishes, call `GET /v1/youtube/channel/about` with the channel's `handle`, `channelId` or `url`. It returns the address behind the About tab's "View email address" control on `author.ext.public_email`, the channel's listed country, and its profile: name, handle, avatar, bio, subscribers, join date and total views. It costs 250 credits per channel. The cheaper `GET /v1/youtube/channel` (10 credits) only finds emails written in the public description, so try it first and use this one for the channels it misses. 250 credits per channel. Checking 20 shortlisted channels costs 5,000 credits. Asking for the same channel again within 24 hours is answered from the shared cache for 2 credits. Failed calls and empty results are free. ## How do I find the channel handle? [#how-do-i-find-the-channel-handle] The handle is the part after `@` in the channel URL: `https://www.youtube.com/@mkbhd` has the handle `mkbhd`. Pass it without the `@`. If you have a URL in the older `https://www.youtube.com/channel/UC…` form, pass the `UC…` part as `channelId`, or pass the whole link as `url`. ## Should I try the cheaper call first? [#should-i-try-the-cheaper-call-first] Yes. Many creators write their business address in the channel description. `GET /v1/youtube/channel` reads that description and puts any address it finds on the same field, `author.ext.public_email`, for 10 credits. Only when that field is `null` is the 250-credit call worth making: it reads the address behind the About tab control, which the description does not always repeat. ```python title="Python: description first, About tab second" import os import requests BASE = "https://api.insightsocial.app/v1" HEADERS = {"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]} def business_email(handle): for path in ("/youtube/channel", "/youtube/channel/about"): body = requests.get(BASE + path, params={"handle": handle}, headers=HEADERS, timeout=120).json() email = body["data"]["author"]["ext"].get("public_email") if email: return email return None ``` ## Make the request [#make-the-request] ```bash title="cURL" curl "https://api.insightsocial.app/v1/youtube/channel/about?handle=mkbhd" \ -H "x-api-key: $INSIGHTSOCIAL_API_KEY" ``` ```python title="Python" import os import requests res = requests.get( "https://api.insightsocial.app/v1/youtube/channel/about", params={"handle": "mkbhd"}, headers={"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}, timeout=120, ) author = res.json()["data"]["author"] print(author["display_name"], author["ext"]["public_email"], author["ext"]["country"]) ``` ```ts title="Node.js" const url = new URL("https://api.insightsocial.app/v1/youtube/channel/about"); url.searchParams.set("handle", "mkbhd"); const body = await (await fetch(url, { headers: { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY! } })).json(); const { author } = body.data; console.log(author.display_name, author.ext.public_email, author.ext.country); ``` ## What comes back [#what-comes-back] A real response from 2026-10-14, shortened: the avatar URL is cut and two of the five links are dropped. ```json title="Response (shortened)" { "success": true, "endpoint": "/v1/youtube/channel/about", "schema_version": "2", "data": { "author": { "id": "UCBJycsmduvYEL83R_U4JriQ", "username": "@mkbhd", "display_name": "Marques Brownlee", "avatar_url": "https://yt3.googleusercontent.com/qu4TmIaYUlS41…", "bio": "MKBHD: Quality Tech Videos | YouTuber | Geek | … business@MKBHD.com\n\nNYC", "verified": true, "followers": 21400000, "followers_approximate": true, "posts_count": 1858, "url": "https://www.youtube.com/channel/UCBJycsmduvYEL83R_U4JriQ", "joined_at": "2008-03-21T00:00:00.000Z", "ext": { "country": "United States", "public_email": "business@mkbhd.com", "links": ["https://twitter.com/MKBHD", "https://instagram.com/MKBHD", "https://discord.gg/MKBHD"], "keywords": "MKBHD, MarquesBrownlee, Marques, Brownlee", "total_views": 5767983054 } } }, "unavailable": [], "credits_used": 250 } ``` | Field | What it holds | | ------------------------------ | --------------------------------------------------------------------------------------------------------- | | `data.author.ext.public_email` | The address behind the About tab's "View email address" control. `null` when the channel has not set one. | | `data.author.ext.country` | The country the channel lists on its About tab. | | `data.author.ext.links` | The links shown on the About tab. | | `data.author.followers` | Subscriber count. `followers_approximate: true` means YouTube rounds it. | | `data.author.posts_count` | Number of videos on the channel. | | `data.author.ext.total_views` | Lifetime views across the channel. | | `data.author.joined_at` | When the channel was created. | The profile follows the shared [author shape](/docs/schema/author). ## What are the limits? [#what-are-the-limits] * **Not every channel sets one.** The address exists only when the creator added it on YouTube. Without one, `public_email` is `null`. * **One channel per call.** The catalogue marks this endpoint premium and advises shortlisting channels before you call it. * **Rounded subscribers.** YouTube shows rounded counts above 1,000, so `followers` is approximate. * **Personal address.** Add `contact_email=1` (no extra credits) to also get `author.ext.contact_email`: when the bio lists two or more addresses, the one the creator gives for personal contact, copied from the bio. It is `null` when there is one address or none, when every address belongs to a manager, agency or brand, or when the choice was not confident. ## What can I build with it? [#what-can-i-build-with-it] * **Sponsorship outreach.** Pull a niche's channels, keep those within your subscriber range, and collect the business address each one publishes for brand inquiries. * **Agency prospecting.** Group a shortlist by `ext.country` before you reach out, so each contact gets the right market and language. * **CRM enrichment.** Add the published business contact, subscriber count and total views to channel records you already keep. ## Related [#related] * [`GET /v1/youtube/channel/about` reference](/docs/api-reference/youtube/channel-about): every parameter and field. * [`GET /v1/youtube/channel`](/docs/api-reference/youtube/channel): the 10-credit profile, which reads emails from the description. * [YouTube endpoints](/docs/platforms/youtube): everything the API covers on YouTube. --- # Ad library aggregation (/docs/recipes/ads-library-aggregation) Pull the ads a brand is running on Meta, LinkedIn and TikTok in one parallel fan-out and flatten them into one table. 150 to 270 credits per brand. You will build a competitive-intelligence view of every ad a company is running on Meta (Facebook and Instagram), LinkedIn and TikTok, flattened into one table. Useful for sales-call prep, market research and creative benchmarking. 150 to 270 credits per brand: Meta ad search (50), LinkedIn ad search (50) and TikTok ad search (metered, 50–170). Auditing a 25-company competitive set costs 3,750 to 6,750 credits. Repeating the same search is charged again, so store the results. ## How do you list every ad one advertiser has live? [#how-do-you-list-every-ad-one-advertiser-has-live] Query each network's public ad library in parallel with the brand name. All three have a search endpoint that takes a name, and the three calls finish together. Every major ad network publishes a transparency library, but each has its own interface, its own query model and its own response shape, and none of the official interfaces export data. ## What you need [#what-you-need] | Endpoint | Network | Parameters | Credits | | --------------------------------------- | ------------------- | --------------------------------------------------------------------------------- | ------- | | `GET /v1/facebook/adlibrary/search/ads` | Meta Ad Library | `query`, optional `country`, `status`, `media_type`, `start_date`, `end_date` | 50 | | `GET /v1/linkedin/ads/search` | LinkedIn Ad Library | `company`, `keyword` or `companyId`, optional `countries`, `startDate`, `endDate` | 50 | | `GET /v1/tiktok/adlibrary/search` | TikTok Ad Library | `query` or `advertiser_name` | 50–170 | Meta takes `query`. LinkedIn takes `company` (or `keyword`, or `companyId`) and spells its dates `startDate` and `endDate`. TikTok takes `query` or `advertiser_name`. A parameter an endpoint does not know is either ignored or rejected with a `400`, which is not charged. Check each endpoint's parameters in `GET /v1/endpoints` before you generalise the call. ## The code [#the-code] TikTok's ad search returns ads as posts in the standard shape (`data.items[].post`). The Meta and LinkedIn ad searches are beta endpoints: `data.items` is a list of each network's own ad records, with our conventions applied (snake\_case keys, string ids). The script reads the few fields an audit needs from each and flattens them into one row type: `{network, id, advertiser, text, url}`. ```python title="Python" # ad_audit.py # Pulls the ads a brand is running on three networks in parallel. # Run with: INSIGHTSOCIAL_API_KEY=isk_live_... python ad_audit.py import os from collections import Counter from concurrent.futures import ThreadPoolExecutor import requests KEY = os.environ["INSIGHTSOCIAL_API_KEY"] BASE = "https://api.insightsocial.app/v1" BRAND = "Duolingo" def get(path, **params): res = requests.get(f"{BASE}/{path}", params=params, headers={"x-api-key": KEY}, timeout=120) return res.json() LEGS = { "meta": ("facebook/adlibrary/search/ads", {"query": BRAND}), "linkedin": ("linkedin/ads/search", {"company": BRAND}), "tiktok": ("tiktok/adlibrary/search", {"advertiser_name": BRAND}), } with ThreadPoolExecutor(max_workers=3) as pool: futures = {net: pool.submit(get, path, **params) for net, (path, params) in LEGS.items()} results = {net: f.result() for net, f in futures.items()} def flatten(network, item): if network == "meta": # beta: Meta Ad Library records snap = item.get("snapshot") or {} return {"id": item.get("ad_archive_id"), "advertiser": item.get("page_name"), "text": (snap.get("body") or {}).get("text"), "url": item.get("url")} if network == "linkedin": # beta: LinkedIn Ad Library records return {"id": item.get("id"), "advertiser": item.get("advertiser"), "text": item.get("description"), "url": item.get("url")} post = item["post"] # TikTok: the standard post shape return {"id": post["id"], "advertiser": post["author"]["display_name"], "text": post["content"]["text"], "url": post["url"]} ads, spent = [], 0 for network, body in results.items(): spent += body.get("credits_used", 0) if not body["success"]: print(f"{network}: {body['error']['type']} (not charged)") continue for item in body["data"].get("items", []): ads.append({"network": network, **flatten(network, item)}) print(f'{len(ads)} ads found for "{BRAND}"') print(dict(Counter(row["network"] for row in ads))) for row in ads[:5]: print(f'[{row["network"]}] {row["advertiser"]}: {(row["text"] or "")[:80]}') print(f"credits used: {spent}") ``` ```ts title="Node.js" // ad-audit.ts const KEY = process.env.INSIGHTSOCIAL_API_KEY; if (!KEY) throw new Error("Set INSIGHTSOCIAL_API_KEY"); const BASE = "https://api.insightsocial.app/v1"; const brand = "Duolingo"; async function get(path: string, params: Record) { const url = new URL(`${BASE}/${path}`); for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v); return (await fetch(url, { headers: { "x-api-key": KEY! } })).json(); } const legs: [string, string, Record][] = [ ["meta", "facebook/adlibrary/search/ads", { query: brand }], ["linkedin", "linkedin/ads/search", { company: brand }], ["tiktok", "tiktok/adlibrary/search", { advertiser_name: brand }], ]; const bodies = await Promise.all(legs.map(([, path, params]) => get(path, params))); // Meta and LinkedIn are beta endpoints (each network's own records); TikTok returns posts. function flatten(network: string, item: any) { if (network === "meta") return { id: item.ad_archive_id, advertiser: item.page_name, text: item.snapshot?.body?.text ?? null, url: item.url }; if (network === "linkedin") return { id: item.id, advertiser: item.advertiser, text: item.description ?? null, url: item.url }; const { post } = item; return { id: post.id, advertiser: post.author.display_name, text: post.content.text, url: post.url }; } const ads = bodies.flatMap((body, i) => body.success ? (body.data.items ?? []).map((item: any) => ({ network: legs[i][0], ...flatten(legs[i][0], item) })) : [], ); console.log(`${ads.length} ads found for "${brand}"`); console.table( ads.reduce>((acc, r) => ({ ...acc, [r.network]: (acc[r.network] ?? 0) + 1 }), {}), ); ``` ## What you get back [#what-you-get-back] ```text title="Example output" 66 ads found for "Duolingo" {'meta': 30, 'linkedin': 24, 'tiktok': 12} [meta] Duolingo: Learn a language for free with fun lessons designed to keep you motivated! [meta] Duolingo: ... ... credits used: 150 ``` The figures are illustrative. The flattened row keeps only what the three networks share. The full records carry more: Meta ads the creative, images, running status (`is_active`) and the platforms they run on; LinkedIn ads the targeting, run dates and impression range; TikTok ads the creative, advertiser and first-shown date. On LinkedIn, `advertiser` is who paid for the ad; `poster` can be a person, when the brand promotes an employee's or a creator's post. Beta endpoints keep each network's structure and may change shape, so read their fields defensively, as the code does with `.get()`. ## What to change [#what-to-change] * **Widen the Meta leg.** `GET /v1/facebook/adlibrary/search/companies` (50) resolves a brand name to its advertiser pages and their `pageId`. `GET /v1/facebook/adlibrary/company/ads` (50–350) then returns one page's full run, which is more precise than a keyword search. `GET /v1/facebook/adlibrary/ad/transcript` (100) returns what a video ad says out loud. * **Read one ad in full.** `GET /v1/facebook/adlibrary/ad`, `GET /v1/linkedin/ad` and `GET /v1/tiktok/adlibrary/ad` are 50 credits each. * **Richer TikTok rows.** `include=ad` on the TikTok search adds the brand name, landing page and advertiser profile link to every row, under `post.ext.ad`. * **What actually performs.** `GET /v1/tiktok/ads/top` (100–1000) returns TikTok's leaderboard of top ads for a market and time window, with click-through rate and rank. Filter it with `keyword` to see a category's best creative. * **Scope by market and date.** Add `country` and `start_date` / `end_date` on Meta, and `countries` and `startDate` / `endDate` on LinkedIn. * **Page deeper.** All three return `pagination.next_cursor`. Send it back as `cursor`, with the same parameters, to get the next page. See [Pagination](/docs/pagination). ## Related [#related] The organic side of the same competitors. Every ad-library endpoint, per network, with prices. The full Facebook surface, including the ad library. TikTok's ad library and top-ads board. --- # Brand mention monitoring (/docs/recipes/brand-mention-monitoring) Run a daily brand-mention sweep across the search endpoints of six platforms in parallel, drop off-topic rows with a keyword check, dedupe against earlier runs and alert on what is new. From 60 credits per sweep. This recipe is the daily job at the heart of social listening. Once a day it searches six platforms for your brand name, drops rows that turn out to be about something else, skips anything it has seen before, and sends what remains to Slack, email or a dashboard. At least 60 credits per sweep: six search calls whose price starts at 10 each. All six are metered, so each call holds its ceiling when it starts (650 on Instagram, 370 on TikTok, 340 on Threads, 300 on Reddit, 150 on YouTube, 50 on X) and is charged what it used. Keep at least 1,860 credits available while a sweep runs. A daily sweep costs at least 1,800 credits a month. ## How do you track brand mentions with an API? [#how-do-you-track-brand-mentions-with-an-api] Call each platform's keyword search with your brand name, in parallel, limited to the last day. Then check each row's text yourself: a brand name that is also an ordinary word ("notion", "apple") matches a lot of posts that are not about you. You also supply the dedupe layer, because "new since the last run" is your state. ## What you need [#what-you-need] | Endpoint | Platform | Date window | Credits | | -------------------------------- | --------- | ----------------------------------------------- | ------- | | `GET /v1/reddit/search` | Reddit | `max_age_days` | 10–300 | | `GET /v1/tiktok/search` | TikTok | `max_age_days` | 10–370 | | `GET /v1/youtube/search` | YouTube | `max_age_days` | 10–150 | | `GET /v1/twitter/search/tweets` | X | `since:` operator in `query`, or `max_age_days` | 10–50 | | `GET /v1/threads/search` | Threads | `start_date` / `end_date` | 10–340 | | `GET /v1/instagram/search/reels` | Instagram | `max_age_days` | 10–650 | All six take `query` and return posts in the same shape: the text is `post.content.text` and the link is `post.url`, on every platform. `max_age_days` keeps only rows published inside the window. It filters after each page is fetched, so the page is billed as usual, and a day with nothing new returns an empty list. ## The code [#the-code] ```python title="Python" # brand_mentions.py # Daily brand-mention sweep with a keyword check and dedupe. # Keep the known URLs somewhere durable between runs. # Run with: INSIGHTSOCIAL_API_KEY=isk_live_... python brand_mentions.py import json import os import re from concurrent.futures import ThreadPoolExecutor from datetime import date, timedelta from pathlib import Path import requests KEY = os.environ["INSIGHTSOCIAL_API_KEY"] BASE = "https://api.insightsocial.app/v1" BRAND = "notion" # The name is an ordinary word, so a row must also mention one of these. CONTEXT = ["app", "workspace", "template", "notes", "wiki", "database", "page"] KNOWN_FILE = Path("known-mentions.json") YESTERDAY = (date.today() - timedelta(days=1)).isoformat() SEARCHES = { "reddit": ("reddit/search", {"max_age_days": 1}), "tiktok": ("tiktok/search", {"max_age_days": 1}), "youtube": ("youtube/search", {"max_age_days": 1}), "twitter": ("twitter/search/tweets", {"max_age_days": 1}), "threads": ("threads/search", {"start_date": YESTERDAY}), "instagram": ("instagram/search/reels", {"max_age_days": 1}), } BRAND_RE = re.compile(rf"\b{re.escape(BRAND)}\b", re.IGNORECASE) CONTEXT_RE = re.compile(r"\b(" + "|".join(map(re.escape, CONTEXT)) + r")\b", re.IGNORECASE) def on_topic(text): # A row with no text (a video with no caption) cannot be checked: leave it out. if not text or not BRAND_RE.search(text): return False return not CONTEXT or bool(CONTEXT_RE.search(text)) def get(path, **params): res = requests.get(f"{BASE}/{path}", params=params, headers={"x-api-key": KEY}, timeout=120) return res.json() def search(platform): path, extra = SEARCHES[platform] return platform, get(path, query=BRAND, **extra) # Step 1: six searches in parallel. with ThreadPoolExecutor(max_workers=6) as pool: results = dict(pool.map(search, SEARCHES)) # Step 2: keep on-topic rows, dedupe against earlier runs by post URL. known = set(json.loads(KNOWN_FILE.read_text())) if KNOWN_FILE.exists() else set() fresh, spent, off_topic = [], 0, 0 for platform, body in results.items(): spent += body.get("credits_used", 0) if not body["success"]: print(f"{platform}: {body['error']['type']}") continue for item in body["data"]["items"]: post = item["post"] text = post["content"]["text"] if not on_topic(text): off_topic += 1 continue url = post["url"] if url and url not in known: known.add(url) fresh.append((platform, post)) KNOWN_FILE.write_text(json.dumps(sorted(known))) # Step 3: alert on what is new. print(f"{len(fresh)} new mentions of {BRAND!r}, {off_topic} off-topic rows skipped ({spent} credits)") for platform, post in fresh: print(f"[{platform}] {post['content']['text'][:120]}") print(f" {post['url']}") # Replace print with a Slack webhook, an email or a database insert. ``` ```ts title="Node.js" // brand-mentions.ts import { readFile, writeFile } from "node:fs/promises"; const KEY = process.env.INSIGHTSOCIAL_API_KEY; if (!KEY) throw new Error("Set INSIGHTSOCIAL_API_KEY"); const BASE = "https://api.insightsocial.app/v1"; const BRAND = "notion"; const CONTEXT = ["app", "workspace", "template", "notes", "wiki", "database", "page"]; const yesterday = new Date(Date.now() - 86_400_000).toISOString().slice(0, 10); const searches: Record]> = { reddit: ["reddit/search", { max_age_days: "1" }], tiktok: ["tiktok/search", { max_age_days: "1" }], youtube: ["youtube/search", { max_age_days: "1" }], twitter: ["twitter/search/tweets", { max_age_days: "1" }], threads: ["threads/search", { start_date: yesterday }], instagram: ["instagram/search/reels", { max_age_days: "1" }], }; const escape = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); const brandRe = new RegExp(`\\b${escape(BRAND)}\\b`, "i"); const contextRe = new RegExp(`\\b(${CONTEXT.map(escape).join("|")})\\b`, "i"); const onTopic = (text: string | null) => !!text && brandRe.test(text) && (CONTEXT.length === 0 || contextRe.test(text)); async function get(path: string, params: Record) { const url = new URL(`${BASE}/${path}`); for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v); return (await fetch(url, { headers: { "x-api-key": KEY! } })).json(); } const platforms = Object.keys(searches); const bodies = await Promise.all( platforms.map((p) => get(searches[p][0], { query: BRAND, ...searches[p][1] })), ); const known = new Set( await readFile("known-mentions.json", "utf8").then(JSON.parse).catch(() => []), ); const fresh: { platform: string; url: string; text: string }[] = []; bodies.forEach((body, i) => { if (!body.success) return console.warn(`${platforms[i]}: ${body.error.type}`); for (const { post } of body.data.items) { const text: string | null = post.content.text; if (!onTopic(text)) continue; if (post.url && !known.has(post.url)) { known.add(post.url); fresh.push({ platform: platforms[i], url: post.url, text: text! }); } } }); await writeFile("known-mentions.json", JSON.stringify([...known])); console.log(`${fresh.length} new mentions of "${BRAND}"`); for (const m of fresh) console.log(`[${m.platform}] ${m.text.slice(0, 120)}\n ${m.url}`); ``` Run it once a day. The one-day window plus the URL set means each mention surfaces once. `/v1/threads/search` bounds the period with `start_date` and `end_date` (`YYYY-MM-DD`). The code sets `start_date` to yesterday's date. On X, you can also put the window in the query itself, for example `query=notion since:2026-09-22`. ## What you get back [#what-you-get-back] ```text title="Example output" 14 new mentions of 'notion', 9 off-topic rows skipped (80 credits) [reddit] Switched our team wiki from Confluence to Notion, here is what broke https://www.reddit.com/r/productivity/comments/... [youtube] My 2026 Notion setup for university, templates included https://www.youtube.com/watch?v=... [twitter] notion's new offline mode finally shipped, every page loads on the train https://x.com/.../status/... ... ``` The figures are illustrative. The keyword check runs on your side, so it costs nothing, and a post about "a notion of fairness" does not reach your alert channel. Rows skipped this way were still fetched and are part of the page you paid for. ## What to change [#what-to-change] * **`BRAND` and `CONTEXT`.** Swap in a competitor, a product or a person. `CONTEXT` matters most when the name is also an ordinary word. Set it to `[]` for a name nobody uses for anything else. * **A stricter check.** Keyword rules miss sarcasm and odd phrasing. For borderline rows, send the text to a classifier or a language model you run, with a prompt that names your brand. * **One language.** Every post carries `post.language`, the platform's own tag (for example `en`). Many platforms leave it `null`, so keep rows where it is `null` rather than dropping them. * **The window.** Match `max_age_days` to how often the job runs, so windows neither overlap nor leave gaps. * **Fewer platforms, lower price.** Drop the searches you do not need. Each one removed saves at least 10 credits and lowers the hold. Check a call's price first with `dry_run=1`, which is never charged. * **Persist the known URLs properly.** A JSON file is fine on one machine. Move it to a database table before you run on more than one worker. ## Related [#related] How people feel about the brand, scored from comments. Track the accounts themselves, not only what people say. The post shape every search returns. Every search endpoint side by side. --- # Competitor tracking (/docs/recipes/competitor-tracking) Snapshot a competitor's followers, engagement and latest posts on TikTok, Instagram, YouTube and X in one parallel sweep, then diff it against the last run. 80 to 210 credits per competitor. You will build the data side of a competitive dashboard: on a schedule, snapshot each competitor's follower counts, engagement rate and latest posts on four platforms, store the snapshot, and compare it with the previous one. Your stored history then reveals sudden follower jumps and new campaigns. 80 to 210 credits per competitor: four profiles at 10 credits each (40), plus four post lists: TikTok (10), Instagram (metered, 10–130), YouTube (metered, 10–20) and X (10). Tracking 10 competitors once a week costs at least 3,200 credits a month. ## How do you track a competitor's social accounts? [#how-do-you-track-a-competitors-social-accounts] Call each platform's profile endpoint and its latest-posts endpoint in parallel, store the result keyed by competitor and date, and diff it against the previous snapshot to spot follower growth and new posts. For most consumer brands, four platforms at two calls each, eight in total, is enough. ## What you need [#what-you-need] | Platform | Profile (10 credits) | Latest posts | | --------- | -------------------------------------- | ------------------------------------------------------------------------ | | TikTok | `GET /v1/tiktok/profile` (`handle`) | `GET /v1/tiktok/profile/videos` (`handle`), 10 | | Instagram | `GET /v1/instagram/profile` (`handle`) | `GET /v1/instagram/profile/posts` (`handle`), 10–130 | | YouTube | `GET /v1/youtube/channel` (`handle`) | `GET /v1/youtube/channel/videos` (`handle`, `includeExtras=true`), 10–20 | | X | `GET /v1/twitter/profile` (`handle`) | `GET /v1/twitter/user/tweets` (`handle`), 10 | Every profile comes back as `data.author` with `followers`, and every post list as `data.items[].post` with `engagement.likes`, `engagement.comments` and the rest, in the same shape on all four platforms. That makes one engagement formula possible: the code computes the average of `(likes + comments) / followers` over the latest posts. It skips a post whose likes or comments are `null` (hidden, or not shown on that list), and a platform whose follower count is `null`, rather than counting them as zero. On YouTube, likes and comments are only filled with `includeExtras=true`. YouTube rounds subscriber counts above 1,000, and the profile says so with `followers_approximate: true`. Add `GET /v1/linkedin/company` (50 credits, a company page URL) for the follower count, and `GET /v1/linkedin/company/posts` (50 credits, `company_id`) for the latest posts. `GET /v1/linkedin/profile/full` (50 credits) returns a company and its latest 10 posts in one call, but it is a beta endpoint: its body follows LinkedIn's own structure rather than the post and profile shapes used here. ## The code [#the-code] ```python title="Python" # competitor_tracking.py # Snapshots one competitor on four platforms. Store the result and diff runs. # Run with: INSIGHTSOCIAL_API_KEY=isk_live_... python competitor_tracking.py import os from concurrent.futures import ThreadPoolExecutor from datetime import date import requests KEY = os.environ["INSIGHTSOCIAL_API_KEY"] BASE = "https://api.insightsocial.app/v1" COMPETITOR = "duolingo" # the same handle on all four platforms here def get(path, **params): res = requests.get(f"{BASE}/{path}", params=params, headers={"x-api-key": KEY}, timeout=120) return res.json() PAIRS = { "tiktok": ("tiktok/profile", "tiktok/profile/videos", {}), "instagram": ("instagram/profile", "instagram/profile/posts", {}), "youtube": ("youtube/channel", "youtube/channel/videos", {"includeExtras": "true"}), "twitter": ("twitter/profile", "twitter/user/tweets", {}), } def own_posts(items): # Leave out pinned posts (old, out of date order) and reposts of other accounts. posts = [i["post"] for i in items] return [p for p in posts if not p["flags"]["pinned"] and not (p["ext"] or {}).get("retweeted_post")] def engagement_rate(posts, followers): """Average (likes + comments) / followers over the posts that show both counts.""" if not followers: return None rates = [(p["engagement"]["likes"] + p["engagement"]["comments"]) / followers for p in posts if p["engagement"]["likes"] is not None and p["engagement"]["comments"] is not None] return round(sum(rates) / len(rates), 6) if rates else None # Step 1: eight calls in parallel (the key allows 10 in flight). with ThreadPoolExecutor(max_workers=8) as pool: futures = {} for platform, (profile_path, posts_path, extra) in PAIRS.items(): futures[(platform, "profile")] = pool.submit(get, profile_path, handle=COMPETITOR) futures[(platform, "posts")] = pool.submit(get, posts_path, handle=COMPETITOR, **extra) results = {k: f.result() for k, f in futures.items()} # Step 2: build today's snapshot. snapshot = {"date": date.today().isoformat(), "handle": COMPETITOR, "platforms": []} spent = 0 for platform in PAIRS: profile = results[(platform, "profile")] posts = results[(platform, "posts")] spent += profile.get("credits_used", 0) + posts.get("credits_used", 0) if not profile["success"]: print(f"{platform}: {profile['error']['type']}") continue author = profile["data"]["author"] items = own_posts(posts["data"]["items"]) if posts["success"] else [] latest = items[0] if items else None snapshot["platforms"].append({ "platform": platform, "followers": author["followers"], "followers_approximate": author["followers_approximate"], "engagement_rate": engagement_rate(items, author["followers"]), "posts_fetched": len(items), "latest_post_id": latest["id"] if latest else None, "latest_post_at": latest["published_at"] if latest else None, "latest_post": ((latest["content"]["text"] if latest else None) or "")[:80], }) for row in snapshot["platforms"]: print(row) print(f"credits used this run: {spent}") # Step 3: persist `snapshot` keyed by (handle, date), then compare with the last one: # follower_delta = today.followers - previous.followers # new_posts = posts published after previous.latest_post_at # engagement_jump = today.engagement_rate / trailing average # Alert when a delta crosses your threshold. ``` ```ts title="Node.js" // competitor-tracking.ts // Run with: INSIGHTSOCIAL_API_KEY=isk_live_... npx tsx competitor-tracking.ts const KEY = process.env.INSIGHTSOCIAL_API_KEY; if (!KEY) throw new Error("Set INSIGHTSOCIAL_API_KEY"); const BASE = "https://api.insightsocial.app/v1"; const competitor = "duolingo"; async function get(path: string, params: Record) { const url = new URL(`${BASE}/${path}`); for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v); const res = await fetch(url, { headers: { "x-api-key": KEY! } }); return res.json(); } const pairs: Record]> = { tiktok: ["tiktok/profile", "tiktok/profile/videos", {}], instagram: ["instagram/profile", "instagram/profile/posts", {}], youtube: ["youtube/channel", "youtube/channel/videos", { includeExtras: "true" }], twitter: ["twitter/profile", "twitter/user/tweets", {}], }; // Pinned posts sit out of date order; reposts are someone else's post. const ownPosts = (items: any[]) => items.map((i) => i.post).filter((p) => !p.flags.pinned && !p.ext?.retweeted_post); function engagementRate(posts: any[], followers: number | null): number | null { if (!followers) return null; const rates = posts .filter((p) => p.engagement.likes !== null && p.engagement.comments !== null) .map((p) => (p.engagement.likes + p.engagement.comments) / followers); return rates.length ? rates.reduce((a, b) => a + b, 0) / rates.length : null; } // Step 1: eight calls in parallel. const platforms = Object.keys(pairs); const results = await Promise.all( platforms.flatMap((p) => { const [profilePath, postsPath, extra] = pairs[p]; return [get(profilePath, { handle: competitor }), get(postsPath, { handle: competitor, ...extra })]; }), ); // Step 2: build today's snapshot. const rows = platforms.map((platform, i) => { const profile = results[i * 2]; const posts = results[i * 2 + 1]; const author = profile.success ? profile.data.author : null; const items = posts.success ? ownPosts(posts.data.items) : []; return { platform, followers: author?.followers ?? null, engagement_rate: author ? engagementRate(items, author.followers) : null, posts_fetched: items.length, latest_post_at: items[0]?.published_at ?? null, latest_post: items[0]?.content.text?.slice(0, 80) ?? null, }; }); console.table(rows); // Step 3: store rows keyed by (handle, date) and diff against the previous run. ``` ## What you get back [#what-you-get-back] ```text title="Example output" {'platform': 'tiktok', 'followers': 19300000, 'followers_approximate': None, 'engagement_rate': 0.0091, 'posts_fetched': 9, 'latest_post_id': '7...', 'latest_post_at': '2026-10-02T17:04:11.000Z', 'latest_post': '...'} {'platform': 'instagram', 'followers': 5600000, 'followers_approximate': None, 'engagement_rate': 0.0047, 'posts_fetched': 11, ...} {'platform': 'youtube', 'followers': 7640000, 'followers_approximate': True, 'engagement_rate': 0.0012, 'posts_fetched': 30, ...} {'platform': 'twitter', 'followers': 14900000, 'followers_approximate': None, 'engagement_rate': 0.0003, 'posts_fetched': 16, ...} credits used this run: 80 ``` The figures are illustrative. The rate is one formula applied to four platforms, not four platforms' own definitions, so compare it across competitors on the same platform first. A `None` rate means no post on the page showed both counts, or the profile had no follower count: keep it `None`, never `0`. ## What to change [#what-to-change] * **Handles per platform.** Real brands rarely use one handle everywhere. Keep a `{tiktok, instagram, youtube, twitter}` map per competitor. * **Your own formula.** Add `shares` where the platform shows them (TikTok, X), or divide by `views` instead of followers for reach-based engagement. A count the platform does not show is `null` on every row; the [platform pages](/docs/platforms/instagram) list which. * **Only fetch new posts.** Store the newest post id per platform and pass it back as `stop_at_id`, or pass the previous run's date as `since`. The list then stops at what you already hold. See [Choosing endpoints](/docs/choosing-endpoints#only-what-is-new). * **Which platforms.** Drop a pair to save at least 20 credits, or add LinkedIn for 100 more. * **Store results, refresh on a schedule.** Every run is charged, so a dashboard should read from your own store and refresh on a schedule, not on every page view. ## Related [#related] Rank accounts on comparable engagement rates. What people say about them, not only what they post. Their paid side on Meta, LinkedIn and TikTok. The engagement fields and when each one is null. --- # Creator engagement scoring (/docs/recipes/creator-engagement-scoring) Compare one creator's engagement on TikTok, Instagram and YouTube in a single ranked table, calculated with one formula from each platform's recent posts. 60 to 190 credits per creator. You will build a vetting table for influencer marketing: a single row per platform for one creator across TikTok, Instagram and YouTube, sorted by an engagement rate you compute the same way on each network. 60 to 190 credits per creator: three profile calls at 10 credits each, plus one page of recent posts per platform (TikTok 10, Instagram 10–130, YouTube 10–20). Vetting a shortlist of 100 creators costs at least 6,000 credits. Checking the same creator again is charged again, so keep the scores you calculated. ## How do you compare engagement rates across platforms? [#how-do-you-compare-engagement-rates-across-platforms] Fetch each platform's profile for the follower count, and one page of the creator's recent posts for the likes and comments. Then apply one formula everywhere: for each post, `(likes + comments) / followers`, and take the median over the page. The median keeps one viral post from carrying the whole score. Raw numbers do not compare on their own. A TikTok like is not an Instagram like, follower counts inflate differently, and every analytics tool defines "engagement rate" its own way. A formula you own, applied to the same fields on every platform, is what makes the ranking fair. Recent posts also avoid a trap in lifetime totals: a TikTok profile's like count is every heart the account ever received, which says little about how its videos do now. ## What you need [#what-you-need] | Endpoint | Returns | Credits | | --------------------------------- | ------------------------------------------------------------ | ------- | | `GET /v1/tiktok/profile` | `data.author.followers` | 10 | | `GET /v1/tiktok/profile/videos` | Recent videos, `post.engagement` | 10 | | `GET /v1/instagram/profile` | `data.author.followers` | 10 | | `GET /v1/instagram/profile/posts` | Recent posts, `post.engagement` | 10–130 | | `GET /v1/youtube/channel` | `data.author.followers` (rounded above 1,000) | 10 | | `GET /v1/youtube/channel/videos` | Recent videos; `includeExtras=true` fills likes and comments | 10–20 | ## The code [#the-code] ```python title="Python" # creator_scoring.py # Run with: INSIGHTSOCIAL_API_KEY=isk_live_... python creator_scoring.py import os from concurrent.futures import ThreadPoolExecutor from statistics import median import requests KEY = os.environ["INSIGHTSOCIAL_API_KEY"] BASE = "https://api.insightsocial.app/v1" # Real creators rarely share one handle, so map them per platform. CREATOR = {"tiktok": "mrbeast", "instagram": "mrbeast", "youtube": "MrBeast"} PATHS = { "tiktok": ("tiktok/profile", "tiktok/profile/videos", {}), "instagram": ("instagram/profile", "instagram/profile/posts", {}), "youtube": ("youtube/channel", "youtube/channel/videos", {"includeExtras": "true"}), } def get(path, **params): res = requests.get(f"{BASE}/{path}", params=params, headers={"x-api-key": KEY}, timeout=120) return res.json() def score(platform): profile_path, posts_path, extra = PATHS[platform] handle = CREATOR[platform] profile = get(profile_path, handle=handle) posts = get(posts_path, handle=handle, **extra) row = {"platform": platform, "followers": None, "engagement_rate": None, "posts_scored": 0, "credits": profile.get("credits_used", 0) + posts.get("credits_used", 0), "why": None} if not profile["success"] or not posts["success"]: row["why"] = (profile.get("error") or posts.get("error"))["type"] return row followers = profile["data"]["author"]["followers"] row["followers"] = followers if not followers: row["why"] = "no follower count" return row rates = [] for item in posts["data"]["items"]: post = item["post"] likes, comments = post["engagement"]["likes"], post["engagement"]["comments"] # Skip pinned posts (old, out of order) and posts with a hidden count. if post["flags"]["pinned"] or likes is None or comments is None: continue rates.append((likes + comments) / followers) row["posts_scored"] = len(rates) if rates: row["engagement_rate"] = round(median(rates), 4) else: row["why"] = "no post with both counts" return row with ThreadPoolExecutor(max_workers=3) as pool: rows = list(pool.map(score, CREATOR)) # Rank the comparable rows; keep the others visible but unranked. ranked = sorted((r for r in rows if r["engagement_rate"] is not None), key=lambda r: r["engagement_rate"], reverse=True) for r in ranked: print(f'{r["platform"]:<10} {r["followers"]:>12,} {r["engagement_rate"]:.4f} ({r["posts_scored"]} posts)') for r in rows: if r["engagement_rate"] is None: print(f'{r["platform"]:<10} cannot compare: {r["why"]}') print("credits used:", sum(r["credits"] for r in rows)) ``` ```ts title="Node.js" // creator-scoring.ts const KEY = process.env.INSIGHTSOCIAL_API_KEY; if (!KEY) throw new Error("Set INSIGHTSOCIAL_API_KEY"); const BASE = "https://api.insightsocial.app/v1"; const creator = { tiktok: "mrbeast", instagram: "mrbeast", youtube: "MrBeast" }; const paths: Record]> = { tiktok: ["tiktok/profile", "tiktok/profile/videos", {}], instagram: ["instagram/profile", "instagram/profile/posts", {}], youtube: ["youtube/channel", "youtube/channel/videos", { includeExtras: "true" }], }; async function get(path: string, params: Record) { const url = new URL(`${BASE}/${path}`); for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v); return (await fetch(url, { headers: { "x-api-key": KEY! } })).json(); } const median = (xs: number[]) => { const s = [...xs].sort((a, b) => a - b); const m = Math.floor(s.length / 2); return s.length % 2 ? s[m] : (s[m - 1] + s[m]) / 2; }; async function score(platform: keyof typeof creator) { const [profilePath, postsPath, extra] = paths[platform]; const handle = creator[platform]; const [profile, posts] = await Promise.all([ get(profilePath, { handle }), get(postsPath, { handle, ...extra }), ]); const followers: number | null = profile.success ? profile.data.author.followers : null; const rates = posts.success && followers ? posts.data.items .map((i: any) => i.post) .filter((p: any) => !p.flags.pinned && p.engagement.likes !== null && p.engagement.comments !== null) .map((p: any) => (p.engagement.likes + p.engagement.comments) / followers) : []; return { platform, followers, engagement_rate: rates.length ? median(rates) : null, posts_scored: rates.length }; } const rows = await Promise.all((Object.keys(creator) as (keyof typeof creator)[]).map(score)); rows.sort((a, b) => (b.engagement_rate ?? -1) - (a.engagement_rate ?? -1)); console.table(rows); ``` ## What you get back [#what-you-get-back] ```text title="Example output" youtube 298,000,000 0.0071 (30 posts) tiktok 112,000,000 0.0109 (9 posts) instagram 71,500,000 0.0052 (11 posts) credits used: 60 ``` The figures are illustrative. A `None` rate is a signal, not a low score: the profile had no follower count, or no post on the page showed both likes and comments. Treat such rows as "cannot compare", never as "best" or "worst". YouTube subscriber counts are rounded above 1,000 (`followers_approximate: true` on the profile), which is close enough for a ranking but not for a precise rate. ## What to change [#what-to-change] * **The formula.** Add `shares` and `saves` where a platform shows them, or divide by `views` for a reach-based rate. A count the platform never shows is `null` on every row, and a count hidden on one post is `null` on that post only, so filter on `None` exactly as the code does. * **A bigger sample.** Send `pagination.next_cursor` back as `cursor` with the same `handle` to read older posts, at the page price each. See [Pagination](/docs/pagination). * **Score a shortlist.** Loop over a list of creator maps and sort the flattened rows. Budget at least 60 credits per creator. * **Weight the rank.** Engagement rate alone favours small accounts. Blend it with `followers` for the trade-off your team actually makes. ## Related [#related] The engagement fields and when each one is null. Go deeper on one platform, down to each video. The same fields across a competitive set, over time. Find the creators to score in the first place. --- # Recipes (/docs/recipes) Nine end-to-end builds on the InsightSocial API, from brand monitoring to ad audits, each with its credit cost worked out before you run it. Each recipe solves one real task from start to finish and tells you what a run costs before you start. The snippets are Python (3.9+, `requests`), with Node.js (22+, native `fetch`) where a recipe benefits from both. They read your key from `INSIGHTSOCIAL_API_KEY`, use real public accounts, queries and URLs, and read the [schema 2](/docs/schema-2) response shape: posts, profiles, comments and transcripts look the same on every platform. Anything a recipe derives, such as an engagement rate or a sentiment score, it computes from those fields in your code. New to the API? The [Quickstart](/docs/quickstart) gets you a key and a first call in a few minutes. ## Pick a recipe [#pick-a-recipe] | Recipe | What you build | Credits per run | | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ----------------------------- | | [Brand mention monitoring](/docs/recipes/brand-mention-monitoring) | A daily sweep of six platforms' search for your brand, deduped | from 120 per sweep | | [Sentiment analysis](/docs/recipes/sentiment-analysis) | How people feel about a topic, scored from the comments with your own model | from 400 | | [Competitor tracking](/docs/recipes/competitor-tracking) | Follower counts, engagement and latest posts across four platforms, snapshotted | 160–420 per competitor | | [Creator engagement scoring](/docs/recipes/creator-engagement-scoring) | One creator's engagement on three platforms, worked out from recent posts and ranked | 120–380 per creator | | [TikTok analytics dashboard](/docs/recipes/tiktok-analytics-dashboard) | Account KPIs, a per-video table and a comment feed | 60–100 per refresh | | [Search then enrich](/docs/recipes/search-then-enrich) | Posts on a topic, then the full profile of everyone who posted them | from 240 | | [Video transcription](/docs/recipes/video-transcription-api) | One `transcribe()` function for seven platforms | 60 (YouTube) or 200 per video | | [Ad library aggregation](/docs/recipes/ads-library-aggregation) | Every ad a brand runs on Meta, LinkedIn and TikTok | 300–540 per brand | | [Music trend detection](/docs/recipes/music-trend-detection) | A cross-platform heat score for a sound on TikTok and Instagram | 40 per song | Where a price is a range, at least one endpoint in the recipe is metered: the top of its range is held when the call starts and you are charged what it actually used. Failed calls, empty results and `dry_run=1` calls are never charged. Repeating a call is charged again (2 credits when the shared cache answers it), so store what you fetch. API credits are non-refundable. See [Credits](/docs/credits). ## Which recipe should I start with? [#which-recipe-should-i-start-with] If you want to see the breadth of the API, start with [Brand mention monitoring](/docs/recipes/brand-mention-monitoring). It runs six platforms' search endpoints in parallel from one key, with one response envelope to handle. If you already know the platform, the [TikTok analytics dashboard](/docs/recipes/tiktok-analytics-dashboard) is the shortest complete example: three calls, one account. ## The helper every recipe uses [#the-helper-every-recipe-uses] Every recipe calls the API through the same small function. It sends the key in `x-api-key`, raises on a failed call, and returns the whole envelope so you can read `data`, `pagination`, `unavailable` and `credits_used`. ```python title="Python" import os import requests KEY = os.environ["INSIGHTSOCIAL_API_KEY"] BASE = "https://api.insightsocial.app/v1" def get(path, **params): res = requests.get( f"{BASE}/{path}", params=params, headers={"x-api-key": KEY}, timeout=120, ) body = res.json() if not body["success"]: raise RuntimeError(f'{body["error"]["type"]}: {body["error"]["message"]}') return body ``` ```ts title="Node.js" const KEY = process.env.INSIGHTSOCIAL_API_KEY; if (!KEY) throw new Error("Set INSIGHTSOCIAL_API_KEY"); const BASE = "https://api.insightsocial.app/v1"; async function get(path: string, params: Record = {}) { const url = new URL(`${BASE}/${path}`); for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v); const res = await fetch(url, { headers: { "x-api-key": KEY! } }); const body = await res.json(); if (!body.success) throw new Error(`${body.error.type}: ${body.error.message}`); return body; } ``` A count the platform hides is `null`, never `0`, so the recipes skip nulls rather than adding them. `unavailable` lists the fields a response could not fill on any row, for example `["items[].post.author.id"]`. Two limits shape how the recipes run calls in parallel: 60 requests per minute and 10 calls in flight per key. The recipes keep their thread pools at 10 or fewer. See [Rate limits](/docs/rate-limits). ## Related [#related] Decision tables for the endpoints that look alike. Every endpoint and its credit price. The response shape every recipe reads. Getting past the first page of any list. --- # Music trend detection (/docs/recipes/music-trend-detection) Score how fast a sound is spreading on TikTok and Instagram Reels with a log-weighted heat score you can track day over day. 20 credits per song. You will build an early-warning detector for sounds that are taking off. A song moves on the creation side first: creators pick it up on TikTok, then it crosses to Instagram Reels, and only later does it reach the charts. Measuring both creation surfaces catches the move while it is still early. 20 credits per song: one page of TikTok videos using the sound (10) and one page of Instagram reels using the audio (10). Finding each track's ids is a one-off: 10 credits on TikTok and 50 on Instagram. Scanning 100 tracks daily costs at least 60,000 credits a month. Scan a shortlist daily and a wider list weekly to keep that down. ## How can you catch a rising sound before it charts? [#how-can-you-catch-a-rising-sound-before-it-charts] Combine two signals. `GET /v1/tiktok/song/videos` returns videos that use a TikTok sound, with their view counts. `GET /v1/instagram/audio/reels` returns reels that use the same track on Instagram. A log-weighted mix of the two, stored per day, shows which songs are accelerating. ## What you need [#what-you-need] | Endpoint | Signal | Parameter | Credits | | ------------------------------- | --------------------------------------- | ---------- | ------- | | `GET /v1/tiktok/song/videos` | Videos that use a TikTok sound | `clipId` | 10 | | `GET /v1/instagram/audio/reels` | Reels that use an Instagram audio track | `audio_id` | 10 | ### Resolving the two ids [#resolving-the-two-ids] Both platforms key sounds by id, not by name, and both ids are stable per track. Look them up once and store them. * **TikTok `clipId`:** `GET /v1/tiktok/search/music` (10 credits) searches sounds by keyword. Each sound comes back as a post: `post.id` is the `clipId`, `post.content.text` the title, and `post.ext.video_count` how many videos use it. `sort_by=most-used` puts the biggest first. `GET /v1/tiktok/song` (10) confirms an id you already have. * **Instagram `audio_id`:** the number in an `instagram.com/reels/audio//` URL, or look it up by keyword with `GET /v1/instagram/search/music` (50 credits, beta). ## The code [#the-code] ```python title="Python" # music_heat.py # A cross-platform heat score for one track. Store it per day and watch the delta. # Run with: INSIGHTSOCIAL_API_KEY=isk_live_... python music_heat.py import math import os from concurrent.futures import ThreadPoolExecutor import requests KEY = os.environ["INSIGHTSOCIAL_API_KEY"] BASE = "https://api.insightsocial.app/v1" # Resolve both ids once (see above), then keep them. TIKTOK_CLIP_ID = "7364498342501435408" # "Espresso", Sabrina Carpenter INSTAGRAM_AUDIO_ID = "1827465019384726" # replace with the track's audio id def get(path, **params): res = requests.get(f"{BASE}/{path}", params=params, headers={"x-api-key": KEY}, timeout=120) return res.json() def views_on_page(body): """Sum of views on the page, and how many rows showed a view count.""" if not body["success"]: return 0, 0 views = [i["post"]["engagement"]["views"] for i in body["data"]["items"]] known = [v for v in views if v is not None] # null means hidden, not zero return sum(known), len(known) with ThreadPoolExecutor(max_workers=2) as pool: tt = pool.submit(get, "tiktok/song/videos", clipId=TIKTOK_CLIP_ID) ig = pool.submit(get, "instagram/audio/reels", audio_id=INSTAGRAM_AUDIO_ID) tiktok, instagram = tt.result(), ig.result() tt_views, tt_count = views_on_page(tiktok) ig_views, ig_count = views_on_page(instagram) # One page per platform is a sample, not a census. Weight it, then track the trend. heat = 0.6 * math.log10(tt_views + 1) + 0.4 * math.log10(ig_views + 1) print({ "tiktok_sample_views": tt_views, "tiktok_videos_on_page": tt_count, "instagram_sample_views": ig_views, "instagram_reels_on_page": ig_count, "heat_score": round(heat, 2), "credits_used": tiktok.get("credits_used", 0) + instagram.get("credits_used", 0), }) ``` ```ts title="Node.js" // music-heat.ts const KEY = process.env.INSIGHTSOCIAL_API_KEY; if (!KEY) throw new Error("Set INSIGHTSOCIAL_API_KEY"); const BASE = "https://api.insightsocial.app/v1"; const tiktokClipId = "7364498342501435408"; // "Espresso", Sabrina Carpenter const instagramAudioId = "1827465019384726"; // replace with the track's audio id async function get(path: string, params: Record) { const url = new URL(`${BASE}/${path}`); for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v); return (await fetch(url, { headers: { "x-api-key": KEY! } })).json(); } // Views summed over the rows that show a count; null means hidden, not zero. const viewsOnPage = (body: any): number => body.success ? body.data.items.reduce((s: number, i: any) => s + (i.post.engagement.views ?? 0), 0) : 0; const [tiktok, instagram] = await Promise.all([ get("tiktok/song/videos", { clipId: tiktokClipId }), get("instagram/audio/reels", { audio_id: instagramAudioId }), ]); const ttViews = viewsOnPage(tiktok); const igViews = viewsOnPage(instagram); const heat = 0.6 * Math.log10(ttViews + 1) + 0.4 * Math.log10(igViews + 1); console.log({ tiktok_sample_views: ttViews, instagram_sample_views: igViews, heat_score: heat.toFixed(2) }); ``` ## What you get back [#what-you-get-back] ```text title="Example output" {'tiktok_sample_views': 52700000, 'tiktok_videos_on_page': 30, 'instagram_sample_views': 36400000, 'instagram_reels_on_page': 12, 'heat_score': 7.66, 'credits_used': 20} ``` The figures are illustrative. Both lists return one page, not every video that uses the sound, so the view sums are a reach sample. A row whose view count is hidden (`null`) adds nothing to the sum and is not counted in `*_on_page`. Compare a track's score with its own history, not with an absolute bar. ## What to change [#what-to-change] * **The two ids.** Swap in the track you are watching. Budget 60 credits once per new track if you still need to look up both ids. * **Add the TikTok usage count.** `GET /v1/tiktok/song` (10 credits) returns the sound as a post, with how many videos use it in `post.ext.use_count`. It is the cleanest creation-velocity number TikTok publishes, and its day-over-day change is worth adding to the score. * **See how the sound is being used.** Read the captions yourself: `post.content.text` on each row of `tiktok/song/videos`. Counting rows by `post.published_at` day gives you a date histogram of the page. * **Find candidates to score.** `GET /v1/tiktok/search/music` with `sort_by=most-used` (10) surfaces the most-used sounds for a keyword. (`GET /v1/instagram/music/trending`, Instagram's trending chart, is switched off while its source fails; see the [changelog](/docs/changelog).) * **Deepen the sample.** Send `pagination.next_cursor` back as `cursor`, with the same id, to read more pages at 10 credits each, and adjust the weights to match. * **Track the delta, not the level.** Store `heat_score` per track per day. A rising score is the signal. A high, flat score is a song that already broke. ## Related [#related] The same parallel-fetch and score pattern, on creators. Sounds, songs, effects and the rest of TikTok. Audio, music charts and reels. Reading more than one page of a list. --- # Search then enrich (/docs/recipes/search-then-enrich) Find the posts about a topic on TikTok and Instagram, collect the accounts that made them, and fetch each account's full profile into one lead list. From 120 credits per run. You will build a creator-discovery pipeline: search two platforms for posts about a topic, collect the unique accounts behind the top posts, and fetch each account's full profile (followers, bio, bio link, public contact email where one is listed) into one table. 120 to 1,120 credits: two searches (TikTok 10–370, Instagram reels 10–650) plus 10 profile lookups at 10 credits each. Looking up an account again is charged again, so keep the profiles you already fetched. `ACCOUNTS_PER_PLATFORM` is the price dial. ## How do you turn a topic into a list of creators? [#how-do-you-turn-a-topic-into-a-list-of-creators] Chain two kinds of endpoint. A post search tells you who is posting about the topic right now. A profile call tells you who they are. Search rows carry the author's handle, and the handle is exactly what the profile endpoint takes, so there is no string-massaging in between. A search result shows that someone posted about the topic. It does not give you their follower count, bio or contact details at a useful depth. The profile does. ## What you need [#what-you-need] | Endpoint | Role | Credits | | -------------------------------- | ------------------------------------------------------------------------------------------- | ------- | | `GET /v1/tiktok/search` | TikTok videos about the topic | 10–370 | | `GET /v1/instagram/search/reels` | Instagram reels about the topic | 10–650 | | `GET /v1/tiktok/profile` | Full TikTok account, including the bio link in `external_url` | 10 | | `GET /v1/instagram/profile` | Full Instagram account, including `external_url` and `author.ext.public_email` from the bio | 10 | ## The code [#the-code] ```python title="Python" # search_then_enrich.py # Topic -> top posts -> unique authors -> full profiles. # Run with: INSIGHTSOCIAL_API_KEY=isk_live_... python search_then_enrich.py import os from concurrent.futures import ThreadPoolExecutor import requests KEY = os.environ["INSIGHTSOCIAL_API_KEY"] BASE = "https://api.insightsocial.app/v1" TOPIC = "home espresso setup" KEYWORDS = ["espresso"] # a post must mention one of these to count as on-topic ACCOUNTS_PER_PLATFORM = 5 SEARCH = {"tiktok": "tiktok/search", "instagram": "instagram/search/reels"} PROFILE = {"tiktok": "tiktok/profile", "instagram": "instagram/profile"} def get(path, **params): res = requests.get(f"{BASE}/{path}", params=params, headers={"x-api-key": KEY}, timeout=120) return res.json() def on_topic(post): text = (post["content"]["text"] or "").lower() return any(k in text for k in KEYWORDS) # Step 1: search both platforms. with ThreadPoolExecutor(max_workers=2) as pool: found = dict(zip(SEARCH, pool.map( lambda p: get(SEARCH[p], query=TOPIC), SEARCH))) # Step 2: unique authors of on-topic posts, in the order the search ranked them. targets = [] for platform, body in found.items(): if not body["success"]: print(f"{platform} search: {body['error']['type']}") continue handles = [] for item in body["data"]["items"]: post = item["post"] handle = post["author"]["username"] if handle and handle not in handles and on_topic(post): handles.append(handle) targets += [(platform, h) for h in handles[:ACCOUNTS_PER_PLATFORM]] # Step 3: enrich every author with a profile call. def enrich(target): platform, handle = target return platform, handle, get(PROFILE[platform], handle=handle) with ThreadPoolExecutor(max_workers=10) as pool: profiles = list(pool.map(enrich, targets)) rows = [] for platform, handle, body in profiles: if not body["success"]: continue author = body["data"]["author"] rows.append({ "platform": platform, "handle": handle, "followers": author["followers"], "bio": (author["bio"] or "")[:60], "link": author["external_url"], "email": (author["ext"] or {}).get("public_email"), }) rows.sort(key=lambda r: r["followers"] or 0, reverse=True) for r in rows: print(r) spent = sum(b.get("credits_used", 0) for b in found.values()) + \ sum(b.get("credits_used", 0) for _, _, b in profiles) print(f"credits used: {spent}") ``` ```ts title="Node.js" // search-then-enrich.ts const KEY = process.env.INSIGHTSOCIAL_API_KEY; if (!KEY) throw new Error("Set INSIGHTSOCIAL_API_KEY"); const BASE = "https://api.insightsocial.app/v1"; const topic = "home espresso setup"; const keywords = ["espresso"]; const PER_PLATFORM = 5; const search = { tiktok: "tiktok/search", instagram: "instagram/search/reels" }; const profile = { tiktok: "tiktok/profile", instagram: "instagram/profile" }; type P = keyof typeof search; async function get(path: string, params: Record) { const url = new URL(`${BASE}/${path}`); for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v); return (await fetch(url, { headers: { "x-api-key": KEY! } })).json(); } const onTopic = (post: any) => keywords.some((k) => (post.content.text ?? "").toLowerCase().includes(k)); // Step 1: search both platforms. const platforms = Object.keys(search) as P[]; const found = await Promise.all(platforms.map((p) => get(search[p], { query: topic }))); // Step 2: unique authors of on-topic posts, per platform. const targets: [P, string][] = []; found.forEach((body, i) => { if (!body.success) return; const handles = [ ...new Set( body.data.items .map((it: any) => it.post) .filter((post: any) => post.author.username && onTopic(post)) .map((post: any) => post.author.username), ), ]; for (const h of handles.slice(0, PER_PLATFORM)) targets.push([platforms[i], h]); }); // Step 3: enrich each author. const profiles = await Promise.all(targets.map(([p, h]) => get(profile[p], { handle: h }))); const rows = profiles .map((b, i) => b.success && { platform: targets[i][0], handle: targets[i][1], followers: b.data.author.followers, link: b.data.author.external_url, email: b.data.author.ext?.public_email ?? null, }) .filter(Boolean); console.table(rows); ``` ## What you get back [#what-you-get-back] ```text title="Example output" {'platform': 'tiktok', 'handle': '...', 'followers': 1840000, 'bio': 'coffee nerd. latte art every day', 'link': 'https://linktr.ee/...', 'email': None} {'platform': 'instagram', 'handle': '...', 'followers': 412000, 'bio': 'Espresso gear reviews', 'link': None, 'email': 'hello@example.com'} ... credits used: 120 ``` The figures are illustrative. `email` is only present when the account lists one publicly in its bio, and `link` (the profile's `external_url`) only when it has a bio link. The keyword check runs on your side, so it costs nothing; posts it drops were still part of the page you paid for. ## What to change [#what-to-change] * **`TOPIC` and `KEYWORDS`.** Any subject with public video discussion. `KEYWORDS` keeps out posts that only share a word with the topic. Set it to the words that must appear in a caption. * **`ACCOUNTS_PER_PLATFORM`.** Each extra account costs 10 credits. * **Skip the second step on TikTok.** `GET /v1/tiktok/search/users?include=profile` (10–1,310) searches accounts directly and fills bio, region and `external_url` on every row in one call. Use it when you want accounts that match a topic, rather than the authors of posts that match it. * **More platforms.** Add `youtube/search` with `youtube/channel`, or `twitter/search/tweets` with `twitter/profile`. Both profile calls cost 20. * **Go deeper than the profile.** Fetch each account's recent posts (`tiktok/profile/videos`, `instagram/profile/posts`) and score them as in [Creator engagement scoring](/docs/recipes/creator-engagement-scoring), or feed the top posts to the [Video transcription recipe](/docs/recipes/video-transcription-api) to get what was said. ## Related [#related] Rank the accounts you found. Turn the top posts into text. Post searches and account searches side by side. The same searches run as a daily sweep. --- # Sentiment analysis (/docs/recipes/sentiment-analysis) Find the conversation about a topic on YouTube and Reddit, pull the comments, and score each comment's sentiment with a model you run. From 200 credits per run. This recipe measures public mood around a topic in three steps: locate the posts where it is discussed, collect the comments on the most active ones, and score the sentiment of those comments. The API returns the comments; the scoring runs on your side, so you choose the model and can change it without changing the data. 200 to 630 credits. Two searches (YouTube 10–150, Reddit 10–300), three YouTube comment pages (10 each) and three Reddit comment trees (50 each). Scoring the comments costs nothing on the API side. The Reddit leg costs the most because each call returns a whole discussion with nested replies expanded, not one page. ## How do you analyze social media sentiment with an API? [#how-do-you-analyze-social-media-sentiment-with-an-api] Two stages. Search finds the posts where the topic is being discussed. The comment endpoints then return what people said under them, one `comment` per item with its text in `comment.text`. Score each text with a sentiment model and count the results, and you have a sentiment split. Sentiment lives in comments, not in posts. The post says "we changed our pricing". The comments say what people think of it. ## What you need [#what-you-need] | Endpoint | Role | Credits | | -------------------------------- | ----------------------------------------------------------------- | ------- | | `GET /v1/youtube/search` | Find videos about the topic | 10–150 | | `GET /v1/reddit/search` | Find Reddit threads about the topic | 10–300 | | `GET /v1/youtube/video/comments` | Top-level comments on a video | 10 | | `GET /v1/reddit/post/comments` | A thread's full comment tree, replies nested in `comment.replies` | 50 | The code uses [VADER](https://github.com/cjhutto/vaderSentiment) in Python (`pip install vaderSentiment requests`) and the [`sentiment`](https://www.npmjs.com/package/sentiment) package in Node.js (`npm install sentiment`). Both are small English word lists that run offline. Swap in any model you prefer: a multilingual transformer, or a language model with a prompt that names your topic. ## The code [#the-code] ```python title="Python" # sentiment.py # Collects comments about a topic and counts the sentiment split. # Run with: INSIGHTSOCIAL_API_KEY=isk_live_... python sentiment.py import os from collections import Counter from concurrent.futures import ThreadPoolExecutor import requests from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer KEY = os.environ["INSIGHTSOCIAL_API_KEY"] BASE = "https://api.insightsocial.app/v1" TOPIC = "github copilot pricing" KEYWORDS = ["copilot"] # a result must mention one of these to count as on-topic THREADS_PER_PLATFORM = 3 analyzer = SentimentIntensityAnalyzer() def get(path, **params): res = requests.get(f"{BASE}/{path}", params=params, headers={"x-api-key": KEY}, timeout=120) return res.json() def on_topic(post): text = (post["content"]["text"] or "").lower() return any(k in text for k in KEYWORDS) def band(text): # VADER's usual cut-offs on the compound score, from -1 to 1. score = analyzer.polarity_scores(text)["compound"] return "negative" if score <= -0.05 else "positive" if score >= 0.05 else "neutral" def flatten_thread(comments): # Reddit nests replies inside each comment; other platforms leave `replies` null. for c in comments: yield c yield from flatten_thread(c["replies"] or []) # Step 1: find the conversation on both platforms, keeping on-topic results. yt = get("youtube/search", query=TOPIC) rd = get("reddit/search", query=TOPIC, timeframe="month") def top_urls(body, keep=lambda url: True): if not body["success"]: return [] posts = [i["post"] for i in body["data"]["items"]] return [p["url"] for p in posts if p["url"] and keep(p["url"]) and on_topic(p)][:THREADS_PER_PLATFORM] videos = top_urls(yt, keep=lambda u: "watch?v=" in u) threads = top_urls(rd) # Step 2: pull the comments under the top results. jobs = [("youtube/video/comments", u) for u in videos] + \ [("reddit/post/comments", u) for u in threads] with ThreadPoolExecutor(max_workers=6) as pool: pages = list(pool.map(lambda j: (j, get(j[0], url=j[1])), jobs)) # Step 3: score every comment and count the split per platform. split = {"youtube": Counter(), "reddit": Counter()} spent = yt.get("credits_used", 0) + rd.get("credits_used", 0) for (path, url), body in pages: spent += body.get("credits_used", 0) if not body["success"]: continue platform = path.split("/")[0] for c in flatten_thread(item["comment"] for item in body["data"]["items"]): # The scorer is English-only. `language` is often null; keep those. if not c["text"] or c["language"] not in (None, "en"): split[platform]["skipped"] += 1 continue split[platform][band(c["text"])] += 1 for platform, counts in split.items(): print(platform, dict(counts)) print(f"credits used: {spent}") ``` ```ts title="Node.js" // sentiment.ts import Sentiment from "sentiment"; const KEY = process.env.INSIGHTSOCIAL_API_KEY; if (!KEY) throw new Error("Set INSIGHTSOCIAL_API_KEY"); const BASE = "https://api.insightsocial.app/v1"; const topic = "github copilot pricing"; const keywords = ["copilot"]; const scorer = new Sentiment(); async function get(path: string, params: Record) { const url = new URL(`${BASE}/${path}`); for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v); return (await fetch(url, { headers: { "x-api-key": KEY! } })).json(); } const onTopic = (post: any) => keywords.some((k) => (post.content.text ?? "").toLowerCase().includes(k)); const band = (text: string) => { const score = scorer.analyze(text).comparative; return score < -0.05 ? "negative" : score > 0.05 ? "positive" : "neutral"; }; // Reddit nests replies inside each comment; other platforms leave `replies` null. const flattenThread = (comments: any[]): any[] => comments.flatMap((c) => [c, ...flattenThread(c.replies ?? [])]); // Step 1: find the conversation. const [yt, rd] = await Promise.all([ get("youtube/search", { query: topic }), get("reddit/search", { query: topic, timeframe: "month" }), ]); const topUrls = (body: any, keep = (u: string) => true): string[] => body.success ? body.data.items.map((i: any) => i.post).filter((p: any) => p.url && keep(p.url) && onTopic(p)) .map((p: any) => p.url).slice(0, 3) : []; const videos = topUrls(yt, (u) => u.includes("watch?v=")); const threads = topUrls(rd); // Step 2: comments under the top results. const jobs = [ ...videos.map((u) => ["youtube", "youtube/video/comments", u]), ...threads.map((u) => ["reddit", "reddit/post/comments", u]), ]; const pages = await Promise.all(jobs.map(([, path, url]) => get(path, { url }))); // Step 3: score and count. const split: Record> = { youtube: {}, reddit: {} }; pages.forEach((body, i) => { if (!body.success) return; const counts = split[jobs[i][0]]; for (const c of flattenThread(body.data.items.map((it: any) => it.comment))) { const b = !c.text || (c.language !== null && c.language !== "en") ? "skipped" : band(c.text); counts[b] = (counts[b] ?? 0) + 1; } }); console.log(split); ``` ## What you get back [#what-you-get-back] ```text title="Example output" youtube {'negative': 212, 'neutral': 96, 'positive': 71} reddit {'negative': 88, 'neutral': 54, 'positive': 40, 'skipped': 3} credits used: 260 ``` The figures are illustrative. `skipped` counts comments with no text, or tagged with a language other than English. ## Where a word list stops [#where-a-word-list-stops] A word-list scorer judges how a comment feels, not how it feels about your topic in particular. A comment that is angry about a bug in a competing product still reads as negative, and sarcasm reads as praise. When you need sentiment toward one named thing, hand the comment text to a model you run with a prompt that names the topic. The same comments are worth reading for more than polarity. Counting comments that ask a question (a `?`, or "how do I") or mention cancelling is often a sharper signal than "38% negative". ## What to change [#what-to-change] * **`TOPIC` and `KEYWORDS`.** Any brand, product, feature or announcement. `KEYWORDS` drops search results that only share a word with the topic. * **How deep to go.** `THREADS_PER_PLATFORM` sets most of the price. At 3 per platform the floor is 200 credits. At 1 per platform it is 80. * **Other platforms.** Swap in `tiktok/search` with `tiktok/post/comments` (10–30), `twitter/search/tweets` with `twitter/tweet/replies` (10), or `instagram/search/reels` with `instagram/post/comments` (50–150). Every comment list returns the same `comment` shape. * **Other languages.** Comments carry `comment.language` when the platform states it. Route each language to a model that reads it, instead of skipping it. * **A fixed window.** Use `timeframe` on Reddit and `max_age_days` on the searches to cover a launch week. ## Related [#related] Every field on a comment, including `language` and `replies` . The daily feed of posts this pipeline can score. All comment lists side by side, with prices. Walk deeper into a comment thread. --- # TikTok analytics dashboard (/docs/recipes/tiktok-analytics-dashboard) Build the data layer of a TikTok dashboard for accounts you do not own, with account KPIs, a per-video table with engagement rates and a scored comment feed. 30 to 50 credits per refresh. This recipe gathers everything a TikTok dashboard needs for any public account: headline account numbers, a table of how each recent video performed, and the comments on the strongest video, each scored for sentiment on your side. 30 to 50 credits per refresh: `profile` (10), one page of `profile/videos` (10) and one page of `post/comments` (metered, 10–30). Every refresh is charged, so a dashboard should store the last result and refresh on a schedule rather than on every page load. Refreshing 50 accounts once a day costs at least 45,000 credits a month. ## Which calls feed a TikTok dashboard? [#which-calls-feed-a-tiktok-dashboard] A full refresh takes three calls. Start with `GET /v1/tiktok/profile` for followers and lifetime likes. Then `GET /v1/tiktok/profile/videos` gives the latest videos with their view, like, comment, share and save counts, and `GET /v1/tiktok/post/comments` fetches the comments for whichever video URL you pass. The engagement rates are calculated from those counts in your code, with a formula you can show your users. TikTok's built-in analytics stop at the accounts you log in to. Working from public numbers with one fixed engagement formula lets you chart any account, whether for a creator tool, a client report or a brand team. ## What you need [#what-you-need] | Endpoint | Returns | Parameters | Credits | | ------------------------------- | ----------------------------------------------------------- | --------------------------------- | ------- | | `GET /v1/tiktok/profile` | Followers, lifetime likes, bio, verification | `handle` or `user_id` | 10 | | `GET /v1/tiktok/profile/videos` | Recent videos with views, likes, comments, shares and saves | `handle`; next page with `cursor` | 10 | | `GET /v1/tiktok/post/comments` | A page of comments on one video | `url`; next page with `cursor` | 10–30 | Need audience geography? `GET /v1/tiktok/user/audience` (50 credits, beta) returns the top follower countries and each one's share. Age and gender are not public on TikTok, so no endpoint returns them. The comment panel scores sentiment with [VADER](https://github.com/cjhutto/vaderSentiment) in Python (`pip install vaderSentiment`) and the [`sentiment`](https://www.npmjs.com/package/sentiment) package in Node.js. Both read English only; use a multilingual model if your accounts comment in other languages. ## The code [#the-code] ```python title="Python" # tiktok_dashboard.py # One full refresh for a TikTok account you do not own. # Run with: INSIGHTSOCIAL_API_KEY=isk_live_... python tiktok_dashboard.py import os import requests from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer KEY = os.environ["INSIGHTSOCIAL_API_KEY"] BASE = "https://api.insightsocial.app/v1" HANDLE = "khaby.lame" def get(path, **params): res = requests.get(f"{BASE}/{path}", params=params, headers={"x-api-key": KEY}, timeout=120) body = res.json() if not body["success"]: raise RuntimeError(f'{body["error"]["type"]}: {body["error"]["message"]}') return body def ratio(part, whole): # None when either side is unknown; a hidden count is null, not 0. return round(part / whole, 4) if part is not None and whole else None # Panel 1: account KPIs profile = get("tiktok/profile", handle=HANDLE) author = profile["data"]["author"] followers = author["followers"] print("followers:", followers, "(rounded)" if author["followers_approximate"] else "") print("lifetime likes:", author["likes_count"]) # Panel 2: per-video table videos = get("tiktok/profile/videos", handle=HANDLE) rows = [] for item in videos["data"]["items"]: post = item["post"] e = post["engagement"] counts = [e["likes"], e["comments"], e["shares"]] interactions = sum(counts) if None not in counts else None rows.append({ "caption": (post["content"]["text"] or "")[:40], "published_at": post["published_at"], "views": e["views"], "likes": e["likes"], "comments": e["comments"], "shares": e["shares"], "rate_by_views": ratio(interactions, e["views"]), "rate_by_followers": ratio(interactions, followers), "url": post["url"], }) for r in rows[:10]: print(r) # Panel 3: comment feed of the most-viewed video, scored for sentiment analyzer = SentimentIntensityAnalyzer() top = max(rows, key=lambda r: r["views"] or 0, default=None) comments = None if top: comments = get("tiktok/post/comments", url=top["url"]) for item in comments["data"]["items"][:5]: text = item["comment"]["text"] or "" compound = analyzer.polarity_scores(text)["compound"] # -1 to 1 print(f"[{compound:+.2f}] {text}") total = profile["credits_used"] + videos["credits_used"] + (comments["credits_used"] if comments else 0) print("credits used:", total) ``` ```ts title="Node.js" // tiktok-dashboard.ts import Sentiment from "sentiment"; const KEY = process.env.INSIGHTSOCIAL_API_KEY; if (!KEY) throw new Error("Set INSIGHTSOCIAL_API_KEY"); const BASE = "https://api.insightsocial.app/v1"; const handle = "khaby.lame"; async function get(path: string, params: Record) { const url = new URL(`${BASE}/${path}`); for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v); const body = await (await fetch(url, { headers: { "x-api-key": KEY! } })).json(); if (!body.success) throw new Error(`${body.error.type}: ${body.error.message}`); return body; } // null when either side is unknown; a hidden count is null, not 0. const ratio = (part: number | null, whole: number | null) => part !== null && whole ? part / whole : null; // Panel 1: account KPIs const profile = await get("tiktok/profile", { handle }); const { followers, likes_count } = profile.data.author; console.log({ followers, lifetime_likes: likes_count }); // Panel 2: per-video table const videos = await get("tiktok/profile/videos", { handle }); const rows = videos.data.items.map(({ post }: any) => { const { views, likes, comments, shares } = post.engagement; const interactions = [likes, comments, shares].includes(null) ? null : likes + comments + shares; return { caption: post.content.text?.slice(0, 40) ?? "", views, likes, shares, rate_by_views: ratio(interactions, views), rate_by_followers: ratio(interactions, followers), url: post.url, }; }); console.table(rows.slice(0, 10)); // Panel 3: comments on the most-viewed video, scored for sentiment const scorer = new Sentiment(); const top = [...rows].sort((a, b) => (b.views ?? 0) - (a.views ?? 0))[0]; if (top) { const comments = await get("tiktok/post/comments", { url: top.url }); for (const { comment } of comments.data.items.slice(0, 5)) { const score = scorer.analyze(comment.text ?? "").comparative; console.log(`[${score.toFixed(2)}] ${comment.text}`); } } ``` ## What you get back [#what-you-get-back] ```text title="Example output" followers: 161000000 lifetime likes: 2500000000 {'caption': '...', 'published_at': '2026-09-30T18:12:44.000Z', 'views': 12400000, 'likes': 2100000, 'comments': 18400, 'shares': 41000, 'rate_by_views': 0.1743, 'rate_by_followers': 0.0134, 'url': 'https://www.tiktok.com/@khaby.lame/video/...'} {'caption': '...', 'published_at': '2026-09-27T16:40:02.000Z', 'views': 8100000, 'likes': 1400000, 'comments': 12100, 'shares': 22000, 'rate_by_views': 0.1771, 'rate_by_followers': 0.0089, 'url': 'https://www.tiktok.com/@khaby.lame/video/...'} [+0.64] this man never needs to say a word [+0.00] which country is this ... credits used: 30 ``` The figures are illustrative. `rate_by_views` is (likes + comments + shares) / views for each video; `rate_by_followers` divides the same sum by the account's followers. Both are `None` when a count is hidden. The profile's like count is lifetime hearts, so it is a KPI on its own, not an input to a rate. ## What to change [#what-to-change] * **`HANDLE`.** Any public TikTok account. Run the script over a list of handles and store each refresh keyed by `(handle, date)`, and follower and engagement charts come straight out of the table. * **Go past the first page.** Send `pagination.next_cursor` back as `cursor`, with the same `handle` or `url`, to get older videos or more comments. Cursors expire after 24 hours. See [Pagination](/docs/pagination). * **Only new videos.** Pass the id of the newest video you already hold as `stop_at_id`, or a date as `since`. * **Comment order.** Add `sort=recent` for newest first or `sort=top` for most liked. * **Add audience geography.** One `GET /v1/tiktok/user/audience` call adds 50 credits per account. ## Related [#related] Compare this account with its Instagram and YouTube. Snapshot several rivals on four networks and compare over time. The engagement fields and when each one is null. Every TikTok endpoint and its price. --- # Video transcription (/docs/recipes/video-transcription-api) One transcribe() function that returns the spoken words of a video on YouTube, TikTok, Instagram, Facebook, X, Reddit or LinkedIn. 30 credits on YouTube, 100 on the others. You will build one `transcribe(url)` function that returns what was said in a social video, whichever of seven platforms it lives on. Every transcript endpoint answers in the same shape, so the only platform-specific part is choosing the endpoint. 30 credits for a YouTube video, 100 credits for a video on any of the other six platforms. You make exactly one call per video. A video with no captions or no speech is not charged. Transcribing the same URL again is charged again, so store the transcript. ## How do you turn a social video into text? [#how-do-you-turn-a-social-video-into-text] Pass the video URL to that platform's transcript endpoint. Each one returns the spoken content as text. Transcripts are the densest signal in social video: captions and thumbnails are written to get the click, while the spoken words are what the creator actually said. ## What you need [#what-you-need] | Endpoint | Covers | Credits | | ------------------------------------ | ------------------------------------------ | ------- | | `GET /v1/youtube/video/transcript` | YouTube videos and Shorts | 30 | | `GET /v1/tiktok/post/transcript` | TikTok videos | 100 | | `GET /v1/instagram/media/transcript` | Instagram reels and videos up to 2 minutes | 100 | | `GET /v1/facebook/post/transcript` | Facebook video posts | 100 | | `GET /v1/twitter/tweet/transcript` | Videos attached to X posts | 100 | | `GET /v1/reddit/post/transcript` | Reddit video posts | 100 | | `GET /v1/linkedin/post/transcript` | Videos in LinkedIn posts | 100 | Every one takes a `url` parameter. ### When there is nothing to transcribe [#when-there-is-nothing-to-transcribe] * **YouTube, Instagram and LinkedIn** answer `404` when a video has no captions or no speech, and name the reason (for example `no_captions` or `no_speech`). That is an expected outcome, not a failure, and it is not charged. * **Reddit** answers with a transcript whose `text` is `null` when Reddit publishes no captions for the video, and lists `transcript.text` in `unavailable`. * **TikTok** reads the video's captions. Add `use_ai_as_fallback=true` to fall back to AI transcription when there are none. ### One shape on every platform [#one-shape-on-every-platform] Every transcript endpoint returns `data.transcript` with three fields: * **`text`:** the whole transcript as one string, or `null` when there is none. * **`language`:** the language tag the platform gives, such as `en`, or `null`. * **`segments`:** timed cues, each `{ text, start_seconds, duration_seconds }`, or `null` when the platform has no timings. YouTube has them; most of the others return the text only. See [Transcript](/docs/schema/transcript) for the full schema. ## The code [#the-code] ```python title="Python" # transcribe.py # Returns the transcript of a social video URL on any of seven platforms. # Run with: INSIGHTSOCIAL_API_KEY=isk_live_... python transcribe.py import os from urllib.parse import urlparse import requests KEY = os.environ["INSIGHTSOCIAL_API_KEY"] BASE = "https://api.insightsocial.app/v1" ENDPOINT = { "youtube": "youtube/video/transcript", "tiktok": "tiktok/post/transcript", "instagram": "instagram/media/transcript", "facebook": "facebook/post/transcript", "twitter": "twitter/tweet/transcript", "reddit": "reddit/post/transcript", "linkedin": "linkedin/post/transcript", } HOSTS = {"youtube.com": "youtube", "youtu.be": "youtube", "tiktok.com": "tiktok", "instagram.com": "instagram", "facebook.com": "facebook", "fb.watch": "facebook", "x.com": "twitter", "twitter.com": "twitter", "reddit.com": "reddit", "v.redd.it": "reddit", "linkedin.com": "linkedin"} def platform_of(url): host = urlparse(url).netloc.lower().removeprefix("www.").removeprefix("m.") for suffix, platform in HOSTS.items(): if host == suffix or host.endswith("." + suffix): return platform raise ValueError(f"unsupported host: {host}") def extract_text(data): # The same on all seven platforms. `text` is null when there is no transcript. return data["transcript"]["text"] or "" def transcribe(url): platform = platform_of(url) res = requests.get(f"{BASE}/{ENDPOINT[platform]}", params={"url": url}, headers={"x-api-key": KEY}, timeout=120) body = res.json() if not body["success"]: # A 404 here usually means "no captions" or "no speech", and costs nothing. return {"platform": platform, "text": None, "error": body["error"]["type"], "credits_used": body["credits_used"]} return {"platform": platform, "text": extract_text(body["data"]), "language": body["data"]["transcript"]["language"], "segments": body["data"]["transcript"]["segments"], "credits_used": body["credits_used"]} # Any public video URL on the seven platforms works; add your own to the list. for url in [ "https://www.youtube.com/watch?v=dQw4w9WgXcQ", ]: t = transcribe(url) if t["text"] is None: print(f"[{t['platform']}] no transcript ({t['error']}), {t['credits_used']} credits") else: print(f"[{t['platform']}] {len(t['text'])} chars, {t['credits_used']} credits") print(t["text"][:200], "...") ``` ```ts title="Node.js" // transcribe.ts const KEY = process.env.INSIGHTSOCIAL_API_KEY; if (!KEY) throw new Error("Set INSIGHTSOCIAL_API_KEY"); const BASE = "https://api.insightsocial.app/v1"; const ENDPOINT: Record = { youtube: "youtube/video/transcript", tiktok: "tiktok/post/transcript", instagram: "instagram/media/transcript", facebook: "facebook/post/transcript", twitter: "twitter/tweet/transcript", reddit: "reddit/post/transcript", linkedin: "linkedin/post/transcript", }; const HOSTS: [string, string][] = [ ["youtube.com", "youtube"], ["youtu.be", "youtube"], ["tiktok.com", "tiktok"], ["instagram.com", "instagram"], ["facebook.com", "facebook"], ["fb.watch", "facebook"], ["x.com", "twitter"], ["twitter.com", "twitter"], ["reddit.com", "reddit"], ["v.redd.it", "reddit"], ["linkedin.com", "linkedin"], ]; function platformOf(url: string) { const host = new URL(url).hostname.replace(/^(www|m)\./, ""); const hit = HOSTS.find(([h]) => host === h || host.endsWith(`.${h}`)); if (!hit) throw new Error(`unsupported host: ${host}`); return hit[1]; } // The same on all seven platforms. `text` is null when there is no transcript. const extractText = (data: any): string => data.transcript.text ?? ""; export async function transcribe(videoUrl: string) { const platform = platformOf(videoUrl); const url = new URL(`${BASE}/${ENDPOINT[platform]}`); url.searchParams.set("url", videoUrl); const body = await (await fetch(url, { headers: { "x-api-key": KEY! } })).json(); if (!body.success) return { platform, text: null, error: body.error.type }; return { platform, text: extractText(body.data), language: body.data.transcript.language, segments: body.data.transcript.segments, credits_used: body.credits_used, }; } console.log(await transcribe("https://www.youtube.com/watch?v=dQw4w9WgXcQ")); ``` ## What you get back [#what-you-get-back] ```json title="Response (YouTube, trimmed)" { "success": true, "platform": "youtube", "endpoint": "/v1/youtube/video/transcript", "schema_version": "2", "data": { "transcript": { "language": "en", "text": "[♪♪♪] ♪ We're no strangers to love ♪ ♪ You know the rules and so do I ♪ ...", "segments": [ { "text": "[♪♪♪]", "start_seconds": 1.36, "duration_seconds": 1.68 }, { "text": "♪ We're no strangers to love ♪", "start_seconds": 18.64, "duration_seconds": 3.24 } ] } }, "unavailable": [], "credits_used": 30, "credits_remaining": 9940, "request_id": "req_1a2b3c4d5e6f", "cached": false, "idempotent_replay": false, "charge_reason": "miss", "free_call": false } ``` On another platform the same call returns the same keys. Where the platform has no timings, `segments` is `null` and `text` carries the whole transcript. ## What to change [#what-to-change] * **Cheaper YouTube.** If you can parse caption files yourself, `GET /v1/youtube/video/subtitles` (10 credits, beta) returns the caption tracks with download URLs instead. * **Find a moment instead of reading everything.** Where `segments` is filled, search it for a phrase and read `start_seconds` to jump to that point in the video, for example to find a discount code or a sponsor mention. * **Pick a language.** YouTube, TikTok and Reddit take `language`, a two-letter code. * **Ads too.** `GET /v1/facebook/adlibrary/ad/transcript` (100 credits) returns what a Facebook Ad Library video ad says out loud. Pass the ad `id` or its library URL. ## Related [#related] Find the videos worth transcribing. Why YouTube has two options and the others have one. What each error type means and whether to retry. Every YouTube endpoint and its price. --- # Author (/docs/schema/author) The schema-2 author and profile objects. Every field, its type, and which platforms fill it. Any endpoint that returns a profile, creator, page or channel returns it in the profile shape below, whichever platform it came from. Posts and comments carry a smaller author object, a subset of the profile. One parser reads both from every platform. This page describes schema 2, which every key gets from October 4, 2026; what changed is on [Schema 2](/docs/schema-2). ## Where profiles sit in `data` [#where-profiles-sit-in-data] | Endpoint returns | Path | | --------------------------------------------------------------- | ------------------------------------------------------ | | One profile (`/v1/instagram/profile`, `/v1/youtube/channel`, …) | `data.author` | | A list of profiles (followers, search results, reactions, …) | `data.items[].author` | | The author of a post | `data.post.author` or `data.items[].post.author` | | The author of a comment | `data.comment.author` or `data.items[].comment.author` | A list of profiles also carries `data.total` (the platform's own total, not the size of this page) and `data.truncated`, both `null` when the platform does not say. ## Author on a post or comment [#author-on-a-post-or-comment] | Field | Type | Nullable | Description | | -------------- | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | `string` | Yes | The account's platform ID. On Reddit it is the account's `t2_…` ID, prefix kept, the same value as the Reddit profile's `id`. `null` where the source gives none: usually on Threads, Twitter/X and Pinterest posts, and then the response lists the path in `unavailable`. | | `username` | `string` | Yes | Handle. | | `display_name` | `string` | Yes | Display name or full name. On Reddit usually `null`: Reddit shows a username, and the `t2_…` ID that arrives in this place goes to `author.id`. | | `avatar_url` | `string` | Yes | Profile picture. | | `verified` | `boolean` | Yes | Whether the account is verified. | For followers, bio and the rest, call the platform's profile endpoint with the `username` or `id`. ## Profile [#profile] ### Example [#example] `/v1/instagram/profile?handle=nasa`, shortened: ```json title="Response" { "success": true, "platform": "instagram", "endpoint": "/v1/instagram/profile", "schema_version": "2", "data": { "author": { "id": "528817151", "username": "nasa", "display_name": "NASA", "avatar_url": "https://scontent-phl2-1.cdninstagram.com/v/…", "bio": "Making the seemingly impossible, possible. ✨", "verified": true, "followers": 104294818, "followers_approximate": null, "following": 90, "posts_count": 4942, "likes_count": null, "url": "https://www.instagram.com/nasa/", "location": null, "external_url": "https://www.nasa.gov", "private": false, "joined_at": null, "last_post_at": "2026-10-03T14:10:18.000Z", "ext": { "business_category": null } } }, "unavailable": [], "credits_used": 10, "credits_remaining": 9990, "request_id": "req_d6ad52ab1f9f", "cached": false, "idempotent_replay": false, "charge_reason": "miss", "free_call": false } ``` ### Fields [#fields] Every field is always present; a value the platform does not give is `null`. | Field | Type | Nullable | Description | | ----------------------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `id` | `string` | No | The platform's ID for the account, always a string. Never parse it as a number. On a Reddit user it is the account's `t2_…` ID with the prefix kept, so it matches `author.id` on that user's posts and comments. On a LinkedIn person it is the member URN (`ACoAA…`), the same value as `author.id` on that member's posts; the vanity slug is `username`, and the numeric member id is `ext.member_id`. The one exception is `/v1/linkedin/search/people/by-url`, whose source gives no URN: there `id` is the slug. A LinkedIn company keeps its numeric company id. | | `username` | `string` | Yes | Handle. | | `display_name` | `string` | Yes | Display name or full name. | | `avatar_url` | `string` | Yes | Profile picture. | | `bio` | `string` | Yes | Bio or description. | | `verified` | `boolean` | Yes | Whether the account is verified. | | `followers` | `integer` | Yes | Followers or subscribers, as a whole number. Exact on Instagram, and on TikTok when an unrounded figure is available. On YouTube above 1,000 subscribers it is the rounded figure YouTube publishes (three significant digits). | | `followers_approximate` | `boolean` | Yes | `true` when `followers` is a rounded or published figure rather than an exact count: LinkedIn people-list display figures, TikTok when no exact count is available, YouTube at 1,000 subscribers or more, and rounded Facebook counts. `null` when the platform gives an exact count or does not say. | | `following` | `integer` | Yes | Accounts this account follows. | | `posts_count` | `integer` | Yes | Total posts or videos. | | `likes_count` | `integer` | Yes | Total likes received across the account's content, where shown. | | `url` | `string` | Yes | Link to the profile itself, always on the platform's own site. Never the bio link. | | `location` | `string` | Yes | An ISO region code such as `US`, or free-text location, where shown. | | `external_url` | `string` | Yes | The website or bio link, where shown. | | `private` | `boolean` | Yes | Whether the account is private. | | `joined_at` | `string` | Yes | When the account was created, ISO 8601 UTC ending in `Z`. | | `last_post_at` | `string` | Yes | When the account last posted, ISO 8601 UTC ending in `Z`. | | `ext` | `object` | Yes | Platform-specific fields. See below. | A count is `null` when the platform does not show it and `0` only when it really is zero. ## Extension fields (`ext`) [#extension-fields-ext] Platform-specific extras on the full profile. Each one appears only on platforms that provide it, so treat every `ext` field as optional. `ext` is `null` when a profile has no extras. Keys are snake\_case on every platform, `_id` values are strings and `_at` values are ISO 8601 UTC times. | Field | Type | Nullable | Description | | -------------------------------- | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `ext.social_context` | `string` | Yes | Social context text the platform shows on the profile. | | `ext.account_created` | `string` | Yes | Account creation date as the platform shows it. | | `ext.country` | `string` | Yes | Country of the account. | | `ext.former_usernames` | `string[]` | Yes | Earlier usernames, where shown. | | `ext.public_email` | `string` | Yes | The first email address found in the bio. | | `ext.contact_email` | `string` | Yes | Opt in with `contact_email=1` on `/v1/instagram/profile`, `/v1/youtube/channel` and `/v1/youtube/channel/about`. When a bio lists two or more addresses, this is the one the creator gives for personal contact, copied exactly. It stays `null` if the bio has fewer than two addresses, if all of them point to a manager, agency or brand, or if no single personal address stands out. `ext.public_email` is not affected. | | `ext.public_phone` | `string` | Yes | Public phone number on the profile. | | `ext.business_category` | `string` | Yes | Business category shown on the profile. | | `ext.hd_avatar_url` | `string` | Yes | Higher-resolution profile picture. | | `ext.website` | `string` | Yes | Website listed on the profile. | | `ext.cover_url` | `string` | Yes | Cover or banner image. | | `ext.page_active` | `boolean` | Yes | Whether a Facebook page is active. | | `ext.employee_count` | `integer` | Yes | LinkedIn company employee count. | | `ext.employee_count_range.start` | `integer` | Yes | Lower bound of the LinkedIn company size range. | | `ext.employee_count_range.end` | `integer` | Yes | Upper bound of the LinkedIn company size range. | | `ext.founded_year` | `integer` | Yes | Year the company was founded. | | `ext.specialities` | `string[]` | Yes | Company specialties listed on LinkedIn. | | `ext.industries` | `string[]` | Yes | Company industries. | | `ext.headquarters` | `string` | Yes | Company headquarters. | | `ext.locations` | `string[]` | Yes | Company office locations. | | `ext.hashtags` | `string[]` | Yes | Hashtags the company follows or lists. | | `ext.funding` | `string` | Yes | Company funding details. | | `ext.address` | `string` | Yes | Address on a Facebook page. | | `ext.price_range` | `string` | Yes | Price range on a Facebook page. | | `ext.rating` | `string` | Yes | Rating on a Facebook page. | | `ext.rating_count` | `integer` | Yes | Number of ratings on a Facebook page. | | `ext.talking_about_count` | `integer` | Yes | Facebook "talking about this" count. | | `ext.business_hours` | `string[]` | Yes | Opening hours on a Facebook page. | | `ext.links` | `string[]` | Yes | Links listed on the profile. | | `ext.ad_library_page_id` | `string` | Yes | Page ID to use with the Facebook Ad Library endpoints. | | `ext.ad_library_status` | `string` | Yes | Whether the page is running ads. | | `ext.urn` | `string` | Yes | LinkedIn URN. | | `ext.is_top_voice` | `boolean` | Yes | LinkedIn Top Voice badge. | | `ext.is_premium` | `boolean` | Yes | LinkedIn Premium member. | | `ext.is_creator` | `boolean` | Yes | LinkedIn creator mode. | | `ext.is_influencer` | `boolean` | Yes | LinkedIn influencer flag. | | `ext.is_open_to_work` | `boolean` | Yes | LinkedIn Open to Work flag. | | `ext.is_hiring` | `boolean` | Yes | LinkedIn Hiring flag. | | `ext.member_id` | `string` | Yes | LinkedIn numeric member ID, where the source gives one. | | `ext.company_id` | `string` | Yes | LinkedIn company ID, for the `company/*` endpoints. | | `ext.reaction_type` | `string` | Yes | The reaction a member left, on reaction lists. | | `ext.keywords` | `string` | Yes | YouTube channel keywords. | | `ext.total_views` | `integer` | Yes | Total channel views. | | `ext.joined_at_timestamp` | `string` | Yes | Account creation time as a timestamp. | | `ext.topic_categories` | `string[]` | Yes | YouTube topic categories. | | `ext.banner_external_url` | `string` | Yes | YouTube channel banner. | | `ext.made_for_kids` | `boolean` | Yes | YouTube made-for-kids flag. | | `ext.hidden_subscriber_count` | `boolean` | Yes | `true` when the YouTube channel hides its subscriber count. | | `ext.related_playlists` | `object` | Yes | YouTube related playlists, such as uploads. | | `ext.topic_ids` | `string[]` | Yes | YouTube topic IDs. | | `ext.unsubscribed_trailer` | `string` | Yes | YouTube channel trailer shown to non-subscribers. | | `ext.join_policy` | `string` | Yes | Who can join a group or community. | | `ext.is_nsfw` | `boolean` | Yes | Subreddit or account marked adult. | | `ext.rules` | `string[]` | Yes | Subreddit rules. | | `ext.weekly_active_users` | `integer` | Yes | Subreddit weekly visitors. | | `ext.weekly_contributions` | `integer` | Yes | Subreddit weekly contributions. | | `ext.rules_text` | `string` | Yes | Subreddit rules as text. | | `ext.language` | `string` | Yes | Language set on the account or community. | | `ext.post_karma` | `integer` | Yes | Reddit post karma. | | `ext.comment_karma` | `integer` | Yes | Reddit comment karma. | | `ext.awardee_karma` | `integer` | Yes | Reddit awardee karma. | | `ext.trophy_count` | `integer` | Yes | Reddit trophies. | | `ext.banner_url` | `string` | Yes | Profile banner image. | | `ext.social_links` | `string[]` | Yes | Social links listed on the profile. | | `ext.group` | `string` | Yes | Group details, on group responses. | ## Which platforms fill which fields [#which-platforms-fill-which-fields] Every platform that returns profiles uses this shape, and `id` is never `null`. The table covers the platforms the API launched with that return profiles. The others return the same shape, and each response's [`unavailable`](/docs/schema/post#unavailable) list names the fields it could not fill. **yes** means the platform can fill the field (it may still be `null` on a given row); a blank means it never does.
| Platform | `following` | `verified` | `joined_at` | `posts_count` | `likes_count` | `private` | `url` | `followers` | `avatar_url` | `bio` | `location` | `external_url` | | --------- | ----------- | ---------- | ----------- | ------------- | ------------- | --------- | ----- | ----------- | ------------ | ----- | ---------- | -------------- | | Facebook | | yes | yes | | yes | | yes | yes | yes | yes | yes | | | Instagram | yes | yes | | yes | | yes | yes | yes | yes | yes | | yes | | LinkedIn | yes | yes | | | | | yes | yes | | yes | yes | | | Reddit | | | yes | | | yes | yes | yes | yes | yes | | | | Threads | | yes | | | | yes | yes | yes | yes | yes | | | | TikTok | yes | | | yes | yes | yes | yes | yes | yes | yes | yes | yes | | Twitter/X | yes | yes | yes | yes | | yes | yes | yes | yes | yes | yes | yes | | YouTube | | | yes | yes | | | yes | yes | yes | yes | | |
On Instagram, TikTok, Threads and Twitter/X, `url` is built from the handle when the platform sends none, and the link in the bio is always `external_url`. On a Reddit user, `id` is the `t2_…` account ID and `display_name` is `null`; a subreddit's `id` is its name. ### `unavailable` [#unavailable] When a field the platform normally fills is `null` on every profile in a response, the response lists its path in `unavailable`, for example `"author.followers"` or `"items[].author.joined_at"`. `bio`, `external_url` and `location` are never listed, because `null` there usually means the account has none. Fields a platform never shows publicly are never listed either: | Platform | Never listed in `unavailable` | | --------- | ------------------------------------------------------ | | Instagram | `likes_count`, `joined_at` | | TikTok | `joined_at` | | Facebook | `following`, `posts_count`, `private` | | Threads | `following`, `posts_count`, `likes_count`, `joined_at` | | YouTube | `following`, `likes_count`, `private` | | Reddit | `following`, `posts_count`, `likes_count` | | LinkedIn | `posts_count`, `likes_count`, `private`, `joined_at` | For fields that are always `null` on a platform, see [Metric substitutions](/docs/metric-substitutions). ## Returned by [#returned-by] Endpoints that return a profile or a list of profiles use this shape on: [Facebook](/docs/platforms/facebook) · [Instagram](/docs/platforms/instagram) · [LinkedIn](/docs/platforms/linkedin) · [Reddit](/docs/platforms/reddit) · [Threads](/docs/platforms/threads) · [TikTok](/docs/platforms/tiktok) · [Twitter/X](/docs/platforms/twitter) · [YouTube](/docs/platforms/youtube) To see which native platform field feeds each unified field, read [Field equivalence](/docs/schema/field-equivalence). --- # Comment (/docs/schema/comment) The schema-2 comment object. Every field, its type, and which platforms fill it. Any endpoint that returns comments or replies returns them in this shape, whichever platform they came from. One parser reads comments from every platform that has them. This page describes schema 2, which every key gets from October 4, 2026; what changed is on [Schema 2](/docs/schema-2). ## Where comments sit in `data` [#where-comments-sit-in-data] | Endpoint returns | Path | | ----------------------------- | ---------------------- | | One comment | `data.comment` | | A list of comments or replies | `data.items[].comment` | A list also carries `data.total` (the platform's own total, not the size of this page) and `data.truncated`, `true` when the platform cut the thread short (Reddit's "more replies" threshold). Both are `null` when the platform does not say. The next page is in `pagination.next_cursor`. ## Example [#example] One row from `/v1/twitter/tweet/replies`, shortened: ```json title="Response" { "success": true, "platform": "twitter", "endpoint": "/v1/twitter/tweet/replies", "schema_version": "2", "data": { "items": [ { "comment": { "id": "2106150051978662105", "url": "https://x.com/bobcarter101/status/2106150051978662105", "parent_id": null, "post_id": "2106061652617568263", "text": "@NASA @Space_Station skills required; watching SpaceX’s live stream.", "author": { "id": null, "username": "bobcarter101", "display_name": "Bob Carter", "avatar_url": "https://pbs.twimg.com/profile_images/…", "verified": true }, "engagement": { "likes": 3, "replies": 2 }, "flags": { "pinned": null, "deleted": false }, "published_at": "2026-10-02T22:31:17.000Z", "language": "en", "replies": null, "ext": { "author_followers": 4909, "quote_count": 0, "views": 681 } } } ], "total": null, "truncated": null }, "pagination": { "next_cursor": "v2c.ouEEuPti…", "has_more": true, "page_size": 36 }, "unavailable": ["items[].comment.author.id"], "credits_used": 10, "credits_remaining": 9990, "request_id": "req_48e49dfbad28", "cached": false, "idempotent_replay": false, "charge_reason": "miss", "free_call": false } ``` X gives no author ID on replies, so every row has `author.id: null` and the response says so in `unavailable`. ## Fields [#fields] Every field is always present; a value the platform does not give is `null`. | Field | Type | Nullable | Description | | --------------------- | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | `string` | No | The platform's ID for the comment, always a string. | | `url` | `string` | Yes | Link to the comment on the platform. | | `parent_id` | `string` | Yes | ID of the comment this one replies to. `null` on a top-level comment, on every platform. A reply carries its parent comment's ID, never the post's. | | `post_id` | `string` | Yes | ID of the post the comment belongs to. | | `text` | `string` | Yes | The comment text. Placeholders such as `[deleted]` or `[removed]` become `null`, so you never see them as text. | | `author.id` | `string` | Yes | The commenter's platform ID. On Reddit it is the account's `t2_…` ID, prefix kept. `null` where the source gives none: on LinkedIn the source gives no member URN for commenters, so it is `null` there (join on `username`), and the numeric member id is in `ext.author_member_id`. | | `author.username` | `string` | Yes | The commenter's handle. `null` when the comment was removed. | | `author.display_name` | `string` | Yes | The commenter's display name. On Reddit usually `null`: Reddit shows a username, and the `t2_…` ID that arrives in this place goes to `author.id`. | | `author.avatar_url` | `string` | Yes | The commenter's profile picture. | | `author.verified` | `boolean` | Yes | Whether the commenter is verified. | | `engagement.likes` | `integer` | Yes | Likes or upvotes. `null` when not shown, and on Reddit when the score is below zero (see `ext.score`). | | `engagement.replies` | `integer` | Yes | Number of replies. `0` when the platform reports the count and there are none; `null` only when the platform gives no reply count. | | `flags.pinned` | `boolean` | Yes | Pinned by the post's author. `null` when the platform does not say. | | `flags.deleted` | `boolean` | No | The comment was deleted or removed. Always `true` or `false`. | | `published_at` | `string` | Yes | When the comment was posted, as ISO 8601 in UTC, ending in `Z`. | | `language` | `string` | Yes | The comment's language as the platform states it, such as `en`. `null` when the platform gives no tag. We do not detect languages. | | `replies` | `object[]` | Yes | Replies the platform returned inline, each a full comment object with its own `replies`. Filled on Reddit. `null` where the platform returns none inline: fetch them from the platform's replies endpoint with this comment's `id`. | | `ext` | `object` | Yes | Platform-specific fields. See below. | ## Fetching replies [#fetching-replies] Replies endpoints take the platform's own identifiers, which every comment carries: | Platform | Call | | -------- | ----------------------------------------------------------------------------------------------------------- | | YouTube | `/v1/youtube/video/comment/replies?continuationToken=` with the top-level `comment.id` | | Facebook | `/v1/facebook/post/comment/replies` with the comment's `ext.feedback_id` and `ext.expansion_token` | | Reddit | Replies arrive inline in `replies`. When `data.truncated` is `true`, page on with `pagination.next_cursor`. | ## Extension fields (`ext`) [#extension-fields-ext] Platform-specific extras. Each one appears only where the platform provides it, so treat every `ext` field as optional. `ext` is `null` when a comment has no extras. Keys are snake\_case on every platform, `_id` values are strings and `_at` values are ISO 8601 UTC times. `ext` never carries our cursors or tokens. | Field | Type | Nullable | Description | | --------------------------- | ---------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ext.depth` | `integer` | Yes | Reddit nesting level: 0 for a top-level comment, 1 for a direct reply, and so on. Present when a flat thread was rebuilt into `replies[]`, so you can check the nesting yourself. | | `ext.feedback_id` | `string` | Yes | Facebook comment ID used by `/v1/facebook/post/comment/replies`. | | `ext.expansion_token` | `string` | Yes | Facebook token used by `/v1/facebook/post/comment/replies`. | | `ext.ip_location` | `string` | Yes | Location the platform displays for the commenter, where shown. | | `ext.urn` | `string` | Yes | LinkedIn comment URN. | | `ext.reaction_counts` | `string[]` | Yes | Counts broken down by reaction type. | | `ext.is_edited` | `boolean` | Yes | `true` when the comment was edited. | | `ext.author_headline` | `string` | Yes | LinkedIn headline of the commenter. | | `ext.updated_at` | `string` | Yes | When the comment was last updated. | | `ext.author_url` | `string` | Yes | Link to the commenter's profile. | | `ext.viewer_rating` | `string` | Yes | YouTube viewer rating value. | | `ext.text_original` | `string` | Yes | The comment text in its original, unformatted form. | | `ext.preview_replies` | `object[]` | Yes | A few replies shown with the comment. | | `ext.lookup` | `string` | Yes | Details of how a single comment was located, on single-comment endpoints. | | `ext.post_title` | `string` | Yes | Title of the parent post. | | `ext.post_url` | `string` | Yes | Link to the parent post. | | `ext.subreddit` | `string` | Yes | Subreddit of the parent post. | | `ext.subreddit_subscribers` | `integer` | Yes | Subscriber count of that subreddit. | | `ext.post_score` | `integer` | Yes | Score of the parent post. | | `ext.post_comment_count` | `integer` | Yes | Comment count of the parent post. | | `ext.post_author` | `string` | Yes | Author of the parent post. | | `ext.post_published_at` | `string` | Yes | When the parent post was published. | | `ext.post_flair` | `string` | Yes | Flair on the parent post. | | `ext.is_submitter` | `boolean` | Yes | `true` when the commenter also wrote the post. | | `ext.edited_at` | `string` | Yes | When the comment was edited. | | `ext.controversiality` | `integer` | Yes | Reddit controversiality flag. | | `ext.author_followers` | `integer` | Yes | The commenter's follower count as included in the row. A snapshot, not live. Present on every `/v1/twitter/tweet/replies` row. | | `ext.author_following` | `integer` | Yes | The commenter's following count as included in the row. Also a snapshot. | | `ext.author_posts_count` | `integer` | Yes | The commenter's post count as included in the row. Also a snapshot. | | `ext.quote_count` | `integer` | Yes | Number of quote posts of this comment, where reported. On X a reply is itself a post. | | `ext.views` | `integer` | Yes | Views of the comment, where reported. Public on X replies from about 2022 onward. | | `ext.saves` | `integer` | Yes | Bookmarks of the comment, where reported (X). | | `ext.score` | `integer` | Yes | Reddit only. A comment score below zero. `engagement.likes` is then `null`, because a negative score is not a like count. | ## Which platforms fill which fields [#which-platforms-fill-which-fields] Every platform that returns comments uses this shape. `id` and `flags.deleted` are never `null`. The table covers the platforms the API launched with that return comments. The others return the same shape, and each response's [`unavailable`](/docs/schema/post#unavailable) list names the fields it could not fill. **yes** means the platform can fill the field (it may still be `null` on a given row); a blank means it never does.
| Platform | `parent_id` | `author.verified` | `flags.pinned` | `url` | `post_id` | `author.avatar_url` | `author.display_name` | `engagement.replies` | `author.username` | `engagement.likes` | | --------- | ----------- | ----------------- | -------------- | ----- | --------- | ------------------- | --------------------- | -------------------- | ----------------- | ------------------ | | Facebook | | | | | | yes | yes | yes | | yes | | Instagram | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | | LinkedIn | | | yes | | | | yes | yes | | | | Reddit | yes | | yes | yes | yes | yes | | | yes | yes | | Threads | | yes | yes | | | yes | yes | yes | yes | yes | | TikTok | yes | yes | yes | | yes | yes | yes | yes | yes | yes | | Twitter/X | yes | yes | | | yes | yes | yes | yes | yes | yes | | YouTube | | | | | yes | yes | | yes | yes | yes |
`parent_id` is filled on replies only; top-level comments carry `null` everywhere. On Reddit, replies are nested in `replies[]` rather than counted. On Facebook, an opaque `pfbid…` profile ID goes to `author.id`, not `author.username`. On Reddit, the account's `t2_` ID goes to `author.id`. ### `unavailable` [#unavailable] When a field the platform normally fills is `null` on every comment in a response, the response lists its path in `unavailable`, for example `"items[].comment.author.id"`. Fields a platform never shows are never listed: the comment `url` on TikTok, `author.username` on Facebook and `author.display_name` on Reddit. ## Returned by [#returned-by] Endpoints that return a comment or a list of comments use this shape on: [Facebook](/docs/platforms/facebook) · [Instagram](/docs/platforms/instagram) · [LinkedIn](/docs/platforms/linkedin) · [Reddit](/docs/platforms/reddit) · [Threads](/docs/platforms/threads) · [TikTok](/docs/platforms/tiktok) · [Twitter/X](/docs/platforms/twitter) · [YouTube](/docs/platforms/youtube) To see which native platform field feeds each unified field, read [Field equivalence](/docs/schema/field-equivalence). --- # Field equivalence (/docs/schema/field-equivalence) Which native platform field feeds each unified field, for the metrics people compare most across platforms. Each platform names the same number its own way: TikTok says `play_count`, YouTube says `viewCount`, Twitter/X says `views.count`. In a response, all three arrive as `post.engagement.views`. The tables below show, per platform, which native fields a unified field is filled from. Build comparisons on the unified field once and they work across every platform listed. Several native names per platform means different endpoints on that platform expose the value under different names. The paths on this page are schema 2, which every key gets from October 4, 2026. ## Fields that moved in schema 2 [#fields-that-moved-in-schema-2] Some values used to sit in `ext` on a few platforms. Schema 2 gives them one place on every platform: | Value | Previous place | Schema 2 | | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | Author ID on a post | `ext.author_id` (Facebook, Instagram lists, LinkedIn, TikTok), `ext.channel_id` (YouTube) | `post.author.id` | | Author ID on a comment | `ext.author_id`, `ext.author_channel_id` (YouTube), Reddit's `t2_` ID in `author.display_name`, Facebook's `pfbid…` ID in `author.username` | `comment.author.id`. Reddit's stays `t2_…`, prefix kept. | | Reddit account ID on a post or profile | `author.display_name` (posts), `display_name` (profiles) | `post.author.id` and the profile's `id`, as `t2_…` with the prefix kept, so posts, comments and profiles join on one value. `display_name` is then `null`. | | Language of a post or comment | `ext.content_language`, `ext.default_language` | `post.language`, `comment.language` | | Rounded follower count | `ext.followers_approximate` | `author.followers_approximate` (profiles) | | Bio link | `ext.bio_link` (TikTok, X), `url` (Instagram) | `author.external_url`. `author.url` is always the profile. | | Original Unix timestamp | `ext.published_at_epoch` | Removed. `published_at` is always ISO 8601 UTC. | | Post format | `ext.media_type`, `ext.content_type`, `ext.post_type` | `post.kind`. The `ext` keys stay where the platform sends them. | | YouTube `ext` keys | camelCase | snake\_case, such as `made_for_kids`, `category_id`, `default_audio_language` | `author.id` is `null` where the source gives no ID (usually Threads, Twitter/X and Pinterest posts), and the response lists it in `unavailable`. ## Engagement [#engagement] ### `post.engagement.views` [#postengagementviews] | Platform | Native field | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Facebook | `interested_count`, `reachEstimate`, `reach_estimate`, `social_context.interested_count`, `videoViewCount`, `view_count` | | Instagram | `data.xdt_shortcode_media.video_play_count`, `items.0.play_count`, `media.play_count`, `play_count` | | Threads | `post.view_counts`, `view_counts` | | TikTok | `aweme_info.statistics.play_count`, `ch_info.view_count`, `impression`, `lifetime_views`, `statistics.play_count`, `stats.playCount`, `userCount`, `vv_count` | | Twitter/X | `view_count`, `views.count` | | YouTube | `viewCount`, `viewCountInt` | The Facebook `interested_count` entries are events, where views carries the interested count; see [Metric substitutions](/docs/metric-substitutions). ### `post.engagement.likes` [#postengagementlikes] | Platform | Native field | | --------- | ---------------------------------------------------------------------------------------------------------------- | | Facebook | `going_count`, `like_count`, `reactionCount`, `reactions_count`, `social_context.going_count` | | Instagram | `data.xdt_shortcode_media.edge_media_preview_like.count`, `items.0.like_count`, `like_count`, `media.like_count` | | LinkedIn | `activity.num_likes`, `data.activity.num_likes`, `stats.total_reactions` | | Pinterest | `pin_count`, `reaction_counts.1`, `totalReactionCount` | | Reddit | `score`, `ups` (a score below zero gives `null` here and `ext.score`) | | Threads | `like_count`, `post.like_count` | | TikTok | `aweme_info.statistics.digg_count`, `like`, `likes`, `statistics.digg_count`, `stats.diggCount` | | Twitter/X | `favorite_count`, `legacy.favorite_count` | | YouTube | `likeCount`, `likeCountInt` | ### `post.engagement.comments` [#postengagementcomments] | Platform | Native field | | --------- | ------------------------------------------------------------------------------------------------------------------------------ | | Facebook | `commentCount`, `comment_count`, `comments_count` | | Instagram | `comment_count`, `data.xdt_shortcode_media.edge_media_to_parent_comment.count`, `items.0.comment_count`, `media.comment_count` | | LinkedIn | `activity.num_comments`, `data.activity.num_comments`, `stats.comments` | | Pinterest | `aggregatedPinData.commentCount`, `aggregated_pin_data.did_it_data.responses_count` | | Reddit | `comments`, `num_comments` | | Threads | `post.text_post_app_info.direct_reply_count`, `text_post_app_info.direct_reply_count` | | TikTok | `aweme_info.statistics.comment_count`, `comment`, `statistics.comment_count`, `stats.commentCount` | | Twitter/X | `legacy.reply_count`, `reply_count` | | YouTube | `commentCount`, `commentCountInt` | ### `post.engagement.shares` [#postengagementshares] | Platform | Native field | | --------- | ------------------------------------------------------------------------------------------ | | Facebook | `share_count` | | Instagram | `items.0.reshare_count` | | LinkedIn | `activity.num_shares`, `data.activity.num_shares`, `stats.reposts` | | Pinterest | `repin_count`, `shareCount` | | Threads | `post.text_post_app_info.repost_count`, `text_post_app_info.repost_count` | | TikTok | `aweme_info.statistics.share_count`, `share`, `statistics.share_count`, `stats.shareCount` | | Twitter/X | `legacy.retweet_count`, `retweet_count` | ### `post.engagement.saves` [#postengagementsaves] | Platform | Native field | | --------- | --------------------------------------------------------------------------------------- | | Instagram | `items.0.save_count` | | Pinterest | `aggregatedPinData.aggregatedStats.saves`, `aggregated_pin_data.aggregated_stats.saves` | | TikTok | `aweme_info.statistics.collect_count`, `statistics.collect_count`, `stats.collectCount` | | Twitter/X | `bookmark_count`, `legacy.bookmark_count` | A platform listed here may still return `null` on most surfaces; [Metric substitutions](/docs/metric-substitutions) says where. A platform missing from a table never fills that field: see [Never shown publicly](#never-shown-publicly). ### Never shown publicly [#never-shown-publicly] These post counts are not shown publicly on most of the platform. They are `null` on most endpoints, and a response never lists them in `unavailable`: | Platform | Post fields | | --------- | ------------------------------------------------------------------------------------------------------------------------------- | | Instagram | `engagement.shares`, `engagement.saves` (Sends and saves come only from the stats endpoints named on [Post](/docs/schema/post)) | | Facebook | `engagement.saves`, `author.username` | | Threads | `engagement.saves` | | YouTube | `engagement.shares`, `engagement.saves` | | Reddit | `engagement.views`, `engagement.shares`, `engagement.saves`, `author.display_name` | | LinkedIn | `engagement.views`, `engagement.saves` | | Pinterest | `engagement.views` | ## Post content [#post-content] ### `post.content.text` [#postcontenttext] | Platform | Native field | | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | Facebook | `accessibility_caption`, `description`, `message`, `name`, `snapshot.body`, `snapshot.body.text`, `text`, `title` | | Instagram | `caption`, `caption.text`, `data.xdt_shortcode_media.edge_media_to_caption.edges.0.node.text`, `items.0.caption.text`, `media.caption.text`, `title` | | LinkedIn | `data.text`, `description`, `text`, `title` | | Pinterest | `description`, `name`, `title` | | Reddit | `title` | | Threads | `caption.text`, `post.caption.text` | | TikTok | `adTitle`, `ad_title`, `aweme_info.desc`, `ch_info.cha_name`, `desc`, `hashtag`, `name`, `title` | | Twitter/X | `note_tweet.note_tweet_results.result.text` | | YouTube | `content`, `title` | ### `post.published_at` [#postpublished_at] | Platform | Native field | | --------- | ------------------------------------------------------------------------------------------------------- | | Facebook | `creation_time`, `publishTime`, `startDateString`, `start_date_string`, `start_timestamp`, `timestamp` | | Instagram | `data.xdt_shortcode_media.taken_at_timestamp`, `items.0.taken_at`, `media.taken_at`, `taken_at` | | LinkedIn | `created_at`, `data.created_at`, `posted_at.timestamp`, `startDate` | | Pinterest | `createdAt`, `created_at` | | Reddit | `created_utc`, `creationDate` | | Threads | `post.taken_at`, `taken_at` | | TikTok | `aweme_info.create_time`, `createTime`, `create_time`, `created_at`, `first_shown_date`, `publish_time` | | Twitter/X | `created_at`, `legacy.created_at` | | YouTube | `publishDate`, `publishedAt`, `publishedTime`, `videoPublishedAt` | Whatever the native format (ISO string, Unix seconds or milliseconds, X's own date text), `published_at` is ISO 8601 in UTC, ending in `Z`. ## Author [#author] ### `author.followers` [#authorfollowers] | Platform | Native field | | --------- | ---------------------------------------------------------------------------------------------------------------- | | Facebook | `followerCount`, `likes`, `member_count` | | Instagram | `data.user.edge_followed_by.count`, `follower_count`, `user.follower_count` | | LinkedIn | `data.follower_and_connection.follower_count`, `data.follower_count`, `follower_count`, `follower_count_display` | | Reddit | `subscribers` | | Threads | `follower_count` | | TikTok | `follower_count`, `user.follower_count`, `user_info.follower_count` | | Twitter/X | `legacy.followers_count`, `member_count` | | YouTube | `SubscriberCount`, `subscriberCount` | ### `author.following` [#authorfollowing] | Platform | Native field | | --------- | ------------------------------------------------------------------------ | | Instagram | `data.user.edge_follow.count`, `following_count`, `user.following_count` | | LinkedIn | `data.follower_and_connection.connection_count` | | TikTok | `following_count`, `user.following_count`, `user_info.following_count` | | Twitter/X | `legacy.friends_count` | ### `author.posts_count` [#authorposts_count] | Platform | Native field | | --------- | --------------------------------------------------------------------------------- | | Instagram | `data.user.edge_owner_to_timeline_media.count`, `media_count`, `user.media_count` | | TikTok | `aweme_count`, `user.aweme_count`, `user_info.aweme_count` | | Twitter/X | `legacy.statuses_count` | | YouTube | `TotalVideosCount`, `videoCount` | ### `author.likes_count` [#authorlikes_count] | Platform | Native field | | -------- | ---------------------------------------------------------------------- | | Facebook | `likeCount` | | TikTok | `total_favorited`, `user.total_favorited`, `user_info.total_favorited` | Reddit and Threads profiles never carry `following`, `posts_count` or `likes_count`; Reddit karma is in `ext.post_karma` and `ext.comment_karma`. For every field, its nullability and per-platform coverage, see the [Post](/docs/schema/post), [Author](/docs/schema/author) and [Comment](/docs/schema/comment) pages. --- # Job (/docs/schema/job) The job posting shape returned by the LinkedIn job endpoints. A beta, long-tail shape in schema 2. Endpoints that return job postings use this shape. On InsightSocial that means LinkedIn. Schema 2 types four entities: [post](/docs/schema/post), [profile](/docs/schema/author), [comment](/docs/schema/comment) and [transcript](/docs/schema/transcript). The job endpoints are long-tail: their `data` is the source's own structure under our naming rules (snake\_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities, and its fields may change. The next page is in `pagination.next_cursor`. The fields below are what these endpoints return today. They are marked beta in the [API reference](/docs/api-reference) and can change without a schema version, so read them defensively. ## Fields [#fields] | Field | Type | Nullable | Description | | ------------------ | --------- | -------- | ----------------------------------------- | | `id` | `string` | No | The platform's ID for the job. | | `title` | `string` | Yes | Job title. | | `url` | `string` | Yes | Link to the posting. | | `company.id` | `string` | Yes | ID of the hiring company. | | `company.name` | `string` | Yes | Company name. | | `company.url` | `string` | Yes | Link to the company page. | | `company.verified` | `boolean` | Yes | Whether the company page is verified. | | `location` | `string` | Yes | Where the job is based. | | `listed_at` | `string` | Yes | When the job was posted, as ISO 8601 UTC. | | `easy_apply` | `boolean` | Yes | Whether the job accepts Easy Apply. | | `remote` | `string` | Yes | Remote, hybrid or on-site status. | | `employment_type` | `string` | Yes | Full-time, part-time, contract and so on. | | `experience_level` | `string` | Yes | Seniority level. | | `description` | `string` | Yes | Job description. | | `apply_url` | `string` | Yes | Where to apply. | ## Extension fields (`ext`) [#extension-fields-ext] Optional extras. Treat each as possibly absent. | Field | Type | Nullable | Description | | ---------------------------- | ---------- | -------- | --------------------------------------------- | | `ext.is_promote` | `boolean` | Yes | Whether the posting is promoted. | | `ext.applicant_count` | `integer` | Yes | Number of applicants shown. | | `ext.skills` | `string[]` | Yes | Skills listed on the job. | | `ext.job_provider` | `string` | Yes | Where the posting originates. | | `ext.salary_min` | `integer` | Yes | Lower end of the salary range. | | `ext.salary_max` | `integer` | Yes | Upper end of the salary range. | | `ext.salary_currency` | `string` | Yes | Salary currency. | | `ext.country_code` | `string` | Yes | Country of the job. | | `ext.linkedin_company_name` | `string` | Yes | Company name as shown on LinkedIn. | | `ext.industries` | `string` | Yes | Industries of the job. | | `ext.job_function` | `string` | Yes | Job function. | | `ext.accepting_applications` | `boolean` | Yes | Whether the job is still taking applications. | | `ext.board` | `string` | Yes | Job board the posting came from. | ## Which fields LinkedIn fills [#which-fields-linkedin-fills] `id` is never `null`. **yes** means LinkedIn can fill the field (it may still be `null` on a given row); a blank means it never does.
| Platform | `company.id` | `company.url` | `company.verified` | `easy_apply` | `experience_level` | `remote` | | -------- | ------------ | ------------- | ------------------ | ------------ | ------------------ | -------- | | LinkedIn | yes | yes | yes | yes | | |
## Returned by [#returned-by] [`/v1/linkedin/job`](/docs/api-reference/linkedin/job), [`/v1/linkedin/company/jobs`](/docs/api-reference/linkedin/company-jobs) and [`/v1/linkedin/search/jobs`](/docs/api-reference/linkedin/search-jobs). See [LinkedIn](/docs/platforms/linkedin). --- # Media list (/docs/schema/media-list) The list shape returned by the YouTube media-file endpoints. A beta, long-tail shape in schema 2. Endpoints that return a video's files (audio and video streams, subtitle tracks or thumbnails) use this shape. These endpoints are long-tail: schema 2 does not type their `data` as an entity. It is the source's own structure under our naming rules (snake\_case keys, such as `mime_type` rather than `mimeType`, string ids, ISO times where the source gives a timestamp), and its fields may change. They are marked beta in the [API reference](/docs/api-reference) and can change without a schema version. ## Fields [#fields] | Field | Type | Nullable | Description | | ------- | ---------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `items` | `object[]` | No | The files for the video. Each item has the file's direct `url` plus its own technical details, such as `mime_type`, `bitrate`, `quality`, `width` and `height`, or `language`. Which details appear depends on the kind of file. Items keep the platform's structure, with snake\_case keys; they are not remapped into a unified object. | | `total` | `integer` | Yes | Total number of results, when the platform reports one. Left out otherwise. | ## Where it is used [#where-it-is-used] Only YouTube returns this shape, on four endpoints. The fields inside each item follow the platform's structure, so there is no per-field availability table. When an endpoint pages, the next page is in `pagination.next_cursor`; see [Pagination](/docs/pagination). | Platform | Endpoints | | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | YouTube | [`/v1/youtube/video/audio`](/docs/api-reference/youtube/video-audio), [`/v1/youtube/video/files`](/docs/api-reference/youtube/video-files), [`/v1/youtube/video/subtitles`](/docs/api-reference/youtube/video-subtitles), [`/v1/youtube/video/thumbnails`](/docs/api-reference/youtube/video-thumbnails) | See [YouTube](/docs/platforms/youtube) for the rest of the platform's endpoints. --- # Post (/docs/schema/post) The schema-2 post object. Every field, its type, and which platforms fill it. Any endpoint that returns a post returns it in this shape, whichever platform it came from. Write one parser and it reads posts from every platform the API serves. This page describes schema 2, which every key gets from October 4, 2026. What changed from the previous shape is on [Schema 2](/docs/schema-2). ## Where posts sit in `data` [#where-posts-sit-in-data] | Endpoint returns | Path | | ----------------------------------------------------------------------- | ------------------- | | One post (`/v1/instagram/post`, `/v1/youtube/video`, …) | `data.post` | | A list of posts (`/v1/instagram/profile/posts`, `/v1/tiktok/search`, …) | `data.items[].post` | A list also carries `data.total`, the total the platform states (not the size of this page), and `data.truncated`, `true` when the platform cut the list short. Both are `null` when the platform does not say. The next page is in `pagination.next_cursor`; see [Pagination](/docs/pagination). ## Example [#example] One row from `/v1/instagram/profile/posts?handle=nasa`, shortened: ```json title="Response" { "success": true, "platform": "instagram", "endpoint": "/v1/instagram/profile/posts", "schema_version": "2", "data": { "items": [ { "post": { "id": "3983374110243288826", "url": "https://www.instagram.com/nasa/p/DdHyaYAifb6/", "kind": "carousel", "content": { "text": "Cementing their names in history. …", "media_urls": ["https://scontent-lga3-2.cdninstagram.com/v/…", "https://scontent-lga3-3.cdninstagram.com/v/…"], "thumbnail_url": null, "duration_seconds": null }, "author": { "id": "528817151", "username": "nasa", "display_name": "NASA", "avatar_url": "https://scontent-lga3-1.cdninstagram.com/v/…", "verified": true }, "engagement": { "views": null, "likes": 120041, "comments": 978, "shares": null, "saves": null }, "flags": { "nsfw": null, "spoiler": null, "pinned": true, "deleted": false, "likes_hidden": null, "comments_hidden": null, "shares_hidden": null, "views_hidden": null, "saves_hidden": null }, "published_at": "2026-09-10T21:20:24.000Z", "language": null, "ext": { "media_type": "carousel", "carousel_count": 4 } } } ], "total": null, "truncated": null }, "pagination": { "next_cursor": "v2c.ZIyWbi3x…", "has_more": true, "page_size": 12 }, "unavailable": [], "credits_used": 10, "credits_remaining": 9990, "request_id": "req_bc4ed40ba570", "cached": false, "idempotent_replay": false, "charge_reason": "miss", "free_call": false } ``` ## Fields [#fields] Every field below is always present. A value the platform does not give is `null`, never left out and never a placeholder. | Field | Type | Nullable | Description | | -------------------------- | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | `string` | No | The platform's ID for the post. Always a string. Never parse it as a number: Instagram and X IDs are larger than 2^53. | | `url` | `string` | Yes | Link to the post on the platform. | | `kind` | `string` | Yes | The post's format: `text`, `image`, `carousel`, `video`, `short`, `live`, `article` or `link`. `short` is short-form vertical video (a reel, a TikTok, a YouTube Short). `null` when the platform does not say and it cannot be read without guessing. More values may be added, so keep a default branch when you switch on it. | | `content.text` | `string` | Yes | Caption, description or body text. | | `content.media_urls` | `string[]` | Yes | Main media, always an array, one URL per item, even for a single photo. `null` when the platform gives none. | | `content.thumbnail_url` | `string` | Yes | Thumbnail image. | | `content.duration_seconds` | `number` | Yes | Length of a video or clip in seconds. `null` when the post is not a video. | | `author.id` | `string` | Yes | The author's platform ID. On Reddit it is the account's `t2_…` ID, prefix kept. On LinkedIn a person's member URN (`ACoAA…`), as on their profile, with the numeric member id in `ext.author_member_id`; a company's numeric id. `null` where the source gives none; see [Which platforms fill which fields](#which-platforms-fill-which-fields). | | `author.username` | `string` | Yes | The author's handle. | | `author.display_name` | `string` | Yes | The author's display name. On Reddit usually `null`: Reddit shows a username, and the `t2_…` ID that arrives in this place goes to `author.id`. | | `author.avatar_url` | `string` | Yes | The author's profile picture. | | `author.verified` | `boolean` | Yes | Whether the author is verified. | | `engagement.views` | `integer` | Yes | Views where the platform shows them. On Instagram videos and reels this is the play count shown in the app, and it counts Instagram plays only, not views of a copy cross-posted to Facebook. Fetch the Facebook copy with `/v1/facebook/post` if you need both. | | `engagement.likes` | `integer` | Yes | Likes or reactions. On Reddit a score below zero is not a like count: `likes` is `null` and the score is in `ext.score`. | | `engagement.comments` | `integer` | Yes | Comment count. On `/v1/instagram/post` it counts top-level comments; the Instagram list endpoints count all comments including replies. Both use this field. | | `engagement.shares` | `integer` | Yes | Shares, reposts or retweets. On Instagram this is the Send count, available only from `/v1/instagram/post/stats`, `/v1/instagram/profile/reels/full` and `/v1/instagram/profile/posts/full`; other Instagram endpoints return `null`. | | `engagement.saves` | `integer` | Yes | Saves or bookmarks. | | `flags.nsfw` | `boolean` | Yes | Marked as adult content. `null` when the platform does not say. | | `flags.spoiler` | `boolean` | Yes | Marked as a spoiler. `null` when the platform does not say. | | `flags.pinned` | `boolean` | Yes | Pinned to the profile. `null` when the platform does not say. | | `flags.deleted` | `boolean` | No | The post has been deleted or removed. Always `true` or `false`. | | `flags.likes_hidden` | `boolean` | Yes | `true` when the creator hides the like count; `engagement.likes` is then `null`. `null` on ordinary posts. | | `flags.comments_hidden` | `boolean` | Yes | `true` when the comment count is hidden; `engagement.comments` is then `null`. | | `flags.shares_hidden` | `boolean` | Yes | `true` when the share count is hidden; `engagement.shares` is then `null`. | | `flags.views_hidden` | `boolean` | Yes | `true` when the view count is hidden; `engagement.views` is then `null`. | | `flags.saves_hidden` | `boolean` | Yes | `true` when the save count is hidden; `engagement.saves` is then `null`. | | `published_at` | `string` | Yes | When the post was published, as ISO 8601 in UTC, ending in `Z`. Platforms that send Unix timestamps are converted. | | `language` | `string` | Yes | The post's language as the platform itself states it, such as `en` or `pt-BR`. `null` when the platform gives no tag. We do not detect languages. | | `ext` | `object` | Yes | Platform-specific fields. See below. | ### Counts: `null` is not `0` [#counts-null-is-not-0] A count is a whole number of zero or more, or `null`. `null` means the platform does not show it, or hides it on this post. `0` means it really is zero. Do not add a `null` into a total as if it were zero. ## Extension fields (`ext`) [#extension-fields-ext] Platform-specific extras. Each one appears only on platforms that provide it and is left out elsewhere, so treat all of them as optional. They hold detail the unified fields cannot, and the IDs that let you pass a result from one endpoint into the next. `ext` is `null` when a post has no extras. Keys are always snake\_case, on every platform, YouTube included (`made_for_kids`, `category_id`). An `ext` value whose key ends in `_id` is a string, and one whose key ends in `_at` is an ISO 8601 UTC time. `ext` never carries our cursors or tokens; page with `pagination.next_cursor`. | Field | Type | Nullable | Description | | -------------------------------- | ---------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ext.music_id` | `string` | Yes | TikTok sound ID. Pass it to `/v1/tiktok/song/videos?clipId=` to find other videos using the same sound. | | `ext.author_followers` | `integer` | Yes | The author's follower count as carried on the row: X and TikTok post rows, and searches that include it. A snapshot, not live. Always `null` on Instagram rows; call `/v1/instagram/profile?handle=` for the exact current count. | | `ext.author_following` | `integer` | Yes | Following count included in the row, when present. | | `ext.author_posts_count` | `integer` | Yes | The author's post count included in the row, when present. | | `ext.download_count` | `integer` | Yes | TikTok only. How many times the video was downloaded to a device. Separate from `engagement.saves`, which counts saves to an in-app collection. | | `ext.region` | `string` | Yes | TikTok only. The ISO 3166-1 alpha-2 country the video is registered to, usually the creator's account country. It is not the viewer's country or a language. On `/v1/tiktok/trending`, `/v1/tiktok/search` and `/v1/tiktok/search/top`, filter on it to keep rows from one country. | | `ext.subreddit` | `string` | Yes | Reddit subreddit name on search and list rows. Pass it to `/v1/reddit/subreddit/details?subreddit=`. | | `ext.title` | `string` | Yes | Post title, where the platform separates title from body. | | `ext.selftext` | `string` | Yes | Reddit text-post body. | | `ext.upvote_ratio` | `integer` | Yes | Reddit only. Share of votes that are upvotes, from 0 to 1. The value is a fraction even though the type says integer. Filled on `/v1/reddit/search` rows where available, `null` elsewhere. | | `ext.flair` | `string` | Yes | Reddit post flair. | | `ext.type` | `string` | Yes | Platform's own post type. | | `ext.content_type` | `string` | Yes | Platform's own content type. `kind` is read from it where it is present. | | `ext.video_view_count` | `integer` | Yes | Instagram's old 3-second view count. No longer served, so new lookups do not carry it. Use `engagement.views`. | | `ext.ig_play_count` | `integer` | Yes | Instagram's own play count, shown separately so you can tell it apart from any combined figure. Present on `/v1/instagram/post/stats`, and on `/v1/instagram/post` for videos. | | `ext.repost_count` | `integer` | Yes | Repost count, where it is separate from shares. | | `ext.media_type` | `string` | Yes | Platform's media type (photo, video, carousel and so on). `kind` is read from it where it is present. | | `ext.text_truncated` | `boolean` | Yes | `true` when the text was cut short on this surface. | | `ext.ip_location` | `string` | Yes | Location the platform displays for the poster, where shown. | | `ext.carousel_count` | `integer` | Yes | Number of items in a carousel. | | `ext.remix_count` | `integer` | Yes | Number of remixes. | | `ext.facebook_likes` | `integer` | Yes | Instagram only. Facebook likes from a cross-post that were removed from `engagement.likes`, so that field matches what instagram.com shows. Add them back for a combined figure. Absent when nothing was removed or the like count is hidden. | | `ext.facebook_comments` | `integer` | Yes | Instagram only. The comment equivalent of `ext.facebook_likes`. Each of the two appears only for the count it changed. | | `ext.usertags` | `string[]` | Yes | Accounts tagged in the post. | | `ext.coauthors` | `string[]` | Yes | Instagram Collab posts. Every co-author as `{ id, username, full_name, is_verified, profile_pic_url }`. A Collab post has one creator but shows on every co-author's grid, so `post.author` may not be the profile you asked for. All accounts on the post are `post.author.username` plus each `username` here. An empty array means not a Collab. Absent where there is no signal, including on `/v1/instagram/post`; use `/v1/instagram/post/stats` for a single post. | | `ext.music` | `string` | Yes | Music or sound attached to the post. | | `ext.sponsor_tags` | `string[]` | Yes | Sponsor or paid-partnership tags. | | `ext.location` | `string` | Yes | Location tagged on the post. | | `ext.quoted_post` | `string` | Yes | Threads quote posts. The quoted post as `{ id, url, author: { id, username }, text, media_urls, thumbnail_url }`. A quote post often has no text or media of its own, and what you see on its page belongs to the quoted post by another author. It stays here so `content.*` always describes the outer post. Absent on posts that are not quotes. | | `ext.retweeted_post` | `string` | Yes | Twitter/X: the original post behind a retweet. | | `ext.quote_count` | `integer` | Yes | Number of quote posts. | | `ext.all_media_urls` | `string[]` | Yes | Every media URL on the post. | | `ext.reaction_counts` | `string[]` | Yes | Counts broken down by reaction type. | | `ext.share_urn` | `string` | Yes | LinkedIn share URN. | | `ext.post_type` | `string` | Yes | Platform's post type. | | `ext.activity_id` | `string` | Yes | LinkedIn activity ID. | | `ext.published_at_precision` | `string` | Yes | How precise `published_at` is when the platform shows only a relative or rounded time. | | `ext.author_urn` | `string` | Yes | LinkedIn URN of the author. | | `ext.author_headline` | `string` | Yes | LinkedIn headline of the author. | | `ext.author_type` | `string` | Yes | Whether the LinkedIn author is a person or a company. | | `ext.is_repost_quote` | `boolean` | Yes | `true` when a repost adds its own commentary. | | `ext.article` | `string` | Yes | LinkedIn article attached to the post. | | `ext.reaction_type` | `string` | Yes | The reaction a member left, on reaction lists. | | `ext.download_media_urls` | `string[]` | Yes | Downloadable media URLs, when requested. | | `ext.tags` | `string[]` | Yes | Tags attached to the post. | | `ext.category_id` | `string` | Yes | YouTube category ID. | | `ext.category_title` | `string` | Yes | YouTube category name. | | `ext.topic_categories` | `string[]` | Yes | YouTube topic categories. | | `ext.duration` | `string` | Yes | YouTube duration in its original format. | | `ext.license` | `string` | Yes | YouTube license. | | `ext.made_for_kids` | `boolean` | Yes | YouTube made-for-kids flag. | | `ext.default_audio_language` | `string` | Yes | YouTube default audio language. | | `ext.has_paid_product_placement` | `boolean` | Yes | YouTube paid product placement flag. | | `ext.caption` | `string` | Yes | YouTube caption status. | | `ext.position` | `integer` | Yes | Position in a YouTube playlist. | | `ext.playlist_id` | `string` | Yes | YouTube playlist ID. | | `ext.video_owner_channel_id` | `string` | Yes | Channel that owns a video in a playlist. | | `ext.video_published_at` | `string` | Yes | When a playlist video was published. | | `ext.description` | `string` | Yes | Full description, where separate from `content.text`. | | `ext.playlist_item_id` | `string` | Yes | YouTube playlist item ID. | | `ext.playlist_owner_channel_id` | `string` | Yes | Channel that owns the playlist. | | `ext.playlist_owner_title` | `string` | Yes | Name of the playlist owner. | | `ext.published_label` | `string` | Yes | The relative time label the platform shows, such as "3 days ago". | | `ext.published_precision` | `string` | Yes | How precise the published time is. | | `ext.video_count` | `integer` | Yes | Number of videos, on playlist or collection rows. | | `ext.commerce` | `string` | Yes | Shopping or commerce details attached to the post. | | `ext.on_screen_texts` | `string[]` | Yes | Text shown on screen in the video. | | `ext.topic_tag` | `string` | Yes | Threads topic tag. | | `ext.topic_tag_id` | `string` | Yes | Threads topic tag ID. | | `ext.reshare_count` | `integer` | Yes | Reshare count, where reported separately. | | `ext.ad` | `string` | Yes | Facebook Ad Library details, on `/v1/facebook/adlibrary/ad` and `/v1/facebook/adlibrary/company/ads` rows: page\_id, currency, spend, reach\_estimate, publisher\_platforms, categories, targeted\_or\_reached\_countries, cta\_text, cta\_type, link\_url, is\_active, end\_date\_iso, display\_format, title, video\_hd\_url, video\_sd\_url. Absent elsewhere. | | `ext.feedback_id` | `string` | Yes | Facebook feedback ID, used to fetch comments and replies. | | `ext.event` | `string` | Yes | Facebook event details, including `interested_count`, `going_count`, `end_timestamp` and `ticket_url`. | | `ext.trend` | `string` | Yes | TikTok trend-board data. On `/v1/tiktok/hashtags/popular`: `rank`, `country_code`, `period_days`, `posts` and `views` for the period (not lifetime), `industry`, `industry_id`, `industry_label` for the first board read (`null` for the overall board), `boards` listing every board the hashtag ranks on as `{ industry, industry_id, industry_label, rank }`, `popularity_curve` as `{ date, value }` with value 0 to 100 relative to the period's peak, and `top_creators`. On `/v1/tiktok/videos/popular`: `rank`, `country_code`, `period_days`, `order_by`, `period_views`, `organic_views`, `engagement_rate`, `six_second_view_through_rate`, `content_tags`. Absent elsewhere. | | `ext.updated_at` | `string` | Yes | When the post was last edited, where shown. | | `ext.score` | `integer` | Yes | Reddit only. A post score below zero. A negative score is not a like count, so `engagement.likes` is `null` and the score is kept here. Absent when the score is zero or above. | ## Which platforms fill which fields [#which-platforms-fill-which-fields] Every platform returns this shape. `id` and `flags.deleted` are never `null`. The table covers the nine platforms the API launched with. The others return the same shape, and each response's [`unavailable`](/docs/schema/post#unavailable) list names the fields it could not fill. How to read the table: **yes** marks a field the platform is able to supply, though any single row can still have it `null`. An empty cell marks a field that platform never supplies, so expect `null` every time.
| Platform | `author.verified` | `engagement.saves` | `engagement.shares` | `content.duration_seconds` | `engagement.views` | `author.avatar_url` | `engagement.comments` | `content.media_urls` | `url` | `engagement.likes` | `content.thumbnail_url` | `flags.pinned` | | --------- | ----------------- | ------------------ | ------------------- | -------------------------- | ------------------ | ------------------- | --------------------- | -------------------- | ----- | ------------------ | ----------------------- | -------------- | | Facebook | yes | | yes | yes | yes | yes | yes | yes | yes | yes | yes | | | Instagram | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | | | LinkedIn | | | yes | yes | | yes | yes | yes | yes | yes | yes | | | Pinterest | yes | yes | yes | yes | | yes | yes | yes | yes | yes | yes | | | Reddit | | | | yes | | yes | yes | yes | yes | yes | yes | yes | | Threads | yes | | yes | | yes | yes | yes | yes | yes | yes | yes | | | TikTok | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | | Twitter/X | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | | | YouTube | | | | yes | yes | yes | yes | yes | yes | yes | yes | |
Instagram saves come only from `/v1/instagram/post/stats` with `include=saves`. See [Metric substitutions](/docs/metric-substitutions) for the full list of per-platform nulls. The three fields schema 2 added are filled where the platform gives them: * **`author.id`**: Facebook, Instagram list endpoints, LinkedIn, TikTok, YouTube (the channel ID) and Reddit, where it is the account's `t2_…` ID with the prefix kept, the same value comments and profiles carry. On Threads, Twitter/X and Pinterest the source usually gives none, so it is `null` and the response lists it in `unavailable`. * **`kind`**: read from the platform's own media or content type where it sends one (Instagram, YouTube), and otherwise only where the format is certain: a reel or Shorts URL, a TikTok video, a LinkedIn article. `null` otherwise. * **`language`**: only where the platform tags it, such as YouTube, Reddit and Twitter/X. ### `unavailable` [#unavailable] When a field the platform normally fills is `null` on every post in a response, the response lists its path in `unavailable`, for example `"items[].post.author.id"`. One filled row is enough to keep a field off the list. Fields a platform never shows publicly are never listed, so you are not told on every call: | Platform | Never listed in `unavailable` | | --------- | ---------------------------------------------------------------------------------- | | Instagram | `engagement.shares`, `engagement.saves` | | Facebook | `author.username`, `engagement.saves` | | Threads | `engagement.saves` | | YouTube | `engagement.shares`, `engagement.saves` | | Reddit | `engagement.views`, `engagement.shares`, `engagement.saves`, `author.display_name` | | LinkedIn | `engagement.views`, `engagement.saves` | | Pinterest | `engagement.views` | Instagram shares and saves are on the list because only the endpoints named above fill them; a plain Instagram post leaves them `null` without that being a gap. TikTok and Twitter/X have nothing on the list. `kind`, `content.*` and `language` are never listed on any platform, because `null` there usually describes the post (a photo has no duration) rather than a missing field. More on this in [Data availability](/docs/data-availability). ## Returned by [#returned-by] Endpoints that return a post or a list of posts use this shape on: [Facebook](/docs/platforms/facebook) · [Instagram](/docs/platforms/instagram) · [LinkedIn](/docs/platforms/linkedin) · [Pinterest](/docs/platforms/pinterest) · [Reddit](/docs/platforms/reddit) · [Threads](/docs/platforms/threads) · [TikTok](/docs/platforms/tiktok) · [Twitter/X](/docs/platforms/twitter) · [YouTube](/docs/platforms/youtube) To see which native platform field feeds each unified field, read [Field equivalence](/docs/schema/field-equivalence). --- # Transcript (/docs/schema/transcript) The schema-2 transcript object, one shape on every platform, and which endpoints return it. Endpoints that turn the speech in a video or audio post into text return one shape on every platform, at `data.transcript`. This page describes schema 2, which every key gets from October 4, 2026; before it, the transcript was a string on some platforms and a list on others. See [Schema 2](/docs/schema-2). ## Example [#example] `/v1/youtube/video/transcript`, shortened: ```json title="Response" { "success": true, "platform": "youtube", "endpoint": "/v1/youtube/video/transcript", "schema_version": "2", "data": { "transcript": { "language": "en", "text": "[♪♪♪] ♪ We're no strangers to love ♪ ♪ You know the rules and so do I ♪ …", "segments": [ { "text": "[♪♪♪]", "start_seconds": 1.36, "duration_seconds": 1.68 }, { "text": "♪ We're no strangers to love ♪", "start_seconds": 18.64, "duration_seconds": 3.24 } ] } }, "unavailable": [], "credits_used": 30, "credits_remaining": 9970, "request_id": "req_5cace944c435", "cached": false, "idempotent_replay": false, "charge_reason": "miss", "free_call": false } ``` ## Fields [#fields] | Field | Type | Nullable | Description | | ---------------------------------------- | ---------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `transcript.language` | `string` | Yes | The transcript's language as the platform states it, such as `en`. `null` when it gives none. | | `transcript.text` | `string` | Yes | The whole transcript as one string. Where the platform sends timed cues, this is their text joined with spaces. `null` when there is no transcript text; the response then lists `transcript.text` in `unavailable`. | | `transcript.segments` | `object[]` | Yes | Timed cues, when the platform has them (YouTube). `null` when the platform sends the transcript as plain text only. | | `transcript.segments[].text` | `string` | No | The text of one cue. | | `transcript.segments[].start_seconds` | `number` | Yes | When the cue starts, in seconds from the start of the video. | | `transcript.segments[].duration_seconds` | `number` | Yes | How long the cue lasts, in seconds. | Read `transcript.text` for the words. Read `segments` only when you need timing, and check it for `null` first. ## Where it is used [#where-it-is-used] Seven platforms return this shape, on eight endpoints. | Platform | Endpoints | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Facebook | [`/v1/facebook/adlibrary/ad/transcript`](/docs/api-reference/facebook/adlibrary-ad-transcript), [`/v1/facebook/post/transcript`](/docs/api-reference/facebook/post-transcript) | | Instagram | [`/v1/instagram/media/transcript`](/docs/api-reference/instagram/media-transcript) | | LinkedIn | [`/v1/linkedin/post/transcript`](/docs/api-reference/linkedin/post-transcript) | | Reddit | [`/v1/reddit/post/transcript`](/docs/api-reference/reddit/post-transcript) | | TikTok | [`/v1/tiktok/post/transcript`](/docs/api-reference/tiktok/post-transcript) | | Twitter/X | [`/v1/twitter/tweet/transcript`](/docs/api-reference/twitter/tweet-transcript) | | YouTube | [`/v1/youtube/video/transcript`](/docs/api-reference/youtube/video-transcript) | Prices differ per endpoint; see [Endpoint pricing](/docs/endpoint-pricing). A worked example is in [Video transcription](/docs/recipes/video-transcription-api). --- # Profile (/docs/api-reference/bluesky/profile) `GET https://api.insightsocial.app/v1/bluesky/profile` Returns a Bluesky user's public profile: handle, display name, avatar, description, follower and follows counts, post count, created date, and verification status. Use it when you have a handle and want the profile only; use user/posts for that account's posts. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Bluesky handle (e.g. `espn.com`). | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # User › Posts (/docs/api-reference/bluesky/user-posts) `GET https://api.insightsocial.app/v1/bluesky/user/posts` Returns a feed of posts for a Bluesky user: text, author info, embeds, reply/repost/like/quote counts, and when each was indexed. Use it when you already have a handle or did and want that account's own posts; use search to find posts by keyword instead. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | no | Bluesky handle. Provide at least one of: `handle`, `user_id`. | | `user_id` | string | no | Bluesky `did` (Bluesky's internal user ID format, e.g. `did:plc:x7d6j54pm22ufehkes6jo4jf`). Provide at least one of: `handle`, `user_id`. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post (/docs/api-reference/bluesky/post) `GET https://api.insightsocial.app/v1/bluesky/post` Returns one Bluesky post: its record text, author info, embeds, reply count, repost count, like count, quote count, and a threaded replies array. Use it when you have a post URL; use user/posts to list everything one account has posted. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Bluesky post URL. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search (/docs/api-reference/bluesky/search) `GET https://api.insightsocial.app/v1/bluesky/search` Returns Bluesky posts matching a keyword, up to 100 per page, each with its text, author, like/comment/share counts, and when it was written. Use it to find posts by keyword across all of Bluesky; use user/posts when you already know the account. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search terms matched against post text. | | `sort` | string | no | `latest` (newest first, the default) or `top` (Bluesky's own top ranking, which favours popular posts over recent ones). One of: latest, top. | | `since` | string | no | Only posts written at or after this time. ISO 8601 date (`2026-09-01`) or datetime with a timezone (`2026-09-01T00:00:00Z`); any other shape is rejected with a free 400. | | `until` | string | no | Only posts written before this time. Same format as `since`. Set it to the oldest `published_at` of your last call to fetch the next older batch; the boundary is compared to the second, so a post from that same second can repeat, and deduplicating on `post.id` removes it. | | `lang` | string | no | Only posts tagged with this language, as a code such as `en`, `ja` or `ko`. | | `author` | string | no | Only posts by this account: a handle such as `espn.com` or a `did` such as `did:plc:...`. An account that does not exist returns an empty list. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search (/docs/api-reference/douyin/search) `GET https://api.insightsocial.app/v1/douyin/search` Returns Douyin videos matching a keyword: caption, creator, likes, comments, shares, saves, hashtags, music, cover image, and duration. Use it to find videos by keyword; use search/users to find creators instead. **Metered: 50–1250 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keywords, for example 美食 or coffee. | | `limit` | integer | no | Rows to return, 1 to 25. Default 10. | | `sort` | string | no | Result ordering: relevance, most_liked, or latest. Default relevance. One of: relevance, most_liked, latest. | | `published` | string | no | Recency window: all, day, week, or half_year. Default all. One of: all, day, week, half_year. | | `duration` | string | no | Length filter: all, under_1m, one_to_five_m, or over_5m. Default all. One of: all, under_1m, one_to_five_m, over_5m. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile (/docs/api-reference/douyin/profile) `GET https://api.insightsocial.app/v1/douyin/profile` Returns a Douyin creator's profile: nickname, Douyin ID, bio, avatar, follower and following counts, total likes received, video count, region, and verification. Use it to check a creator's follower count cheaply; use profile/posts for their videos. **60 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | no | Douyin sec_uid, the MS4wLjAB... string in a profile URL, or the numeric user ID. Provide at least one of: `handle`, `url`, `id`. | | `url` | string | no | Full Douyin profile URL, for example https://www.douyin.com/user/MS4wLjABAAAA... Provide at least one of: `handle`, `url`, `id`. | | `id` | string | no | Numeric Douyin user ID. Provide at least one of: `handle`, `url`, `id`. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Posts (/docs/api-reference/douyin/profile-posts) `GET https://api.insightsocial.app/v1/douyin/profile/posts` Returns a Douyin creator's recent videos, newest first: caption, likes, comments, shares, saves, hashtags, music, and cover image. Use it to pull a creator's recent videos; use profile when you only need their follower count. **Metered: 50–1250 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | no | Douyin sec_uid, the MS4wLjAB... string in a profile URL, or the numeric user ID. Provide at least one of: `handle`, `url`, `id`. | | `url` | string | no | Full Douyin profile URL. Provide at least one of: `handle`, `url`, `id`. | | `id` | string | no | Numeric Douyin user ID. Provide at least one of: `handle`, `url`, `id`. | | `limit` | integer | no | Rows to return, 1 to 25. Default 10. | | `recent_days` | integer | no | Only return videos published in the last N days. Leave unset for the newest regardless of age. | | `exclude_pinned` | boolean | no | Set true to skip videos pinned to the top of the profile, which can be old. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post (/docs/api-reference/douyin/post) `GET https://api.insightsocial.app/v1/douyin/post` Returns one Douyin video: caption, creator, likes, comments, shares, saves, hashtags, music, duration, dimensions, cover image, and tagged place when set. Use it when you have a video URL or aweme ID and need the full record; search returns only a summary row. **100 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Douyin video URL, for example https://www.douyin.com/video/7681266289940544369, or the bare numeric aweme ID. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post › Comments (/docs/api-reference/douyin/post-comments) `GET https://api.insightsocial.app/v1/douyin/post/comments` Returns top-level comments on a Douyin video: text, commenter, like count, reply count, IP region, and whether the creator liked it. Use it to read a video's top-level comments; use comment/replies for the thread under one of them. **Metered: 20–1000 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Douyin video URL or the bare numeric aweme ID. | | `limit` | integer | no | Comments to return, 1 to 100. Default 20. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Comment › Replies (/docs/api-reference/douyin/comment-replies) `GET https://api.insightsocial.app/v1/douyin/comment/replies` Returns the reply thread under one Douyin comment: text, replier, like count, IP region, and verification, each tagged with the parent comment id. Use it after post/comments, passing a comment id whose reply count is above zero. **Metered: 50–2500 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Douyin video URL or the bare numeric aweme ID the comment sits under. | | `comment_id` | string | yes | The comment to expand, taken from the id field of a GET /v1/douyin/post/comments row. Only comments with a non-zero engagement.replies have a thread. | | `limit` | integer | no | Replies to return, 1 to 50. Default 10. | | `cursor` | string | no | Continuation token from a previous response's pagination block. Omit for the first page. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Users (/docs/api-reference/douyin/search-users) `GET https://api.insightsocial.app/v1/douyin/search/users` Returns Douyin creators matching a keyword: nickname, Douyin ID, bio, follower count, total likes received, avatar, profile URL, and verification label. Use it to shortlist creators by keyword, follower band, or account type; use search to find videos instead. **Metered: 50–1250 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keywords, for example 美食 or a brand name. | | `limit` | integer | no | Rows to return, 1 to 25. Default 10. | | `cursor` | string | no | Continuation token from a previous response's pagination block. Omit for the first page. | | `followers` | string | no | Follower band: all, under_1k, 1k_to_10k, 10k_to_100k, 100k_to_1m, or over_1m. Default all. One of: all, under_1k, 1k_to_10k, 10k_to_100k, 100k_to_1m, over_1m. | | `user_type` | string | no | Account type: all, regular_user, enterprise_verified, or individual_verified. Default all. One of: all, regular_user, enterprise_verified, individual_verified. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Trending (/docs/api-reference/douyin/trending) `GET https://api.insightsocial.app/v1/douyin/trending` Returns the live Douyin hot-search board: roughly 50 trending topics with rank, hot value, view count, video count, and the time each topic broke. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to see what is trending across Douyin right now; there is no keyword filter, the whole board comes back in one call. **250 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile (/docs/api-reference/facebook/profile) `GET https://api.insightsocial.app/v1/facebook/profile` Returns a Facebook page's public profile: page id, display name, profile image, bio, follower count, and page-like count. Use it when you have a page URL and want a quick snapshot before pulling that page's posts, photos, or reels. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the Facebook page or profile | | `get_business_hours` | string | no | Get the business's hours | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Posts (/docs/api-reference/facebook/profile-posts) `GET https://api.insightsocial.app/v1/facebook/profile/posts` Returns recent posts from a Facebook page or profile, each with the post text, like and comment counts, the per-reaction breakdown, media, and publish time. Share counts are null on a plain call. Use it to pull a page's recent content by url or pageId. The optional include=engagement fills each row's share count, and a reel's exact views and duration, in the same call. **Metered: 10–40 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | no | Full URL of the Facebook page or profile to fetch posts for. Provide at least one of: `url`, `pageId`. | | `pageId` | string | no | Facebook profile page id. Provide at least one of: `url`, `pageId`. | | `since` | string | no | Only posts published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Older posts are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Pinned posts sit out of date order and never end the walk. The page still costs what a page costs, so a daily poll pays for the pages it walks and no more. | | `stop_at_id` | string | no | The id (`post.id`) or URL (`post.url`) of the newest post you already hold. The page stops just before it: that post and everything after it are left off, `next_cursor` is not returned, and `pagination.stopped_at` is `known_id`. A pinned post never counts as the stop point. If it is not on this page the page is returned in full with its cursor, so keep walking. `pagination.stopped_at` is `end` when the list ran out first and `null` while there is more to walk. | | `cursor` | string | no | To paginate through the posts | | `include` | string | no | Set to `engagement` (one token only) to fill `post.engagement.shares` on every row, and `post.engagement.views` and `post.content.duration_seconds` on reels, in this one call. A row that links to an event rather than a post is not looked up. Without it the call is unchanged. One of: engagement. | | `recent_days` | integer | no | Keep only posts published in the last N days (1 to 3650) and stop pagination once a page reaches older posts. Does not change the price of a page. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post (/docs/api-reference/facebook/post) `GET https://api.insightsocial.app/v1/facebook/post` Returns one Facebook post in full: the post text, like, comment and share counts, a reactions breakdown, media attachments, and author info. Use it when you have a single post URL and need more than profile/posts gives, such as the reactions breakdown. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the Facebook post | | `get_comments` | boolean | no | Accepted for backwards compatibility and currently a NO-OP: this endpoint returns the same body with or without it. | | `get_transcript` | boolean | no | Accepted for backwards compatibility and currently a NO-OP: this endpoint returns the same body with or without it. Use `/v1/facebook/post/transcript` for a video transcript. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post › Comments (/docs/api-reference/facebook/post-comments) `GET https://api.insightsocial.app/v1/facebook/post/comments` Returns comments on a Facebook post, each with the commenter's name, comment text, like count, reply count, and creation time. Use it to read a post's discussion; it also returns the feedback_id and expansion_token that post/comment/replies needs. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | no | Full URL of the Facebook post to fetch comments for. Provide at least one of: `url`, `feedback_id`. | | `feedback_id` | string | no | Using feedback_id (instead of url) will *really* speed up the request. You can get the feedback_id when you make a request to /v1/facebook/post. Provide at least one of: `url`, `feedback_id`. | | `cursor` | string | no | Cursor to get more comments. Get 'cursor' from previous response. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | --- # Group › Posts (/docs/api-reference/facebook/group-posts) `GET https://api.insightsocial.app/v1/facebook/group/posts` Returns posts from a Facebook group, 3 to 4 per page, each with the post text, reaction count, comment count, the per-reaction breakdown, and author info. Use it for group content, since profile/posts covers pages only. Facebook serves a group 3 to 4 posts per page, so a long history is many pages. Each page is billed as one call: quote a long walk with dry_run first. Send next_cursor back as cursor for older posts; each post's url works as the input to post and post/comments. Share counts are not on this surface; read one with post. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | no | Full URL of the Facebook group. Provide at least one of: `url`, `group_id`. | | `group_id` | string | no | The ID of the group. Provide at least one of: `url`, `group_id`. | | `sort_by` | string | no | How to sort the posts. CHRONOLOGICAL_LISTINGS reads the group's listings feed and can stall on some groups (a group with no listings feed does not answer); use the default sort, or CHRONOLOGICAL for posts in date order. One of: TOP_POSTS, RECENT_ACTIVITY, CHRONOLOGICAL, CHRONOLOGICAL_LISTINGS. | | `cursor` | string | no | Cursor to get the next page of posts. Take it from `pagination.next_cursor` on the previous response. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Group (/docs/api-reference/facebook/group) `GET https://api.insightsocial.app/v1/facebook/group` Returns a Facebook group's public record: name, description, member count, privacy, visibility, and creation date. Use it to identify a group by URL or group_id before calling group/posts for the recent posts. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | no | Full URL of the Facebook group. Provide at least one of: `url`, `group_id`. | | `group_id` | string | no | Numeric Facebook group id. Provide at least one of: `url`, `group_id`. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post › Transcript (/docs/api-reference/facebook/post-transcript) `GET https://api.insightsocial.app/v1/facebook/post/transcript` Returns the spoken words of a Facebook video post as a transcript, using Facebook's auto-generated captions. Use it when you need what was said in a video; post returns the caption text and engagement but not the speech. **100 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the Facebook video post | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Photos (/docs/api-reference/facebook/profile-photos) `GET https://api.insightsocial.app/v1/facebook/profile/photos` Returns a page or profile's photos, each with a photo id, permalink, full-size image URL, thumbnail, and accessibility caption when available. Use it for a page's photo gallery. The plain row carries no author, engagement or publish time, so add the optional include=details to fill them from each photo's own post in the same call. **Metered: 10–90 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the Facebook page or profile | | `include` | string | no | Set to `details` (one token only) to fill `post.author.display_name`, `post.author.avatar_url`, `author.id`, `post.engagement.likes`, `.comments` and `.shares`, and `post.published_at` on every photo in this one call. A photo inside a multi-photo post carries its own counts, not the post's. Without it the call is unchanged. One of: details. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Reels (/docs/api-reference/facebook/profile-reels) `GET https://api.insightsocial.app/v1/facebook/profile/reels` Returns a page or profile's reels, each with a reel id, description, thumbnail, permalink, publish time, and a rounded public view count. Use it for a cheap reel listing; when you need exact views plus likes, comments, and shares, use profile/reels/full instead. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the Facebook page or profile | | `recent_days` | integer | no | Keep only reels published in the last N days (1 to 3650) and stop pagination once a page reaches older reels. Does not change the price of a page. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Adlibrary › Ad (/docs/api-reference/facebook/adlibrary-ad) `GET https://api.insightsocial.app/v1/facebook/adlibrary/ad` Returns one ad from the Facebook Ad Library: creative, CTA and link, run dates, display format, and the advertiser's page URL, likes and categories. Use it when you have an ad id or URL. Spend and reach are political-ads-only, and Facebook publishes no per-ad impression count. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | no | Facebook Ad Library ad ID. Ad ids rotate as campaigns end, so take a current one from `/v1/facebook/adlibrary/company/ads` or `/v1/facebook/adlibrary/search/ads` rather than reusing an old one. Provide at least one of: `id`, `url`. | | `url` | string | no | Facebook Ad Library URL for the ad (`https://www.facebook.com/ads/library?id=...`). A page URL is rejected with a 400. Provide at least one of: `id`, `url`. | | `trim` | boolean | no | Set to true for a trimmed down version of the response | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Adlibrary › Company › Ads (/docs/api-reference/facebook/adlibrary-company-ads) `GET https://api.insightsocial.app/v1/facebook/adlibrary/company/ads` Returns the ads one company or page is running in the Facebook Ad Library, each with its creative, status, spend, and targeting info. Use it when you know the advertiser by pageId or company name; to find ads by keyword across advertisers, use adlibrary/search/ads. **Metered: 50–350 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `pageId` | string | no | Facebook page ID of the advertiser. Provide at least one of: `pageId`, `companyName`. | | `companyName` | string | no | The name of the company. Can either use this or pageId. Provide at least one of: `pageId`, `companyName`. | | `country` | string | no | This can only be one country. It has to be the 2 letter code for the country. It defaults to ALL. | | `status` | string | no | Status of the ad. Defaults to ACTIVE. One of: ALL, ACTIVE, INACTIVE. | | `media_type` | string | no | Media type of the ad. Defaults to ALL. Meme refers to ads with image and text. Not sure why they call it meme. One of: ALL, IMAGE, VIDEO, MEME, IMAGE_AND_MEME, NONE. | | `language` | string | no | Language to filter ads on. Needs to be 2 letter language code, ie EN, ES, FR, etc | | `sort_by` | string | no | Sort by impressions (high to low), or Most Recent (relevancy_monthly_grouped). Defaults to impressions. One of: total_impressions, relevancy_monthly_grouped. | | `start_date` | string | no | Start date to search for. Format: YYYY-MM-DD | | `end_date` | string | no | End date to search for. Format: YYYY-MM-DD | | `cursor` | string | no | Cursor to paginate through results | | `trim` | boolean | no | Set to true for a trimmed down version of the response | | `include` | string | no | Set to `audience` (one token only) to fill, on every ad that ran in the EU, `post.ext.ad.eu_total_reach`, `post.ext.ad.eu_reach_breakdown` (reach per EU country by age band and gender), `post.ext.ad.targets_eu`, `target_ages`, `target_gender` and `target_locations`. Without it the call is unchanged. One of: audience. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Adlibrary › Search › Ads (/docs/api-reference/facebook/adlibrary-search-ads) `GET https://api.insightsocial.app/v1/facebook/adlibrary/search/ads` Returns Facebook Ad Library ads matching a keyword, each with its creative text, images, sponsor info, and running status. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to find ads across all advertisers by keyword; if you already know the advertiser, adlibrary/company/ads is more direct. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keyword or phrase to find ads in the Facebook Ad Library | | `sort_by` | string | no | Sort by impressions (high to low), or Most Recent (relevancy_monthly_grouped). Defaults to impressions. One of: total_impressions, relevancy_monthly_grouped. | | `search_type` | string | no | If you want to search by exact phrase or not One of: keyword_unordered, keyword_exact_phrase. | | `ad_type` | string | no | Search for all ads or only political and issue ads One of: all, political_and_issue_ads. | | `country` | string | no | This can only be one country. It has to be the 2 letter code for the country. It defaults to ALL. | | `status` | string | no | Status of the ad. Defaults to ACTIVE. One of: ALL, ACTIVE, INACTIVE. | | `media_type` | string | no | Media type of the ad. Defaults to ALL. Meme just means the ad has text and an image. No clue why they call it meme. One of: ALL, IMAGE, VIDEO, MEME, IMAGE_AND_MEME, NONE. | | `start_date` | string | no | Impressions start date. Needs to be in YYYY-MM-DD format. | | `end_date` | string | no | Impressions end date. Needs to be in YYYY-MM-DD format. | | `cursor` | string | no | Cursor to paginate through results | | `trim` | boolean | no | Set to true for a trimmed down version of the response | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Adlibrary › Search › Companies (/docs/api-reference/facebook/adlibrary-search-companies) `GET https://api.insightsocial.app/v1/facebook/adlibrary/search/companies` Returns advertiser pages in the Facebook Ad Library matching a name, each with its page id, name, handle, verification badge and follower count. Use it to find an advertiser's pageId first, then pass that pageId to adlibrary/company/ads, which is also where you count their ads. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keyword or phrase to find companies in the Facebook Ad Library | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Events (/docs/api-reference/facebook/profile-events) `GET https://api.insightsocial.app/v1/facebook/profile/events` Returns a Facebook page's upcoming and past events, each with a title, link, the start time, the venue, the city, the rendered time line, and the cancelled, past and online flags. Use it for one page's own events; events covers a city. The start time sits on published_at and is often in the future. Optional include=details adds the description, hosts, cover and RSVP counts. **Metered: 10–90 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the public Facebook page. | | `cursor` | string | no | Cursor returned by the previous response for pagination. | | `include` | string | no | Set to `details` (one token only) to fill, on every event in this one call, `post.ext.event.description`, `address`, `latitude`, `longitude`, `hosts`, `host_context_text`, `category`, `privacy`, `attendance_count`, `interested_count` and `going_count`, the cover image on `post.content.thumbnail_url`, and the RSVP counts on `post.engagement.views` (interested) and `.likes` (going). Without it the call is unchanged. One of: details. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post › Comment › Replies (/docs/api-reference/facebook/post-comment-replies) `GET https://api.insightsocial.app/v1/facebook/post/comment/replies` Returns the replies under a single Facebook comment, each with the author name, reply text, like count, and creation time. Use it after post/comments: pass that response's feedback_id and expansion_token, which are not the comment id. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `feedback_id` | string | yes | `feedback_id` from `/v1/facebook/post/comments` for the parent comment (this is NOT the comment ID). | | `expansion_token` | string | yes | `expansion_token` from `/v1/facebook/post/comments` for the parent comment. | | `cursor` | string | no | Cursor returned by the previous response for pagination. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | --- # Marketplace › Location › Search (/docs/api-reference/facebook/marketplace-location-search) `GET https://api.insightsocial.app/v1/facebook/marketplace/location/search` Returns Facebook Marketplace locations and cities matching a search term, each with its lat and lng coordinates. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it first to turn a place name into coordinates, then pass those to marketplace/search. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Location search query (city or area name). | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Marketplace › Search (/docs/api-reference/facebook/marketplace-search) `GET https://api.insightsocial.app/v1/facebook/marketplace/search` Returns Facebook Marketplace listings near a lat/lng, each with title, price, location, photo, delivery types, and sold or live flags. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to find listings in an area; get lat and lng from marketplace/location/search, and de-duplicate by listing id since ordering can shift. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keyword (e.g., `bike`, `couch`). | | `lat` | string | yes | Latitude of the search location. | | `lng` | string | yes | Longitude of the search location. | | `radius_km` | integer | no | Search radius in kilometers. | | `min_price` | integer | no | Minimum listing price. | | `max_price` | integer | no | Maximum listing price. | | `count` | integer | no | Number of listings to return per page. | | `sort_by` | string | no | Sort order for results. One of: suggested, distance_ascend, creation_time_descend, price_ascend, price_descend. | | `delivery_method` | string | no | Delivery filter: local pickup only, shipping only, or all. One of: all, local_pickup, shipping. | | `condition` | string | no | Filter listings by item condition. One of: new, used_like_new, used_good, used_fair. | | `date_listed` | string | no | Filter by listing recency (relative window or numeric day count). One of: all, 1, 7, 30, last_24_hours, last_7_days, last_30_days. | | `availability` | string | no | Filter by listing status: `available`, `sold`, or `all`. One of: available, sold, all. | | `cursor` | string | no | Opaque pagination cursor returned by the previous response: forward as-is. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Marketplace › Item (/docs/api-reference/facebook/marketplace-item) `GET https://api.insightsocial.app/v1/facebook/marketplace/item` Returns one Facebook Marketplace listing: title, description, price, location, condition, photos, seller, and availability flags. Use it after marketplace/search when you need one listing in full, by numeric id or Marketplace URL. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | no | Facebook Marketplace item ID (numeric). Provide at least one of: `id`, `url`. | | `url` | string | no | Full URL of the Marketplace item. Provide at least one of: `id`, `url`. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Events › Search (/docs/api-reference/facebook/events-search) `GET https://api.insightsocial.app/v1/facebook/events/search` Returns public Facebook events matching a keyword, each with name, date sentence, venue, cover photo, interested and going counts, and online and past flags. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to find events by topic anywhere; filter on is_past yourself, since the directory also returns events that have already happened. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Event name or keyword to search for. | | `cursor` | string | no | Cursor returned by the previous response for pagination. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Events (/docs/api-reference/facebook/events) `GET https://api.insightsocial.app/v1/facebook/events` Returns the events listed on a Facebook city or region events page, each with a title, link, cover image, the start time, the venue, and the going and interested counts. Use it to see what is on in a place, optionally narrowed by a time filter; use profile/events for one page's own events. The optional include=details adds the description, address and hosts. **Metered: 10–130 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the city's Facebook Events page. | | `time` | string | no | Relative time window. Defaults to all time when omitted. One of: today, this_week, next_week. | | `cursor` | string | no | Cursor returned by the previous response for pagination. | | `include` | string | no | Set to `details` (one token only) to fill, on every event in this one call, `post.ext.event.description`, `address`, `city`, `latitude`, `longitude`, `hosts`, `host_context_text`, `category`, `privacy`, `is_canceled` and `attendance_count`, and the host credit on `post.author.display_name`. Without it the call is unchanged. One of: details. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Event › Details (/docs/api-reference/facebook/event-details) `GET https://api.insightsocial.app/v1/facebook/event/details` Returns one Facebook event in full: title, description, start and end time, location, host, cover image, and RSVP counts when shown. Use it after events, events/search, or profile/events when a listing row is not enough, by event id or URL. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | no | Facebook event ID (numeric). Provide at least one of: `id`, `url`. | | `url` | string | no | Full URL of the event. Provide at least one of: `id`, `url`. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Adlibrary › Ad › Transcript (/docs/api-reference/facebook/adlibrary-ad-transcript) `GET https://api.insightsocial.app/v1/facebook/adlibrary/ad/transcript` Returns the spoken words of a Facebook Ad Library video ad, from Facebook's captions when available or transcribed from the video itself. Use it when you need what an ad says out loud; adlibrary/ad returns the creative and spend but not the speech. **100 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | no | Facebook Ad Library ad ID. Ad ids rotate as campaigns end, so take a current one from `/v1/facebook/adlibrary/company/ads` rather than reusing an old one. Provide at least one of: `id`, `url`. | | `url` | string | no | Facebook Ad Library URL for the ad. A page URL is rejected with a 400. Provide at least one of: `id`, `url`. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Full (/docs/api-reference/facebook/profile-full) `GET https://api.insightsocial.app/v1/facebook/profile/full` Returns a page's profile plus its latest 3 posts in one call. Use it instead of calling profile and profile/posts yourself; the profile still comes back even if the posts part fails. **Beta.** A composite: `data.profile` is a schema-2 Profile and `data.posts[].post` are schema-2 Posts, and the posts page with `cursor`. The rest of `data` is the source's own structure under our naming rules (snake_case keys, string ids). Fields outside the profile and posts may change. See [Schema 2](/docs/schema-2). **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full Facebook profile or page URL. One of the identity params is required. | | `posts` | integer | no | How many of the fetched recent posts to return and compute the metrics over (1-100, default 25). | | `cursor` | string | no | Pass a prior response's posts_cursor to read the next page of posts. | | `include` | string | no | | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Reels › Full (/docs/api-reference/facebook/profile-reels-full) `GET https://api.insightsocial.app/v1/facebook/profile/reels/full` Returns a page's reels with exact per-reel engagement merged in: views, likes, comments, and shares, plus each reel's id, thumbnail, and link. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it instead of profile/reels when the numbers matter, since that list carries only a rounded view count and no likes or comments. **Metered: 50–250 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the Facebook page or profile | | `cursor` | string | no | Cursor from a prior response's next_cursor to page deeper. | | `limit` | integer | no | Return up to this many reels in one call (1-50). The endpoint pages the underlying list server-side until it has collected this many (or runs out), and bills per page read. Omit for a single page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Posts (/docs/api-reference/facebook/search-posts) `GET https://api.insightsocial.app/v1/facebook/search/posts` Returns public Facebook posts matching a keyword, each with text, permalink, publish time, like count, comment count, and author. Use it when you have a phrase, not a page URL. recent_posts=true ranks by recency; start_date and end_date bound the window. **Metered: 10–50 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Keyword or phrase to search for. | | `cursor` | string | no | Cursor from the previous response. | | `start_date` | string | no | Include posts on or after this date (YYYY-MM-DD). | | `end_date` | string | no | Include posts on or before this date (YYYY-MM-DD). | | `recent_posts` | boolean | no | When true, rank by recency instead of relevance. | | `location_uid` | string | no | Facebook location id to scope the search. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `max_age_days` | integer | no | Keep only rows published within this many days (`post.published_at`), 1 to 3650. A row with no date is discarded. | | `max_pages` | integer | no | 1 to 5 (default 1). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed as one call to this endpoint; the whole walk counts once against your rate limit. | --- # Search › Pages (/docs/api-reference/facebook/search-pages) `GET https://api.insightsocial.app/v1/facebook/search/pages` Returns Facebook pages matching a keyword, each with page id, display name, permalink, avatar, and verified flag. Use it to find pages by name; follower counts are not on this card, so take author.url to profile for the full page. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Keyword or phrase to search for. | | `cursor` | string | no | Cursor from the previous response. | | `location_uid` | string | no | Facebook location id to scope the search. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › People (/docs/api-reference/facebook/search-people) `GET https://api.insightsocial.app/v1/facebook/search/people` Returns Facebook people matching a keyword, each with profile id, display name, permalink, avatar, and verified flag. Use it to find people by name. The id is often a pfbid token, not a numeric user id, and follower counts are not on this card. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Name or phrase to search for. | | `cursor` | string | no | Cursor from the previous response. | | `location_uid` | string | no | Facebook location id to scope the search to a city. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Videos (/docs/api-reference/facebook/search-videos) `GET https://api.insightsocial.app/v1/facebook/search/videos` Returns public Facebook videos matching a keyword, each with video id, caption, permalink, thumbnail, and author. Use it to find videos by phrase. View counts on this card are a display string and are not parsed into engagement.views. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Keyword or phrase to search for. | | `cursor` | string | no | Cursor from the previous response. | | `start_date` | string | no | Include videos on or after this date (YYYY-MM-DD). | | `end_date` | string | no | Include videos on or before this date (YYYY-MM-DD). | | `recent_videos` | boolean | no | When true, rank by recency instead of relevance. | | `location_uid` | string | no | Facebook location id to scope the search. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Groups (/docs/api-reference/facebook/search-groups) `GET https://api.insightsocial.app/v1/facebook/search/groups` Returns public Facebook groups matching your keywords, one row per group, with the group's name, id and URL in the same shape the group endpoints take. Use it when you need to find the groups to read before pulling their posts. Add include=details for member counts, descriptions and privacy. **Metered: 10–110 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Keywords describing the groups to find, for example `vintage cars`. Keywords only: the search is already limited to public Facebook groups, so a `site:` operator is refused before billing. | | `page` | integer | no | Page number, starting at 1. Forwarding `pagination.next_cursor` as `cursor` does the same. | | `include` | string | no | Set to `details` (one token only) to fill, on every group in this one call, `author.followers` (members), `author.bio` (the description), `author.joined_at`, `author.ext.group.privacy_label`, `visibility_label`, `activity` and `id` (the numeric group id), and the name where it was null. Without it the call is unchanged. One of: details. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | --- # Profile (/docs/api-reference/instagram/profile) `GET https://api.insightsocial.app/v1/instagram/profile` Returns an Instagram account's public profile: bio, exact integer follower count on author.followers, following count, picture URL, verification, and author.ext.public_email from the bio. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Instagram username without the @ symbol | | `trim` | boolean | no | Set to true to get a trimmed response | | `contact_email` | string | no | Optional. When 1, also returns author.ext.contact_email: when the bio lists two or more email addresses, the one the creator gives for contacting them personally, copied verbatim from the bio. Null when there is one address or none, when every address belongs to a manager, agency or brand, or when the choice was not confident. author.ext.public_email is unchanged. No extra credits. One of: 1. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › About (/docs/api-reference/instagram/profile-about) `GET https://api.insightsocial.app/v1/instagram/profile/about` Returns Instagram's "About this account" details for a public account (country, month joined, published contact) plus its follower count and lifetime post count. Use it when you need an account's total post count, or to tell where a creator is based, for example to include or exclude a region. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Instagram username without the @ symbol | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Posts (/docs/api-reference/instagram/profile-posts) `GET https://api.insightsocial.app/v1/instagram/profile/posts` Returns a user's recent posts with caption, like and comment counts, media URL, media type, and timestamp; share counts are not included. Collab posts list every co-author under ext.coauthors. Use it for a plain feed pull; profile/posts/full adds per-post share counts. On a collab post the author is whoever created it, so read ext.coauthors for everyone on it. **Metered: 10–130 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Instagram username without the @ symbol | | `since` | string | no | Only posts published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Older posts are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Pinned posts sit out of date order and never end the walk. The page still costs what a page costs, so a daily poll pays for the pages it walks and no more. | | `stop_at_id` | string | no | The id (`post.id`) or URL (`post.url`) of the newest post you already hold. The page stops just before it: that post and everything after it are left off, `next_cursor` is not returned, and `pagination.stopped_at` is `known_id`. A pinned post never counts as the stop point. If it is not on this page the page is returned in full with its cursor, so keep walking. `pagination.stopped_at` is `end` when the list ran out first and `null` while there is more to walk. | | `trim` | boolean | no | Set to true to request the lighter record. That record is the narrow width described above, so the page is normally served from the second source with every field. A trimmed page served as it is omits `post.ext.coauthors` and leaves `post.flags.pinned` null. The price is the same either way. | | `exclude_pinned` | boolean | no | Set to true to leave out the posts the owner pinned to the top of the profile. A row whose pin state is unknown, such as a narrow or trimmed row, is kept. | | `recent_days` | integer | no | Keep only posts published in the last N days (1 to 3650). Older posts are removed from the page, pinned ones included, and once a page reaches posts older than the window `next_cursor` is not returned, so a walk back to a date window stops on its own. The page still costs what a page costs. | | `include` | string | no | Photos and carousels are never looked up. One of: audio. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | --- # Post (/docs/api-reference/instagram/post) `GET https://api.insightsocial.app/v1/instagram/post` Returns one post's details: caption, view count on videos and reels, like and comment counts, media URLs, media type, the author, and any tagged users. Use it for the standard single post lookup, views included on video; use post/stats only when you also need the share count. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the Instagram post | | `region` | string | no | 2 letter country code to set the proxy in | | `trim` | boolean | no | Set to true to get a trimmed response | | `download_media` | boolean | no | Set to true to also download the video/images and get back durable hosted media URLs under `data.post.ext.download_media_urls` (`[{ post_id, cdn_url, type, cached }]`). Use these for archiving; the raw `media_urls` are short-lived signed CDN links that expire. Adds a few seconds of latency while the media is fetched. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post › Comments (/docs/api-reference/instagram/post-comments) `GET https://api.insightsocial.app/v1/instagram/post/comments` Returns a post's comments, each with its text, author, like count, reply count, and time, ranked by popularity by default or newest first. Use it to read the comment section of one post, paging deeper with the cursor it returns; pass sort=recent for newest first. **Metered: 50–150 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | URL of the Instagram post (a /p/, /reel/, /reels/, or /tv/ link). | | `sort` | string | no | Ordering: `top` (default) ranks by Instagram's popularity order (most-liked first); `recent` returns newest-first. Use `recent` when you are paging through a whole thread: Instagram ranks only the most-liked head, so a `top` walk starts repeating comments once you page past it. One of: top, recent. | | `cursor` | string | no | Pagination cursor. Use the `next_cursor` from the previous response to fetch the next page. | | `safe_url` | boolean | no | When true, returns URL-safe profile picture links suitable for embedding. | | `scan_pages` | integer | no | Optional, 1 to 3, default 1. With 2 or 3, the call reads that many pages, drops comments repeated across pages, and returns them all sorted by likes (sort=top) or newest first (sort=recent). Use it when the first page of a top-sorted thread is low-like comments. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | --- # Comment (/docs/api-reference/instagram/comment) `GET https://api.insightsocial.app/v1/instagram/comment` Returns one comment's current text, like count, reply count, author, and timestamp, plus the post's total comment count for context. Use it when you already know which comment you want, so you do not have to page through post/comments to find it again. **Metered: 50–150 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `comment_url` | string | no | An Instagram comment permalink: `https://www.instagram.com/p/{shortcode}/c/{commentId}/` (or a reply permalink `.../c/{parent}/r/{reply}/`, best-effort). Mutually exclusive with `post_url`+`comment_id`. Provide at least one of: `comment_url`, `post_url`. | | `post_url` | string | no | The post URL (a `/p/`, `/reel/`, `/reels/`, or `/tv/` link). Combine with `comment_id`, or with `author_username`/`text_contains` for a search. Provide at least one of: `comment_url`, `post_url`. | | `comment_id` | string | no | The target comment's numeric id (`pk`). Requires `post_url`. | | `author_username` | string | no | Return up to `max` comments authored by this username (no comment id needed). Mutually exclusive with `text_contains` and any comment id. | | `text_contains` | string | no | Return up to `max` comments whose text contains this snippet (case-insensitive). Mutually exclusive with `author_username` and any comment id. | | `deep_scan` | boolean | no | Widen the scan budget for deeply-buried comments (raises the per-chain page ceiling and deadline). | | `position_hint` | string | no | Opaque token from a prior lookup's `lookup.position_hint`. Passing it back replays just the last-known sort chain first, making a re-check of an already-found comment cheap. | | `max` | integer | no | For `author_username`/`text_contains` search: max matches to return (1-20, default 5). | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Basic Profile (/docs/api-reference/instagram/basic-profile) `GET https://api.insightsocial.app/v1/instagram/basic-profile` Returns a minimal Instagram profile looked up by numeric user id: username, full name, and profile picture. Use it when all you have is a numeric user id and only need the name and avatar; use profile for bio, follower counts, and verification. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `userId` | string | yes | Instagram numeric user ID | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Reels (/docs/api-reference/instagram/profile-reels) `GET https://api.insightsocial.app/v1/instagram/profile/reels` Returns a user's reels with view, like and comment counts and a thumbnail; share counts are not included here. Collab reels list every co-author under ext.coauthors. Use it for reels only rather than the whole post feed; use profile/reels/full when you also need per-reel share counts. **Metered: 10–140 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `user_id` | string | no | Instagram user id. Use this for faster response times. Provide at least one of: `user_id`, `handle`. | | `handle` | string | no | Instagram username without the @ symbol. Provide at least one of: `user_id`, `handle`. | | `since` | string | no | Only reels published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Older reels are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Pinned reels sit out of date order and never end the walk. The page still costs what a page costs, so a daily poll pays for the pages it walks and no more. | | `stop_at_id` | string | no | The id (`post.id`) or URL (`post.url`) of the newest reel you already hold. The page stops just before it: that reel and everything after it are left off, `next_cursor` is not returned, and `pagination.stopped_at` is `known_id`. A pinned reel never counts as the stop point. If it is not on this page the page is returned in full with its cursor, so keep walking. `pagination.stopped_at` is `end` when the list ran out first and `null` while there is more to walk. | | `trim` | boolean | no | Set to true to request the lighter record: a smaller, faster page that keeps the engagement, the author, the caption, the timestamp and the reel URL. What it drops is `post.content.media_urls` (the video file), `post.content.duration_seconds`, `post.ext.coauthors` and `post.flags.pinned`. The cover image is returned as `post.content.thumbnail_url` either way. Leave it unset when you need the video URL or the duration. | | `exclude_pinned` | boolean | no | Set to true to leave out the posts the owner pinned to the top of the profile. A row whose pin state is unknown, which is every row with trim=true, is kept. | | `recent_days` | integer | no | Keep only posts published in the last N days (1 to 3650). Older posts are removed from the page, pinned ones included, and once a page reaches posts older than the window `next_cursor` is not returned, so a walk back to a date window stops on its own. The page still costs what a page costs. | | `include` | string | no | `stats` returns the full stat line on every reel: `engagement.shares`, `engagement.saves` and `post.ext.repost_count` beside views, likes, comments, the remix counter, `post.ext.music_id` for original sounds and licensed music alike, and `post.ext.audio_cluster_id`, Instagram's audio cluster for the reel. It is served by a second source, so a cursor from a request without it cannot continue a walk with it; start from page 1. Both tokens can be combined (`include=audio,stats`). | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | --- # Highlights (/docs/api-reference/instagram/highlights) `GET https://api.insightsocial.app/v1/instagram/highlights` Returns a user's saved story highlight collections, each with its title, cover image, and the number of items inside. Use it to list the highlight collections on a profile, then pass one of the ids to highlight/detail to open it. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `user_id` | string | no | Instagram user id. Use for faster response times. Provide at least one of: `user_id`, `handle`. | | `handle` | string | no | Instagram username without the @ symbol. Provide at least one of: `user_id`, `handle`. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Highlight › Detail (/docs/api-reference/instagram/highlight-detail) `GET https://api.insightsocial.app/v1/instagram/highlight/detail` Returns the individual stories saved inside one highlight, with their media URLs, timestamps, and interaction counts. Use it after highlights: pass the id it returned to see the contents of a single collection. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | Instagram highlight ID: the numeric id, with or without the `highlight:` prefix. Get it from `/v1/instagram/highlights`. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Reels (/docs/api-reference/instagram/search-reels) `GET https://api.insightsocial.app/v1/instagram/search/reels` Returns reels matching a keyword, each with its view count, like count, and author; share and save counts are not included on this endpoint. Use it for keyword based reel discovery; search/hashtag is the better choice when you have a specific tag rather than a phrase. Pass a result URL to post/stats for a reel's share count. **Metered: 10–650 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keyword or phrase to find Instagram reels | | `date_posted` | string | no | Date posted One of: last-week, last-month, last-year. | | `page` | integer | no | The page number to return. | | `region` | string | no | Two-letter market code (ES, MX, DE, BR, KR, FR) to localise the results. Instagram does not rank search by country, so the market's own name is appended to your query (`fitness` with `ES` searches `fitness españa`), unless the query already names the market or is already written in the market's own script (Hangul for KR), which Instagram localises by itself. It favours creators from the market but does not promise that every creator is from it; send `country` for that. For a topic about a place, such as eSIMs or travel, a phrasing locals use works better (`esim móvil` rather than `esim`). Same price as a plain call. Only these markets are supported; any other code is refused with the list, at no charge. A typo is a free 400. One of: ES, MX, DE, BR, KR, FR. | | `include` | string | no | Send `creator` to add each reel's creator card, read from the creator's About this account panel: `post.ext.author_country`, `author_followers`, `author_following`, `author_posts_count`, `author_public_email` and `author_public_phone`, plus the creator's name, avatar and verified flag where the row lacks them. `author_country` is the country the creator declares there, not where the reel was filmed; it is null when Instagram does not publish one, and nothing is guessed. A typo is a free 400. One of: creator. | | `country` | string | no | Two-letter market code (ES, MX, DE, BR, KR, FR) to keep only reels whose creator is based in that market. When no row matches, the page is `items: []` with the warning `country_no_match`, and the lookups are still billed. A page left empty only because the creator lookups failed costs nothing. Works with `max_pages`. A typo is a free 400. One of: ES, MX, DE, BR, KR, FR. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `min_views` | integer | no | Keep only rows with at least this many views (`post.engagement.views`). A row whose view count is unknown is discarded, so every returned row meets the floor. | | `max_age_days` | integer | no | Keep only rows published within this many days (`post.published_at`), 1 to 3650. A row with no date is discarded. | | `sort_rows` | string | no | `views`: return the kept rows ordered by view count, highest first, across every page walked. Rows without a view count go last. One of: views. | | `max_pages` | integer | no | 1 to 5 (default 1). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed as one call to this endpoint; the whole walk counts once against your rate limit. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | --- # Media › Transcript (/docs/api-reference/instagram/media-transcript) `GET https://api.insightsocial.app/v1/instagram/media/transcript` Returns the spoken words in an Instagram video or reel of up to 2 minutes as text. A video with no speech returns 404 with reason no_speech, free of charge. Use it when you need what was said in a video rather than its metrics; pass the reel or video URL. Photo posts and videos over 2 minutes cannot be transcribed. **100 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the Instagram video or reel (a `/reel/`, `/p/` or `/tv/` link to a video post). Photo posts have no audio to transcribe. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Media › Screen Text (/docs/api-reference/instagram/media-screen-text) `GET https://api.insightsocial.app/v1/instagram/media/screen-text` Returns the text shown on an Instagram post's images, read by AI: the photo, each carousel slide up to 10, or a reel's cover frame, with results per slide. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it for text designed into a post, like carousel headlines; the caption lives on post and spoken words on media/transcript. Text shown later in a reel is not read. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the Instagram post, reel or video (a `/p/`, `/reel/`, `/reels/` or `/tv/` link) | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Audio › Reels (/docs/api-reference/instagram/audio-reels) `GET https://api.insightsocial.app/v1/instagram/audio/reels` Returns reels that use one specific audio track, each with its engagement counts and the account that posted it. Use it after search/music: pass the audio_id to see who is posting with that sound. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `audio_id` | string | yes | Instagram audio ID: the numeric id from an instagram.com/reels/audio/{audio_id}/ URL | | `cursor` | string | no | Pagination cursor. Use the `next_cursor` from the previous response to fetch the next page. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Hashtag (/docs/api-reference/instagram/search-hashtag) `GET https://api.insightsocial.app/v1/instagram/search/hashtag` Returns public posts carrying a hashtag, each with shortcode, URL, caption, media URLs, engagement counts, and the author; share and save counts are not included. Use it to pull a hashtag feed; set type to top, recent, or clips to pick the ranking, and page deeper with the cursor it returns. Pass a result URL to post/stats for a share count. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `hashtag` | string | yes | The hashtag to search for. The leading # is optional. | | `type` | string | no | Ranking of the returned posts: `top` (default), `recent`, or `clips` (reels only). Only `recent` pages: `top` and `clips` are one ranked page each and return `has_more: false`. One of: top, recent, clips. | | `cursor` | string | no | Pagination cursor for `type=recent` only. Use the `next_cursor` from the previous response and send the same `type` again. `top` and `clips` return no cursor. | | `safe_url` | boolean | no | When true, returns URL-safe media links suitable for embedding. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `min_views` | integer | no | Keep only rows with at least this many views (`post.engagement.views`). A row whose view count is unknown is discarded, so every returned row meets the floor. | | `max_age_days` | integer | no | Keep only rows published within this many days (`post.published_at`), 1 to 3650. A row with no date is discarded. | | `sort_rows` | string | no | `views`: return the kept rows ordered by view count, highest first, across every page walked. Rows without a view count go last. One of: views. | | `max_pages` | integer | no | 1 to 5 (default 1). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed as one call to this endpoint; the whole walk counts once against your rate limit. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Profiles (/docs/api-reference/instagram/search-profiles) `GET https://api.insightsocial.app/v1/instagram/search/profiles` Returns public profiles matching a keyword, each with username, display name, bio, follower and following counts, post count, and profile URL. Use it to find accounts by topic, keeping in mind it searches Google's index of Instagram rather than Instagram's own account search. **Metered: 10–250 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Bio or caption keyword/phrase to search for. | | `cursor` | string | no | The cursor returned by the previous response. In this version it is the next Google results page number. | | `include` | string | no | A typo is a free 400. One of: about. | | `limit` | integer | no | Cap the rows served, and with `include=about` the credits held. 1 to 12. Default 8 when the token is present so the hold is 17, not 25. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Reels › Trending (/docs/api-reference/instagram/reels-trending) `GET https://api.insightsocial.app/v1/instagram/reels/trending` Returns reels from Instagram's public trending page, each with shortcode, URL, caption, media URLs, engagement counts where shown, and the account. Use it to sample global trends; it takes no region. Call again to see more. For one region, page location/posts after search/location. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Followers (/docs/api-reference/instagram/followers) `GET https://api.insightsocial.app/v1/instagram/followers` Returns the accounts that follow a user, each with username, display name, avatar URL, verification status, and profile URL. Use it to walk an account's follower list, passing either handle or user_id and paging deeper with the cursor it returns. **Metered: 50–100 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | no | Instagram username without the @ symbol. Provide at least one of: `handle`, `user_id`. | | `user_id` | string | no | Instagram numeric user ID. Use this for faster responses. Provide at least one of: `handle`, `user_id`. | | `cursor` | string | no | Pagination cursor. Use the `next_cursor` from the previous response to fetch the next page. | | `coverage` | string | no | Send `full` to walk a merged list: accounts are added from several reads of the list and no account appears twice inside a page, until the rows reach the profile's count in `data.total`. On the pages one source covered on its own a handful of accounts can repeat across consecutive pages; wherever that source cannot cover the list the merged walk runs instead, and it never returns an account it has already given you. Requires `handle`. A page usually takes about 8 seconds and can take up to about 45, and a `503` is retried with the same cursor. A cursor from this mode only continues this mode. One of: full. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Following (/docs/api-reference/instagram/following) `GET https://api.insightsocial.app/v1/instagram/following` Returns the accounts a user follows, each with username, display name, avatar URL, verification status, and profile URL. Use it for the other side of the graph from followers: the accounts this user chose to follow. **Metered: 50–100 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | no | Instagram username without the @ symbol. Provide at least one of: `handle`, `user_id`. | | `user_id` | string | no | Instagram numeric user ID. Use this for faster responses. Provide at least one of: `handle`, `user_id`. | | `cursor` | string | no | Pagination cursor. Use the `next_cursor` from the previous response to fetch the next page. | | `coverage` | string | no | Send `full` to walk a merged list: accounts are added from several reads of the list and no account appears twice inside a page, until the rows reach the profile's count in `data.total`. On the pages one source covered on its own a handful of accounts can repeat across consecutive pages; wherever that source cannot cover the list the merged walk runs instead, and it never returns an account it has already given you. Requires `handle`. A page usually takes about 8 seconds and can take up to about 45, and a `503` is retried with the same cursor. A cursor from this mode only continues this mode. One of: full. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Similar (/docs/api-reference/instagram/similar) `GET https://api.insightsocial.app/v1/instagram/similar` Returns the accounts Instagram suggests as similar to a given user, each with username, display name, avatar URL, verification status, and URL. Use it to find accounts comparable to one you already know; it returns a single fixed list and cannot be paged. **Metered: 50–850 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | no | Instagram username without the @ symbol. Provide at least one of: `handle`, `user_id`. | | `user_id` | string | no | Instagram numeric user ID. Use this for faster responses. Provide at least one of: `handle`, `user_id`. | | `include` | string | no | Set to `profile` (one token only) to fill `author.followers`, `author.following` and `author.bio` on every row in this one call, plus `author.posts_count` where the lookup has an exact total. The hydrated list is its top 20 accounts unless you pass `limit` (1 to 80). One of: profile. | | `limit` | integer | no | Take the top N accounts of the list (1 to 80). Without `include=profile` the full list is returned unless you cap it. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post › Likers (/docs/api-reference/instagram/post-likers) `GET https://api.insightsocial.app/v1/instagram/post/likers` Returns up to about 2,000 of the accounts that liked a post, each with username, display name, avatar, and verification status, plus the full like count, in one call with no further pages. Use it to see who liked a post. A post with a few hundred likes comes back whole; past that, expect about 1,000 to 2,000 likers however many likes it has (999 of 5,967 and 1,999 of 1,309,613 in our tests), and fewer on an older post (737 of 1,390,071). **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | URL of the Instagram post (a /p/ or /reel/ link). | | `safe_url` | boolean | no | When true, returns URL-safe profile picture links suitable for embedding. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post › Stats (/docs/api-reference/instagram/post-stats) `GET https://api.insightsocial.app/v1/instagram/post/stats` Returns one post's engagement: likes, comments, and, on video posts only, the play count and the share count shown by the paper plane icon. Collab posts also list co-authors under ext.coauthors. Use it when you need the share count for a single post, or the co-author list of one collab post; views also come on post itself, and photo posts return both numbers as null. **Metered: 50–90 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | URL of the Instagram post (a /p/, /reel/, or /tv/ link). | | `safe_url` | boolean | no | When true, returns URL-safe media links suitable for embedding. | | `include` | string | no | Set to `saves` (one token only) to ask a third source for a reel's save count (`post.engagement.saves`) and repost count (`post.ext.repost_count`) when the plain call returned without them. Photos and carousels are never looked up, because Instagram publishes no save count on them. One of: saves. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Tagged (/docs/api-reference/instagram/tagged) `GET https://api.insightsocial.app/v1/instagram/tagged` Returns posts that tag a given user, each with its shortcode, URL, caption, media URLs, engagement counts, and the account that posted it. Use it to see what other people posted about an account; profile/posts returns only what the account posted itself. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | no | Instagram username without the @ symbol. Provide at least one of: `handle`, `user_id`. | | `user_id` | string | no | Instagram numeric user ID. Use this for faster responses. Provide at least one of: `handle`, `user_id`. | | `cursor` | string | no | Pagination cursor. Use the `next_cursor` from the previous response to fetch the next page. | | `safe_url` | boolean | no | When true, returns URL-safe media links suitable for embedding. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Location › Posts (/docs/api-reference/instagram/location-posts) `GET https://api.insightsocial.app/v1/instagram/location/posts` Returns posts tagged at a place, newest first, about 60 a page, each with shortcode, URL, caption, media URLs, engagement counts, and the author. Use it after search/location for region-scoped posts. Page back in time with the cursor; for a time window, stop once a whole page is older than its start. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `location_id` | string | yes | Instagram location id. Use location.pk from /v1/instagram/search/location. | | `cursor` | string | no | Pagination cursor. Use the `next_cursor` from the previous response to fetch the next, older page. | | `safe_url` | boolean | no | When true, returns URL-safe media links suitable for embedding. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Engagement (/docs/api-reference/instagram/engagement) `GET https://api.insightsocial.app/v1/instagram/engagement` Returns an engagement report for an account: its engagement rate over the recent posts, follower count, total likes and comments, and a per-post breakdown. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it when you want the engagement math done for you, including likes and comments per hour on each post, rather than raw posts to total yourself. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Instagram username without the @ symbol. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Location (/docs/api-reference/instagram/search-location) `GET https://api.insightsocial.app/v1/instagram/search/location` Returns places matching a keyword, each with its location id, name, and coordinates, plus a display title and subtitle. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to turn a place name into a location id, then pass that id to location/posts to see what was posted there. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keyword or phrase to find Instagram locations. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search (/docs/api-reference/instagram/search) `GET https://api.insightsocial.app/v1/instagram/search` Returns mixed Instagram search matches for a query: accounts, hashtags, and places in one payload. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it when you need the same mixed results Instagram's search box shows; search/profiles, search/hashtag, and search/location split those types. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keyword as typed in Instagram's search box. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Popular (/docs/api-reference/instagram/search-popular) `GET https://api.insightsocial.app/v1/instagram/search/popular` Returns popular Instagram posts matching a keyword, each with caption, play count, permalink, thumbnail, and the owner's username. Use it to sample what is popular for a topic; search/reels is the choice when you want a keyword reel feed instead. **Metered: 10–130 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Keyword or topic to find popular Instagram posts. | | `cursor` | string | no | Cursor from the previous response to fetch the next page. | | `include` | string | no | Set to `engagement` (one token only) to fill `post.engagement.likes`, `.comments`, `.shares`, `.views`, `post.published_at` and `post.content.duration_seconds` on every row in this one call. One of: engagement. | | `limit` | integer | no | Take the top N rows of the page (1 to 12) after the search has run. It is not a page size: `next_cursor` still advances past the full page, so rows beyond N on this page are not returned by the next page. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post › Comment › Replies (/docs/api-reference/instagram/post-comment-replies) `GET https://api.insightsocial.app/v1/instagram/post/comment/replies` Returns replies under one Instagram comment, each with text, like count, nested reply count, author, and timestamp. Use it after post/comments: pass the post URL and the parent comment_id to expand one thread. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | URL of the Instagram post that holds the parent comment. | | `comment_id` | string | yes | Id of the parent comment whose replies to list. | | `cursor` | string | no | Cursor from the previous response to fetch the next page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | --- # Username Suggestions (/docs/api-reference/instagram/username-suggestions) `GET https://api.insightsocial.app/v1/instagram/username-suggestions` Returns suggested available Instagram usernames built from a keyword you supply. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it when picking a new handle; it invents name ideas and does not look up existing accounts, which is what search/profiles does. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Keyword to seed the username suggestions. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Music (/docs/api-reference/instagram/search-music) `GET https://api.insightsocial.app/v1/instagram/search/music` Returns audio tracks matching a keyword, each with artist, title, audio and cover artwork URLs, duration, and usage details. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to find a specific sound by name. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keyword or phrase to find Instagram audio tracks. | | `cursor` | string | no | Pagination cursor. Use the `next_cursor` from the previous response to fetch the next page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Stories (/docs/api-reference/instagram/stories) `GET https://api.insightsocial.app/v1/instagram/stories` Returns the stories a user has live right now, each with its id, image or video URLs, thumbnail, duration, capture time, and the author. Use it to check an account's current story tray; an account with nothing live returns an empty list rather than an error. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | no | Instagram username without the @ symbol. Provide at least one of: `handle`, `user_id`. | | `user_id` | string | no | Instagram numeric user ID. Use this for faster responses. Provide at least one of: `handle`, `user_id`. | | `safe_url` | boolean | no | When true, returns URL-safe media links suitable for embedding. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Story › Download (/docs/api-reference/instagram/story-download) `GET https://api.insightsocial.app/v1/instagram/story/download` Returns the full resolution image or video URLs and metadata for one specific story. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it after stories: pass the author's user_id plus the story_id it returned to pull that one story's media. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `user_id` | string | yes | Instagram numeric user ID of the story's author. | | `story_id` | string | yes | ID of the individual story to download. | | `safe_url` | boolean | no | When true, returns URL-safe media links suitable for embedding. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Full (/docs/api-reference/instagram/profile-full) `GET https://api.insightsocial.app/v1/instagram/profile/full` Returns the profile plus its latest 12 posts in one call. Use it instead of calling profile and profile/posts separately; the profile still comes back if posts cannot be fetched. **Beta.** A composite: `data.profile` is a schema-2 Profile and `data.posts[].post` are schema-2 Posts, and the posts page with `cursor`. The rest of `data` is the source's own structure under our naming rules (snake_case keys, string ids). Fields outside the profile and posts may change. See [Schema 2](/docs/schema-2). **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Instagram username or handle, with or without a leading @. One of the identity params is required. | | `posts` | integer | no | How many of the fetched recent posts to return and compute the metrics over (1-100, default 25). | | `cursor` | string | no | Pass a prior response's posts_cursor to read the next page of posts. | | `include` | string | no | | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Reels › Full (/docs/api-reference/instagram/profile-reels-full) `GET https://api.insightsocial.app/v1/instagram/profile/reels/full` Returns a user's reels with views, likes, comments, and a per-reel share count where the second source exposes one, plus a coverage figure. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it when share counts matter across a creator's reels; profile/reels is the lighter choice when views, likes, and comments are enough. **Metered: 50–250 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | no | Instagram username (with or without a leading @). Required unless user_id is given. Provide at least one of: `handle`, `user_id`. | | `user_id` | string | no | Numeric Instagram user id. Alternative to handle: the account's username is looked up from the id first, so a user_id call returns the same items, share counts and price as a handle call. An id that matches no account returns 404 and is not charged. Provide at least one of: `handle`, `user_id`. | | `cursor` | string | no | Opaque cursor from a prior response's next_cursor to page deeper. | | `limit` | integer | no | Return up to this many items in one call (1-50). The endpoint pages the underlying source server-side until it has collected this many (or runs out), and bills per page read. Omit for a single page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Posts › Full (/docs/api-reference/instagram/profile-posts-full) `GET https://api.insightsocial.app/v1/instagram/profile/posts/full` Returns a user's recent posts with likes, comments, views, and a per-post share count where the second source exposes one, plus a coverage figure. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it when share counts matter across a whole feed; profile/posts is the lighter choice when likes and comments are enough. **Metered: 50–250 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | no | Instagram username (with or without a leading @). Required unless user_id is given. Provide at least one of: `handle`, `user_id`. | | `user_id` | string | no | Numeric Instagram user id. Alternative to handle: the account's username is looked up from the id first, so a user_id call returns the same items, share counts and price as a handle call. An id that matches no account returns 404 and is not charged. Provide at least one of: `handle`, `user_id`. | | `cursor` | string | no | Opaque cursor from a prior response's next_cursor to page deeper. | | `limit` | integer | no | Return up to this many items in one call (1-50). The endpoint pages the underlying source server-side until it has collected this many (or runs out), and bills per page read. Omit for a single page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Clip (/docs/api-reference/kick/clip) `GET https://api.insightsocial.app/v1/kick/clip` Returns one Kick clip's title, view count, duration, category, creator name, channel name, thumbnail URL, and creation timestamp. Use it when you have a Kick clip URL and need its stats and metadata. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the Kick clip | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile (/docs/api-reference/kwai/profile) `GET https://api.insightsocial.app/v1/kwai/profile` Returns a Kwai user's public profile: username, bio, avatar, verification status, and follower, following, like, and post counts. Use it for a quick profile snapshot; use user/posts for that account's posts. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | no | Kwai profile handle (without the @). Provide at least one of: `handle`, `url`. | | `url` | string | no | Kwai profile URL. Provide at least one of: `handle`, `url`. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # User › Posts (/docs/api-reference/kwai/user-posts) `GET https://api.insightsocial.app/v1/kwai/user/posts` Returns a paginated list of a Kwai user's posts: captions, media URLs, covers, engagement counts, and author info. Use it to list an account's posts; use profile when you only need the account's own counts. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | no | Kwai profile handle (without the @). Provide at least one of: `handle`, `url`. | | `url` | string | no | Kwai profile URL. Provide at least one of: `handle`, `url`. | | `cursor` | string | no | Cursor from the previous response for the next page | | `count` | integer | no | Number of posts to return (max 50) | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post (/docs/api-reference/kwai/post) `GET https://api.insightsocial.app/v1/kwai/post` Returns one Kwai post: caption, video and cover URLs, view/like/comment/share counts, author info, and music metadata. Use it when you have a single post URL; use user/posts to list everything an account has posted. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | no | Full URL of the Kwai post | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile (/docs/api-reference/linkedin/profile) `GET https://api.insightsocial.app/v1/linkedin/profile` Returns a LinkedIn member's public profile: full name, headline, location, follower and connection counts, profile picture, and profile URL. Use it as the starting point for any person lookup, then call the profile sub-endpoints for their experiences, skills, or posts. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile page | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Company (/docs/api-reference/linkedin/company) `GET https://api.insightsocial.app/v1/linkedin/company` Returns a LinkedIn company page: company name, description, follower count, headquarters location, website, and logo. Use it to look up a company by its page URL, and to get the numeric company_id that the other company endpoints need. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn company page | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Company › By Domain (/docs/api-reference/linkedin/company-by-domain) `GET https://api.insightsocial.app/v1/linkedin/company/by-domain` Returns the LinkedIn company page that owns a website domain: name, description, follower count, headquarters, website, logo, and numeric company id. Use it when you have a company website rather than a LinkedIn page URL. company looks up the same record from a page URL. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `domain` | string | yes | Company website domain, with or without a scheme. `microsoft.com` and `https://www.microsoft.com/` resolve the same page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Similar (/docs/api-reference/linkedin/profile-similar) `GET https://api.insightsocial.app/v1/linkedin/profile/similar` Returns members LinkedIn groups with a given profile, each with handle, headline, profile URL, and picture. Use it after profile to find nearby people; search/people filters by title, company, or location instead. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile to find similar members for. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Companies (/docs/api-reference/linkedin/search-companies) `GET https://api.insightsocial.app/v1/linkedin/search/companies` Returns LinkedIn company pages matching a keyword, each with name, tagline, page URL, and logo. Use it to find companies by name and optional size, industry, location, or open-jobs filters, then pass a result to company. **100 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Company name or keyword. | | `page` | integer | no | 1-based page number. Defaults to 1. | | `company_size` | string | no | Headcount bucket. One of `1-10`, `11-50`, `51-200`, `201-500`, `501-1000`, `1001-5000`, `5001-10000`, `10001+`. One of: 1-10, 11-50, 51-200, 201-500, 501-1000, 1001-5000, 5001-10000, 10001+. | | `has_jobs` | boolean | no | When true, return only companies that currently have open jobs. | | `industry_ids` | string | no | Comma-separated industry ids from /v1/linkedin/search/industry. | | `geocode` | string | no | Comma-separated location ids from /v1/linkedin/search/location. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Posted Jobs (/docs/api-reference/linkedin/profile-posted-jobs) `GET https://api.insightsocial.app/v1/linkedin/profile/posted-jobs` Returns the jobs a LinkedIn member has posted, each with job id, title, hiring company, location, and posting date. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it when you have a person's profile URL and want the roles they listed, not every opening at their employer. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile whose posted jobs to list. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Job › Hiring Team (/docs/api-reference/linkedin/job-hiring-team) `GET https://api.insightsocial.app/v1/linkedin/job/hiring-team` Returns the members LinkedIn shows as the hiring team on a job, each with handle, headline, profile URL, and picture. Use it after search/jobs or company/jobs, passing a job id or the job URL. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | no | LinkedIn job ID (from /v1/linkedin/search/jobs or /v1/linkedin/company/jobs). Provide at least one of: `id`, `url`. | | `url` | string | no | Full URL of the LinkedIn job posting. Provide at least one of: `id`, `url`. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Articles (/docs/api-reference/linkedin/profile-articles) `GET https://api.insightsocial.app/v1/linkedin/profile/articles` Returns Pulse articles a LinkedIn member published, each with text, author, engagement, and date. Use it for long-form writing. profile/posts is the short-form feed; article fetches one Pulse URL. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile whose articles to list. | | `page` | integer | no | 1-based page number. Defaults to 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Article (/docs/api-reference/linkedin/article) `GET https://api.insightsocial.app/v1/linkedin/article` Returns one LinkedIn Pulse article: its text, author, like and comment counts, and publish time. Use it when you have a /pulse/ URL. Comments and reactions are article/comments and article/reactions. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn Pulse article. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Article › Comments (/docs/api-reference/linkedin/article-comments) `GET https://api.insightsocial.app/v1/linkedin/article/comments` Returns comments on a LinkedIn Pulse article, with commenter identity, text, and timestamp. Use it after article or profile/articles, passing the article URL. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn Pulse article. | | `page` | integer | no | 1-based page number. Defaults to 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Article › Reactions (/docs/api-reference/linkedin/article-reactions) `GET https://api.insightsocial.app/v1/linkedin/article/reactions` Returns members who reacted to a LinkedIn Pulse article, each with handle, name, picture, and reaction type. Use it after article, passing the same Pulse URL. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn Pulse article. | | `page` | integer | no | 1-based page number. Defaults to 1. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Activity (/docs/api-reference/linkedin/profile-activity) `GET https://api.insightsocial.app/v1/linkedin/profile/activity` Returns when a LinkedIn member last showed public activity. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to see recency, not the feed itself. profile/posts returns the posts. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Company › Also Viewed (/docs/api-reference/linkedin/company-also-viewed) `GET https://api.insightsocial.app/v1/linkedin/company/also-viewed` Returns company pages LinkedIn shows as people also viewed, each with name, page URL, and logo. Use it after company to find nearby pages; pass the numeric company_id from company, or the company page URL. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `company_id` | string | no | Numeric LinkedIn company id, for example 1441 for Google. It is `author.id` on `/v1/linkedin/company`. Send this or `url`. Provide at least one of: `company_id`, `url`. | | `url` | string | no | Full URL of the LinkedIn company page, for example https://www.linkedin.com/company/google/. Send this or `company_id`. Provide at least one of: `company_id`, `url`. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Company › Employees Count (/docs/api-reference/linkedin/company-employees-count) `GET https://api.insightsocial.app/v1/linkedin/company/employees-count` Returns how many LinkedIn members list a company as employer, optionally broken down by location. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it for a headcount by city or country; company.ext.employee_count is the headline total. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `company_id` | string | yes | LinkedIn numeric company ID (from /v1/linkedin/company). | | `geocode` | string | no | Comma-separated location ids from /v1/linkedin/search/location. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Interests › Schools (/docs/api-reference/linkedin/profile-interests-schools) `GET https://api.insightsocial.app/v1/linkedin/profile/interests/schools` Returns the school pages a LinkedIn member follows, each with name and page URL. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it after profile. search/schools turns a university name into a school id. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile. | | `page` | integer | no | 1-based page number. Defaults to 1. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Interests › Newsletters (/docs/api-reference/linkedin/profile-interests-newsletters) `GET https://api.insightsocial.app/v1/linkedin/profile/interests/newsletters` Returns the newsletters a LinkedIn member follows, each with name and page URL. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it after profile to see which newsletters they follow. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile. | | `page` | integer | no | 1-based page number. Defaults to 1. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Interests › Top Voices (/docs/api-reference/linkedin/profile-interests-top-voices) `GET https://api.insightsocial.app/v1/linkedin/profile/interests/top-voices` Returns the Top Voice profiles a LinkedIn member follows, each with name and profile URL. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it after profile to see which Top Voices they follow. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Position Skills (/docs/api-reference/linkedin/profile-position-skills) `GET https://api.insightsocial.app/v1/linkedin/profile/position-skills` Returns a member's positions with the skills listed on each role. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it when you need skills per job. profile/experiences is the role list; profile/skills is the flat list. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Top Position (/docs/api-reference/linkedin/profile-top-position) `GET https://api.insightsocial.app/v1/linkedin/profile/top-position` Returns the company page of the member's current top position: company id, name, website, headcount, industries and locations, not the job title. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to find where a member works now. company returns the same company in the canonical schema; profile/experiences has titles and dates. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › People › By Url (/docs/api-reference/linkedin/search-people-by-url) `GET https://api.insightsocial.app/v1/linkedin/search/people/by-url` Returns LinkedIn members from a people-search results URL, each with handle, headline, and profile URL. Use it when you already have a LinkedIn people-search URL. Keyword search is search/people. **100 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | LinkedIn people-search results URL. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › All (/docs/api-reference/linkedin/profile-all) `GET https://api.insightsocial.app/v1/linkedin/profile/all` Returns a member's entire public profile in one call: `author` with both counts, and `background` with About, experience, education, skills, recommendations, Featured and interests. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it for a one-call profile audit, in place of stacking profile, profile/complete, profile/stats, the five interests lanes and profile/similar. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | no | Full URL of the LinkedIn profile. Provide at least one of: `url`, `urls`. | | `urls` | string | no | Up to 10 full LinkedIn profile URLs in one call, comma separated. The response becomes `{items, total}` instead of a single profile. A list where nothing resolves is a free 404. Provide at least one of: `url`, `urls`. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Complete (/docs/api-reference/linkedin/profile-complete) `GET https://api.insightsocial.app/v1/linkedin/profile/complete` Returns a member's full background: `author` in the /profile shape, and `background` with About, experiences, educations, skills and recommendations in the profile/* item shapes. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it for background checks and enrichment, where one premium call replaces profile, profile/experiences, profile/educations, profile/skills and profile/recommendations. **100 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › With Posts (/docs/api-reference/linkedin/profile-with-posts) `GET https://api.insightsocial.app/v1/linkedin/profile/with-posts` Returns a member's profile with follower and connection counts in `author`, recent `posts` in the profile/posts item shape, and experiences, educations and skills under `background`. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it for one call covering the counts, recent posts and career history. Use profile/complete when you need recommendations. **100 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post › With Comments (/docs/api-reference/linkedin/post-with-comments) `GET https://api.insightsocial.app/v1/linkedin/post/with-comments` Returns one LinkedIn post packed with its comments in a single payload. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to fetch a post and its thread together. Prefer post plus post/comments when you want each on its own schema. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Activity URL of the LinkedIn post. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post (/docs/api-reference/linkedin/post) `GET https://api.insightsocial.app/v1/linkedin/post` Returns one LinkedIn post: its text, author name and picture, like, comment and share counts, attached media, and the publish time. Use it when you have a post URL and want the post itself, not its comments, reactions, or reposts. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn post. It has to carry the post's ACTIVITY id, either `https://www.linkedin.com/feed/update/urn:li:activity:` or `https://www.linkedin.com/posts/-activity--`. Every endpoint that lists posts publishes the activity form as `post.url`, so copy that field rather than rebuilding the URL from `post.id`, which is not always the activity id. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › People (/docs/api-reference/linkedin/search-people) `GET https://api.insightsocial.app/v1/linkedin/search/people` Returns LinkedIn members matching a name or filters, each with handle, headline, location, follower count, profile URL, and member id. Use it to build a prospect list by job title, company, school, or location; use company/people to list one employer's staff. **Metered: 100–500 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Name or display-name keyword to search for (e.g. 'Bill Gates'). | | `page` | integer | no | Page number for pagination (default 1). | | `first_name` | string | no | Filter by first name. | | `last_name` | string | no | Filter by last name. | | `title` | string | no | Filter by job title or headline. | | `current_company` | string | no | Filter by current company, using a numeric company id from /v1/linkedin/company (`author.id`). Comma-separated for multiple. | | `past_company` | string | no | Filter by a previously-worked company, using a numeric company id from /v1/linkedin/company (`author.id`). | | `school` | string | no | Filter by school, using a school id from /v1/linkedin/search/schools (`id`). | | `industry` | string | no | Filter by industry, using an industry id from /v1/linkedin/search/industry (`industry_id`). | | `geocode_location` | string | no | Filter by location, using a geocode id from /v1/linkedin/search/location (`geocode`). | | `profile_language` | string | no | Filter by profile language (ISO 2-letter code, e.g. 'en'). | | `service_category` | string | no | Filter by service category, using a numeric service-category id. No endpoint resolves these, so pass one you already hold. | | `follower_of` | string | no | Return people who follow a specific member, using the member's URN from /v1/linkedin/profile (`author.ext.urn`). This one is a URN and not a numeric id. | | `include` | string | no | Set to `profile` (one token only) to join every row to the member's profile lookup in this one call: `author.followers` becomes the exact count and `followers_approximate` reads false on every row the lookup filled, `author.following` is filled with the member's connection count, and the location, joined date, country, website, cover image and profile flags land where the row lacks them. One of: profile. | | `limit` | integer | no | Take the top N rows of this page (1 to 10). The page cursor still advances past the full page of 10, so the rows past your limit are skipped, not carried to the next page. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Company › People (/docs/api-reference/linkedin/company-people) `GET https://api.insightsocial.app/v1/linkedin/company/people` Returns the members who list a company as their employer, each with handle, headline, location, profile picture, and follower count. Use it when you already have a company_id and want its staff; use search/people to filter across all of LinkedIn instead. **Metered: 100–500 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `company_id` | string | yes | LinkedIn numeric company ID (from /v1/linkedin/company). | | `page` | integer | no | Page number for pagination (default 1). | | `include` | string | no | Set to `profile` (one token only) to join every row to the member's profile lookup in this one call: `author.followers` becomes the exact count and `followers_approximate` reads false on every row the lookup filled, `author.following` is filled with the member's connection count, and the location, joined date, country, website, cover image and profile flags land where the row lacks them. One of: profile. | | `limit` | integer | no | Take the top N rows of this page (1 to 10). The page cursor still advances past the full page of 10, so the rows past your limit are skipped, not carried to the next page. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post › Comments (/docs/api-reference/linkedin/post-comments) `GET https://api.insightsocial.app/v1/linkedin/post/comments` Returns the comments on a LinkedIn post: commenter name, comment text, reaction counts, reply count, pinned and edited flags, and timestamps. Use it for the top-level comments on a post, then pass a comment's id to post/comments/replies to open one thread. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn post. It has to carry the post's ACTIVITY id, either `https://www.linkedin.com/feed/update/urn:li:activity:` or `https://www.linkedin.com/posts/-activity--`. Every endpoint that lists posts publishes the activity form as `post.url`, so copy that field rather than rebuilding the URL from `post.id`, which is not always the activity id. | | `page` | integer | no | Page number for pagination (default 1). | | `post_type` | string | no | Post type: 'activity' (default) or 'ugc'. One of: activity, ugc. | | `sort_order` | string | no | Comment ordering: 'relevance' or 'recent'. One of: recent, relevance. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Posts (/docs/api-reference/linkedin/profile-posts) `GET https://api.insightsocial.app/v1/linkedin/profile/posts` Returns the posts a LinkedIn member published, each with text, author name, like, comment and share counts, media, and publish time. Use it for what a person posted themselves; profile/reactions covers posts they only reacted to, profile/comments their comments. **Metered: 50–2000 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile, company, or post. | | `limit` | integer | no | How many posts to return in the one call, 1 to 100. Defaults to the platform's 20. 100 is the platform's hard ceiling. Ask for the depth you need up front - there is no second page to fetch. | | `urn` | string | no | The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster. Verified on the deployed API 05/09/2026. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Reactions (/docs/api-reference/linkedin/profile-reactions) `GET https://api.insightsocial.app/v1/linkedin/profile/reactions` Returns the posts a LinkedIn member reacted to, each with the post text, its original author, like, comment and share counts, and date. Use it to see what a person engages with rather than what they publish, which profile/posts returns. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile, company, or post. | | `urn` | string | no | The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster. Verified on the deployed API 05/09/2026. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post › Reposts (/docs/api-reference/linkedin/post-reposts) `GET https://api.insightsocial.app/v1/linkedin/post/reposts` Returns the reposts of a LinkedIn post, each with the resharer's name, any added commentary, engagement counts, and publish time. Use it to see who amplified a post; post/reactions lists who reacted and post/comments lists who replied. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn post. It has to carry the post's ACTIVITY id, either `https://www.linkedin.com/feed/update/urn:li:activity:` or `https://www.linkedin.com/posts/-activity--`. Every endpoint that lists posts publishes the activity form as `post.url`, so copy that field rather than rebuilding the URL from `post.id`, which is not always the activity id. | | `cursor` | string | no | LinkedIn cursor. | | `page` | integer | no | 1-based page number. Defaults to 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Group › Posts (/docs/api-reference/linkedin/group-posts) `GET https://api.insightsocial.app/v1/linkedin/group/posts` Returns the posts inside a LinkedIn group, each with text, author name, like, comment and share counts, media, and publish time. Use it to read a group's discussion feed; call group first if you also need the group's own details. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `group_id` | string | yes | LinkedIn numeric group ID. | | `page` | integer | no | 1-based page number. Defaults to 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Company › Affiliated Pages (/docs/api-reference/linkedin/company-affiliated-pages) `GET https://api.insightsocial.app/v1/linkedin/company/affiliated-pages` Returns a company's affiliated and showcase pages, each with page name, LinkedIn URL, speciality, follower count, and logo. Use it to map a parent brand to its regional and product pages before pulling posts or jobs for each one. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `company_id` | string | yes | LinkedIn numeric company ID (from /v1/linkedin/company). | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post › Comments › Replies (/docs/api-reference/linkedin/post-comments-replies) `GET https://api.insightsocial.app/v1/linkedin/post/comments/replies` Returns the replies under one LinkedIn comment, each with the replier's name, reply text, reaction and reply counts, and timestamps. Use it after post/comments: pass that comment's comment_id together with the post url to expand a single thread. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn post. It has to carry the post's ACTIVITY id, either `https://www.linkedin.com/feed/update/urn:li:activity:` or `https://www.linkedin.com/posts/-activity--`. Every endpoint that lists posts publishes the activity form as `post.url`, so copy that field rather than rebuilding the URL from `post.id`, which is not always the activity id. | | `comment_id` | string | yes | The parent comment's `comment.id` from /v1/linkedin/post/comments, or its `comment.ext.urn` (urn:li:comment:(ugcPost:,)). | | `cursor` | string | no | LinkedIn cursor. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Experiences (/docs/api-reference/linkedin/profile-experiences) `GET https://api.insightsocial.app/v1/linkedin/profile/experiences` Returns a LinkedIn member's work history, with each role's job title, company, employment dates, location, and description. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it for the full career list, which the main profile endpoint condenses down to the current company only. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile, company, or post. | | `page` | integer | no | 1-based page number. Defaults to 1. | | `urn` | string | no | The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster. Verified on the deployed API 05/09/2026. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Educations (/docs/api-reference/linkedin/profile-educations) `GET https://api.insightsocial.app/v1/linkedin/profile/educations` Returns a LinkedIn member's education history, with each entry's school, degree, field of study, and the years attended. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it for schooling detail; profile/experiences covers jobs, and search/schools turns a school name into a filter id. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile, company, or post. | | `page` | integer | no | 1-based page number. Defaults to 1. | | `urn` | string | no | The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster. Verified on the deployed API 05/09/2026. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Skills (/docs/api-reference/linkedin/profile-skills) `GET https://api.insightsocial.app/v1/linkedin/profile/skills` Returns the skills listed on a LinkedIn member's profile, with each skill's name and the endorsements shown against it. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to score a person against a skill requirement, separately from the roles returned by profile/experiences. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile, company, or post. | | `page` | integer | no | 1-based page number. Defaults to 1. | | `urn` | string | no | The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster. Verified on the deployed API 05/09/2026. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Honors (/docs/api-reference/linkedin/profile-honors) `GET https://api.insightsocial.app/v1/linkedin/profile/honors` Returns the honors and awards on a LinkedIn member's profile, with each award's title, issuer, date, and description. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it for awards only: certifications live in profile/certifications and published work in profile/publications. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile, company, or post. | | `page` | integer | no | 1-based page number. Defaults to 1. | | `urn` | string | no | The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster. Verified on the deployed API 05/09/2026. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Certifications (/docs/api-reference/linkedin/profile-certifications) `GET https://api.insightsocial.app/v1/linkedin/profile/certifications` Returns the licenses and certifications on a LinkedIn member's profile, with each one's name, issuer, and issue or expiry dates. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to check formal credentials, as opposed to self-listed skills in profile/skills or degrees in profile/educations. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile, company, or post. | | `page` | integer | no | 1-based page number. Defaults to 1. | | `urn` | string | no | The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster. Verified on the deployed API 05/09/2026. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Publications (/docs/api-reference/linkedin/profile-publications) `GET https://api.insightsocial.app/v1/linkedin/profile/publications` Returns the publications on a LinkedIn member's profile, with each one's title, publisher, publication date, description, and link. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it for articles, papers, and books a person published, not for their LinkedIn posts, which profile/posts returns. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile, company, or post. | | `page` | integer | no | 1-based page number. Defaults to 1. | | `urn` | string | no | The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster. Verified on the deployed API 05/09/2026. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Volunteers (/docs/api-reference/linkedin/profile-volunteers) `GET https://api.insightsocial.app/v1/linkedin/profile/volunteers` Returns the volunteer experience on a LinkedIn member's profile, with each entry's role, organisation, cause, and dates. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it for unpaid and community roles, which the paid work history in profile/experiences does not cover. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile, company, or post. | | `page` | integer | no | 1-based page number. Defaults to 1. | | `urn` | string | no | The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster. Verified on the deployed API 05/09/2026. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Recommendations (/docs/api-reference/linkedin/profile-recommendations) `GET https://api.insightsocial.app/v1/linkedin/profile/recommendations` Returns the recommendations on a LinkedIn member's profile, with the recommender's name and headline, the recommendation text, and date. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it for written references, and set type to received or given to choose which side of the relationship you want. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile, company, or post. | | `page` | integer | no | 1-based page number. Defaults to 1. | | `type` | string | no | Filter by kind. See the allowed values. One of: received, given. | | `urn` | string | no | The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster. Verified on the deployed API 05/09/2026. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Interests › Companies (/docs/api-reference/linkedin/profile-interests-companies) `GET https://api.insightsocial.app/v1/linkedin/profile/interests/companies` Returns the companies a LinkedIn member follows, with each company's name, LinkedIn URL, follower count, and logo. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to infer a person's interests and vendor affinities; profile/interests/groups covers the groups they follow. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile, company, or post. | | `page` | integer | no | 1-based page number. Defaults to 1. | | `urn` | string | no | The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster. Verified on the deployed API 05/09/2026. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Interests › Groups (/docs/api-reference/linkedin/profile-interests-groups) `GET https://api.insightsocial.app/v1/linkedin/profile/interests/groups` Returns the LinkedIn groups a member follows, with each group's name, URL, and the size of its membership. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to find the communities a person belongs to, then pass a group_id to group or group/posts to go deeper. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile, company, or post. | | `page` | integer | no | 1-based page number. Defaults to 1. | | `urn` | string | no | The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster. Verified on the deployed API 05/09/2026. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Images (/docs/api-reference/linkedin/profile-images) `GET https://api.insightsocial.app/v1/linkedin/profile/images` Returns the image posts on a LinkedIn member's profile, with each post's text, image links, engagement counts, and publish time. Use it when you only want photo posts; profile/posts returns every format and profile/videos returns the videos. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile, company, or post. | | `urn` | string | no | The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster. Verified on the deployed API 05/09/2026. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Videos (/docs/api-reference/linkedin/profile-videos) `GET https://api.insightsocial.app/v1/linkedin/profile/videos` Returns the video posts on a LinkedIn member's profile, with each post's text, video and thumbnail links, engagement counts, and date. Use it to collect a person's videos, then pass a video post's URL to post/transcript to read what was said. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile, company, or post. | | `urn` | string | no | The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster. Verified on the deployed API 05/09/2026. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Comments (/docs/api-reference/linkedin/profile-comments) `GET https://api.insightsocial.app/v1/linkedin/profile/comments` Returns the comments a LinkedIn member left on other people's posts, with the comment text, the post it sits under, and the timestamp. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to track someone's activity in other conversations; post/comments returns the comments on one specific post. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile, company, or post. | | `urn` | string | no | The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster. Verified on the deployed API 05/09/2026. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post › Reactions (/docs/api-reference/linkedin/post-reactions) `GET https://api.insightsocial.app/v1/linkedin/post/reactions` Returns the people who reacted to a LinkedIn post, each with their name, headline, profile URL, and the reaction they left. Use it to see who liked or praised a post, optionally filtered by type; post/comments covers the people who wrote a comment. **Metered: 100–500 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn post. It has to carry the post's ACTIVITY id, either `https://www.linkedin.com/feed/update/urn:li:activity:` or `https://www.linkedin.com/posts/-activity--`. Every endpoint that lists posts publishes the activity form as `post.url`, so copy that field rather than rebuilding the URL from `post.id`, which is not always the activity id. | | `page` | integer | no | 1-based page number. Defaults to 1. | | `type` | string | no | Filter by kind. See the allowed values. One of: all, like, praise, empathy, appreciation, interest. | | `include` | string | no | Set to `profile` (one token only) to join every row to the member's profile lookup in this one call: `author.followers` becomes the exact count and `followers_approximate` reads false on every row the lookup filled, `author.following` is filled with the member's connection count, and the location, joined date, country, website, cover image and profile flags land where the row lacks them. One of: profile. | | `limit` | integer | no | Take the top N rows of this page (1 to 10). The page cursor still advances past the full page of 10, so the rows past your limit are skipped, not carried to the next page. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Company › Jobs (/docs/api-reference/linkedin/company-jobs) `GET https://api.insightsocial.app/v1/linkedin/company/jobs` Returns the jobs a company has posted, each with job id, title, location, hiring company details, posting date, and Easy Apply flag. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it for one employer's openings; search/jobs looks across all companies and job returns one posting's full text. **100 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `company_id` | string | yes | LinkedIn numeric company ID (from /v1/linkedin/company). | | `page` | integer | no | 1-based page number. Defaults to 1. | | `date_posted` | string | no | How recently the job was listed. One of `anytime`, `past_24_hours`, `past_week`, `past_month`. Note the job lanes use `past_24_hours` while /v1/linkedin/search/posts uses `past_24h`; they are different vocabularies and are not interchangeable. One of: anytime, past_24_hours, past_week, past_month. | | `experience_level` | string | no | Required seniority for the role. One of: internship, entry_level, associate, mid_senior, director, executive. | | `job_type` | string | no | Contract type for the role. One of: full_time, part_time, contract, temporary, volunteer, internship, other. | | `remote` | string | no | Workplace type: onsite, remote, or hybrid. One of: onsite, remote, hybrid. | | `easy_apply` | boolean | no | When true, return only jobs that support LinkedIn Easy Apply. | | `sort_by` | string | no | Result ordering. One of: recent, relevant. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Jobs (/docs/api-reference/linkedin/search-jobs) `GET https://api.insightsocial.app/v1/linkedin/search/jobs` Returns LinkedIn job postings matching a keyword, each with job id, title, hiring company, location, posting date, and Easy Apply flag. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to search openings across employers with filters like remote, job type, and experience level, then pass an id to job. **100 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keyword. | | `page` | integer | no | 1-based page number. Defaults to 1. | | `date_posted` | string | no | How recently the job was listed. One of `anytime`, `past_24_hours`, `past_week`, `past_month`. Note the job lanes use `past_24_hours` while /v1/linkedin/search/posts uses `past_24h`; they are different vocabularies and are not interchangeable. One of: anytime, past_24_hours, past_week, past_month. | | `experience_level` | string | no | Required seniority for the role. One of: internship, entry_level, associate, mid_senior, director, executive. | | `job_type` | string | no | Contract type for the role. One of: full_time, part_time, contract, temporary, volunteer, internship, other. | | `remote` | string | no | Workplace type: onsite, remote, or hybrid. One of: onsite, remote, hybrid. | | `easy_apply` | boolean | no | When true, return only jobs that support LinkedIn Easy Apply. | | `sort_by` | string | no | Result ordering. One of: recent, relevant. | | `company` | string | no | Filter by company, using a numeric company id from /v1/linkedin/company (`author.id`). | | `geocode` | string | no | Filter by location, using a geocode id from /v1/linkedin/search/location. | | `industry_ids` | string | no | Comma-separated industry ids from /v1/linkedin/search/industry. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Location (/docs/api-reference/linkedin/search-location) `GET https://api.insightsocial.app/v1/linkedin/search/location` Returns LinkedIn locations matching a place name, each with the location's display name and the geocode id LinkedIn filters on. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it first to turn a city or country name into the id that search/people and search/jobs expect for location filters. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keyword. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Schools (/docs/api-reference/linkedin/search-schools) `GET https://api.insightsocial.app/v1/linkedin/search/schools` Returns LinkedIn school pages matching a name, each with the school's name, page URL, and the id used in search filters. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to turn a university name into the school id that search/people accepts as a filter. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keyword. | | `page` | integer | no | 1-based page number. Defaults to 1. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Industry (/docs/api-reference/linkedin/search-industry) `GET https://api.insightsocial.app/v1/linkedin/search/industry` Returns LinkedIn industries matching a keyword, each with the industry name and the id LinkedIn uses to identify it. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to turn an industry name into the id that search/people and search/jobs need for their industry filters. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keyword. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › About (/docs/api-reference/linkedin/profile-about) `GET https://api.insightsocial.app/v1/linkedin/profile/about` Returns the month a LinkedIn member joined and how recently contact details and the profile photo were updated, plus identity verification when LinkedIn shows it for that member. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to judge how current a profile is. Verification is included only for members whose verification panel can be read. Values come back as relative text, not exact dates. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile, company, or post. | | `urn` | string | no | The member's opaque LinkedIn URN, as returned in `author.ext.urn` by /v1/linkedin/profile. Optional, and a pure optimisation: pass it ALONGSIDE `url` (which stays required) and this call skips the internal handle lookup, so it answers faster. Verified on the deployed API 05/09/2026. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Contact (/docs/api-reference/linkedin/profile-contact) `GET https://api.insightsocial.app/v1/linkedin/profile/contact` Returns the contact details a LinkedIn member exposes publicly: websites, phone numbers, address, WeChat, and Twitter, alongside their name and profile urn. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it when you need a way to reach a person; most fields come back empty unless the member filled them in, and no email address is returned. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile, company, or post. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Stats (/docs/api-reference/linkedin/profile-stats) `GET https://api.insightsocial.app/v1/linkedin/profile/stats` Returns a LinkedIn member's audience size: their follower count and their connection count. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it when all you need is reach numbers and you do not want to pull the whole profile. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn profile, company, or post. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Company › Job Count (/docs/api-reference/linkedin/company-job-count) `GET https://api.insightsocial.app/v1/linkedin/company/job-count` Returns the number of jobs a LinkedIn company currently has open. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to track hiring volume over time without paging through the full list in company/jobs. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `company_id` | string | yes | LinkedIn numeric company ID (from /v1/linkedin/company). | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Company › Insights (/docs/api-reference/linkedin/company-insights) `GET https://api.insightsocial.app/v1/linkedin/company/insights` Returns headcount breakdowns of a LinkedIn company's members as name and count pairs, covering locations, schools, job functions, skills, service categories, and fields of study. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it for a workforce profile of an employer without paging through staff; company/people lists the individual members. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `company_id` | string | yes | LinkedIn numeric company ID (from /v1/linkedin/company). | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Group (/docs/api-reference/linkedin/group) `GET https://api.insightsocial.app/v1/linkedin/group` Returns a LinkedIn group's profile: name, description, member count, posting rules, industries, owners, logo and cover images, and its public and active flags. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to size up a group and read its rules before pulling its feed with group/posts. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `group_id` | string | yes | LinkedIn numeric group ID. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Job (/docs/api-reference/linkedin/job) `GET https://api.insightsocial.app/v1/linkedin/job` Returns one LinkedIn job posting in full: title, hiring company, location, posting date, the job description text, and the Easy Apply flag. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it after search/jobs or company/jobs, passing a job id from those lists to read the complete description. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `id` | string | yes | LinkedIn job ID. | | `include_skills` | boolean | no | When true, include the job's required skills in `job.ext.skills`. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Company › Posts (/docs/api-reference/linkedin/company-posts) `GET https://api.insightsocial.app/v1/linkedin/company/posts` Returns the recent posts from a LinkedIn company page, each with text, author name, like, comment and share counts, media, and date. Use it for what a brand publishes; profile/posts covers an individual member and group/posts covers a group feed. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `company_id` | string | yes | LinkedIn numeric company ID (from /v1/linkedin/company). | | `page` | integer | no | Page number for pagination (default 1). | | `sort_by` | string | no | Post ordering: 'top' or 'recent'. One of: top, recent. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Ad (/docs/api-reference/linkedin/ad) `GET https://api.insightsocial.app/v1/linkedin/ad` Returns one LinkedIn ad from the Ad Library: the ad copy, creative images, the advertiser's name and logo, total impressions, and start date. Use it after ads/search, passing an ad's Ad Library URL to read that single ad in full. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn ad | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Ads › Search (/docs/api-reference/linkedin/ads-search) `GET https://api.insightsocial.app/v1/linkedin/ads/search` Returns ads from the LinkedIn Ad Library matching a company, keyword, country, or date range, with each ad's copy and the advertiser behind it. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to find what a competitor is running, then pass a result's ad URL to ad for the complete record. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `company` | string | no | The company name to search for. 'Microsoft' for example. Provide at least one of: `company`, `keyword`, `companyId`. | | `keyword` | string | no | The keyword to search for. Provide at least one of: `company`, `keyword`, `companyId`. | | `companyId` | string | no | The company id to search for. Provide at least one of: `company`, `keyword`, `companyId`. | | `countries` | string | no | Comma separated list of countries. Example: US,CA,MX | | `startDate` | string | no | Start date to search for. Format: YYYY-MM-DD | | `endDate` | string | no | End date to search for. Format: YYYY-MM-DD | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Posts › Archive (/docs/api-reference/linkedin/profile-posts-archive) `GET https://api.insightsocial.app/v1/linkedin/profile/posts/archive` Returns a LinkedIn member's deep post archive, read from their own feed rather than a search index, with the exact publish timestamp, media and the full engagement breakdown including share counts. It is metered per post returned, so ask for the limit you need. **Metered: 50–5000 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | The member's LinkedIn profile URL, for example https://www.linkedin.com/in/williamhgates/. | | `limit` | integer | no | How many posts to return, 1 to 100 (default 20). Pass 100 to walk the full archive: only a full page returns a `next_cursor`. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Posts (/docs/api-reference/linkedin/search-posts) `GET https://api.insightsocial.app/v1/linkedin/search/posts` Returns public LinkedIn posts and Pulse articles matching a keyword, with each result's text, author, media, like and comment counts, and date. Use it for broad keyword monitoring; it reads public search results, so treat coverage as best effort rather than complete. **Metered: 10–480 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | no | Keyword or phrase to search for. Optional when `from_member` or `from_company` is set: omit it to get that subject's posts unfiltered, and note that supplying a real content word narrows the results to posts matching it. Provide at least one of: `query`, `from_member`, `from_company`. | | `from_member` | string | no | Filter to posts from one member, by their LinkedIn member urn — the bare `ACoAA…` value published as `author.ext.urn` by /v1/linkedin/profile. A profile URL, a public slug (`williamhgates`) or a `urn:li:fsd_profile:`-prefixed value is rejected, because the source cannot use any of them. Pass this on its own, with no `query`, to walk one member's back catalogue. Provide at least one of: `query`, `from_member`, `from_company`. | | `from_company` | string | no | Filter to posts from a company, by numeric LinkedIn company id (for example `1035`). A company slug or URL is rejected; read the id from `author.id` on /v1/linkedin/company. Provide at least one of: `query`, `from_member`, `from_company`. | | `page` | integer | no | 1-based page number of Google results. Defaults to 1. | | `sort_by` | string | no | Result ordering: `relevance` (default) or `date_posted`. On a subject-only call (`from_member` or `from_company` with no `query`) the results are ordered by date and `relevance` is rejected, because there is no query for them to be relevant to. One of: date_posted, relevance. | | `date_posted` | string | no | Date filter based on Google-indexed results. One of `past_24h`, `past_week`, `past_month` (underscores, not hyphens). Note the JOB lanes use `past_24_hours` for the same concept; the two vocabularies are not interchangeable. One of: past_24h, past_week, past_month. | | `content_type` | string | no | Narrow to one kind of post. One of: videos, photos, jobs, live_videos, documents, collaborative_articles. | | `limit` | integer | no | Return up to this many posts in one call, 1 to 200. Repeating the identical call inside the 2-minute cache window is free. Requires `query`. Cannot be combined with `page` or `cursor`: the result always starts at the top and `pagination.next_cursor` is null. `pagination.has_more` says whether more posts were available when the call stopped, and `walk.stopped` says why it stopped (`limit`, `exhausted`, `deadline` or `sources_unavailable`). | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | --- # Post › Transcript (/docs/api-reference/linkedin/post-transcript) `GET https://api.insightsocial.app/v1/linkedin/post/transcript` Returns the spoken text of a video in a LinkedIn post. A post with no transcript answers 404 with reason no_captions, and a repost is read from the original post it shares. Use it to read or search what was said in a LinkedIn video; you are only charged when a transcript actually comes back. **100 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the LinkedIn post to transcribe. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Full (/docs/api-reference/linkedin/profile-full) `GET https://api.insightsocial.app/v1/linkedin/profile/full` Returns a LinkedIn company page and its latest 10 posts in one call. Use it instead of calling company and company/posts. **Beta.** A composite: `data.profile` is a schema-2 Profile and `data.posts[].post` are schema-2 Posts, and the posts page with `cursor`. The rest of `data` is the source's own structure under our naming rules (snake_case keys, string ids). Fields outside the profile and posts may change. See [Schema 2](/docs/schema-2). **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full LinkedIn company profile or page URL. One of the identity params is required. | | `posts` | integer | no | How many of the fetched recent posts to return and compute the metrics over (1-100, default 25). | | `cursor` | string | no | Pass a prior response's posts_cursor to read the next page of posts. | | `include` | string | no | | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Business (/docs/api-reference/nextdoor/business) `GET https://api.insightsocial.app/v1/nextdoor/business` Returns one local business listed on Nextdoor: name, phone, email, website, street address, categories and how many neighbours recommend it. Use it when you have a business page URL; to find businesses by city and trade use search. **30 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `slug` | string | yes | The last segment of the business page URL, nextdoor.com/pages//. | | `market` | string | no | us (nextdoor.com, default), gb (nextdoor.co.uk) or ca (ca.nextdoor.com). One of: us, gb, ca. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search (/docs/api-reference/nextdoor/search) `GET https://api.insightsocial.app/v1/nextdoor/search` Returns local businesses on Nextdoor for a city and trade, each with name, address, categories and, where listed, phone, email and website. Use it to build a local lead list; for one business you already know use business. **Metered: 30–170 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `city` | string | yes | City slug: the city and its state or region joined by two hyphens, lowercase, e.g. austin--tx or york--england. | | `query` | string | yes | The trade to look for, e.g. plumber, bakery, electrician. | | `limit` | integer | no | Businesses to return, 1 to 20. Default 20. Billed per business returned. | | `market` | string | no | us (nextdoor.com, default), gb (nextdoor.co.uk) or ca (ca.nextdoor.com). One of: us, gb, ca. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search (/docs/api-reference/pinterest/search) `GET https://api.insightsocial.app/v1/pinterest/search` Returns Pinterest pins matching a keyword, each with its text, original image, pinner and pin date; include=engagement adds save, reaction, comment and share counts. Use it to discover pins on a topic; add include=engagement for each pin's saves, or call pin when you already have one pin URL. **Metered: 10–260 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Keyword or phrase to search Pinterest pins for. | | `cursor` | string | no | Cursor to get the next page. Take it from `pagination.next_cursor` on the previous response. | | `trim` | boolean | no | Set to true for a trimmed down version of the response | | `include` | string | no | Set to `engagement` (one token only) to fill `post.engagement.saves`, `.likes` (reactions), `.comments` and `.shares` on every row in this one call. One of: engagement. | | `limit` | integer | no | Take the top N rows of the page (1 to 25) after the search has run. It is not a page size: `next_cursor` still advances past the full page, so rows beyond N on this page are not returned by the next page. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Pin (/docs/api-reference/pinterest/pin) `GET https://api.insightsocial.app/v1/pinterest/pin` Returns one Pinterest pin: its text, original image, pinner, pin date, and its save, reaction, comment and share counts. Use it when you have a pin URL and want everything about that single pin rather than a list. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the Pinterest pin | | `trim` | boolean | no | Set to true for a trimmed down version of the response | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Url Stats (/docs/api-reference/pinterest/url-stats) `GET https://api.insightsocial.app/v1/pinterest/url-stats` Returns how many times each of up to 10 external URLs has been saved to Pinterest through the Save Button. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to measure a page's pull on Pinterest, passing URLs exactly as used, since http vs https and a trailing slash count as different URLs. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `urls` | string | yes | Comma-separated list of 1-10 absolute http(s):// URLs, passed to Pinterest verbatim. Variants (https vs http, with/without trailing slash, with/without query string) are counted as different URLs. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Board (/docs/api-reference/pinterest/board) `GET https://api.insightsocial.app/v1/pinterest/board` Returns the pins on a Pinterest board with text, image URL, author, and save, comment and share counts; include=engagement adds each pin's date and reaction count. Use it when you have a board URL: about 15 pins a page, cursor for the rest, include=engagement for pin dates. user/boards finds the boards. **Metered: 10–160 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the Pinterest board | | `trim` | boolean | no | Set to true for a trimmed down version of the response | | `cursor` | string | no | Cursor to get the next page of pins. Take it from `pagination.next_cursor` on the previous response. | | `include` | string | no | Set to `engagement` (one token only) to fill `post.published_at` and `post.engagement.likes` (plus any save, comment or share count the board row lacked) on every row in this one call. One of: engagement. | | `limit` | integer | no | Take the top N pins of the page (1 to 15). It is not a page size: `next_cursor` still advances past the full page, so pins beyond N on this page are not returned by the next page. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # User › Boards (/docs/api-reference/pinterest/user-boards) `GET https://api.insightsocial.app/v1/pinterest/user/boards` Returns the boards a Pinterest user has created, each with its title, description, pin count, and cover image. Use it to list someone's boards from their handle, then pass a board URL to board to get that board's pins. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | The username of the user to get boards for. (e.g. broadstbullycom from https://www.pinterest.com/broadstbullycom/) | | `trim` | boolean | no | Set to true for a trimmed down version of the response | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Trends (/docs/api-reference/pinterest/trends) `GET https://api.insightsocial.app/v1/pinterest/trends` Returns the Pinterest Trends list for one country: ranked search terms with a relative search index, for growing, seasonal, monthly or yearly tables. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it when you want what a market is searching for on Pinterest right now, with no keyword to start from. **100 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `country` | string | no | ISO country code, in any case: AR, AU, BR, CA, CO, DE, EG, ES, FR, GB, ID, IE, IN, IT, KR, MX, MY, NZ, PH, SA, TH, TR, US. Pinterest publishes no list for other countries, and one of them is a free 400. IE reads Pinterest's GB+IE list and AU and NZ read AU+NZ; GB alone has its own list. Defaults to US. One of: AR, AU, BR, CA, CO, DE, EG, ES, FR, GB, ID, IE, IN, IT, KR, MX, MY, NZ, PH, SA, TH, TR, US. | | `type` | string | no | Which Pinterest Trends table: growing (the default), seasonal, top_monthly or top_yearly. One of: growing, seasonal, top_monthly, top_yearly. | | `limit` | integer | no | How many terms to return, from the top, 1 to 50. Defaults to 25. The price is the same at any depth. | | `include` | string | no | Keep only terms containing one of these keywords, comma-separated (up to 10, e.g. nails,wedding). | | `exclude` | string | no | Drop terms containing any of these keywords, comma-separated (up to 10, e.g. fall). | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search (/docs/api-reference/quora/search) `GET https://api.insightsocial.app/v1/quora/search` Returns Quora questions matching a keyword: title, URL, answer count, follower count, and publish time. Use it to find questions on a topic; use answers to read written answers on the same topic. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keywords, for example learn programming. | | `time` | string | no | Recency window: all, hour, day, week, month, or year. Default all. One of: all, hour, day, week, month, year. | | `limit` | integer | no | Maximum rows to return, 1 to 10. Default 10. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post (/docs/api-reference/quora/post) `GET https://api.insightsocial.app/v1/quora/post` Returns one Quora question: title, body text, and published date. Quora questions rarely carry a separate body beyond the title. Use it when you have a question URL; use answers to find written answers on that topic by keyword. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the Quora question, for example https://www.quora.com/What-is-the-best-alternative-to-SendGrid-Email-API. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Answers (/docs/api-reference/quora/answers) `GET https://api.insightsocial.app/v1/quora/answers` Returns Quora answers matching a keyword: the answer text, author name and credential, upvotes, views, and the parent question title. Use it when you want written answers on a topic; use search for the questions themselves. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keywords, for example learn programming. | | `time` | string | no | Recency window: all, hour, day, week, month, or year. Default all. One of: all, hour, day, week, month, year. | | `limit` | integer | no | Maximum rows to return, 1 to 10. Default 10. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Posts (/docs/api-reference/quora/posts) `GET https://api.insightsocial.app/v1/quora/posts` Returns posts from Quora Spaces matching a keyword: URL, author, upvotes, comments, and publish time. Body text is often absent on this surface. Use it for Quora Space posts rather than Q&A content; use answers for written answers to questions. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keywords. | | `time` | string | no | Recency window: all, hour, day, week, month, or year. Default all. One of: all, hour, day, week, month, year. | | `limit` | integer | no | Maximum rows to return, 1 to 10. Default 10. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profiles (/docs/api-reference/quora/profiles) `GET https://api.insightsocial.app/v1/quora/profiles` Returns Quora profiles matching a keyword: display name, profile URL, credential, follower count, and avatar. Use it to find a person by name; use answers or posts to read what they have written. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keywords, for example a topic or a person's name. | | `time` | string | no | Recency window: all, hour, day, week, month, or year. Default all. One of: all, hour, day, week, month, year. | | `limit` | integer | no | Maximum rows to return, 1 to 10. Default 10. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Spaces (/docs/api-reference/quora/spaces) `GET https://api.insightsocial.app/v1/quora/spaces` Returns Quora Spaces matching a keyword: name, URL, description, member count, and icon. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to find Quora Spaces, topic communities, by name; use posts for what has been posted inside one. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keywords. | | `time` | string | no | Recency window: all, hour, day, week, month, or year. Default all. One of: all, hour, day, week, month, year. | | `limit` | integer | no | Maximum rows to return, 1 to 10. Default 10. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Topics (/docs/api-reference/quora/topics) `GET https://api.insightsocial.app/v1/quora/topics` Returns Quora topics matching a keyword: name, URL, follower count, and topic photo. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to find a topic page by name; use search to find the questions tagged under it. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keywords. | | `time` | string | no | Recency window: all, hour, day, week, month, or year. Default all. One of: all, hour, day, week, month, year. | | `limit` | integer | no | Maximum rows to return, 1 to 10. Default 10. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Subreddit (/docs/api-reference/reddit/subreddit) `GET https://api.insightsocial.app/v1/reddit/subreddit` Returns posts from a subreddit, each with title, body, score, comment count, author, permalink, and creation timestamp. Use it to read a community's feed, noting timeframe works only with sort=top, which is auto-selected when you omit sort; subreddit/search filters that same community by keyword. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `subreddit` | string | yes | Subreddit name without the r/ prefix | | `timeframe` | string | no | Timeframe to get posts from. Applied with sort=top (auto-selected when you omit sort). One of: all, day, week, month, year. | | `sort` | string | no | Sort order One of: best, hot, new, top, rising. | | `trim` | boolean | no | Set to true for a trimmed down version of the response. This also skips the enrichment pass, so `author.avatar_url`, `content.media_urls`, `flags.spoiler`, `post.url` and the `ext.*` fields are omitted along with the rest of the trimmed leaves. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | --- # Subreddit › Details (/docs/api-reference/reddit/subreddit-details) `GET https://api.insightsocial.app/v1/reddit/subreddit/details` Returns a subreddit's own details: subscriber count, active user count, description, creation date, rules, and icon URL. Use it to size up a community before pulling its posts; the name is case-sensitive here, so spell it exactly as Reddit does. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `subreddit` | string | no | Subreddit name without the r/ prefix. Case-sensitive: use the subreddit's canonical casing (e.g. `AskReddit`). Provide at least one of: `subreddit`, `url`. | | `url` | string | no | Subreddit URL. Provide at least one of: `subreddit`, `url`. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search (/docs/api-reference/reddit/search) `GET https://api.insightsocial.app/v1/reddit/search` Returns posts matching a keyword from across Reddit, each with title, score, comment count, subreddit, permalink, and the body at ext.selftext. Use it to search all of Reddit at once. Bodies arrive free here, so include_body=true is rarely needed and refunds what it does not spend. subreddit/search searches inside one community. **Metered: 10–300 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search words or a phrase. Words match any, not all: vision pro also returns posts that mention only one of them. Wrap a phrase in double quotes to match it exactly (query="vision pro"). Reddit operators such as subreddit:name work too. | | `sort` | string | no | Sort by One of: relevance, new, top, comment_count. | | `timeframe` | string | no | Only return posts from this window. Applied on sort=relevance, sort=top and sort=comment_count. It has no effect with sort=new, which is already ordered newest-first and returns recent posts regardless. One of: all, day, week, month, year. | | `trim` | boolean | no | Set to true for a trimmed down version of the response. On this endpoint it also skips the second source that widens the page, so a trimmed page is both thinner and usually shorter. Measured 09/09/2026: default 29 rows, `trim=true` 25 rows. It does not drop `author.avatar_url` or `ext.*` — those leaves stay when the first source already carried them. Use it to skip the widening pass, not to strip fields. | | `include_body` | boolean | no | Set to true to include each post's body (selftext) on the search page. Rarely needed now: bodies arrive on the rows themselves. Link posts have no body, so they are looked up and not charged for. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `max_age_days` | integer | no | Keep only rows published within this many days (`post.published_at`), 1 to 3650. A row with no date is discarded. | | `max_pages` | integer | no | 1 to 5 (default 1). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed as one call to this endpoint; the whole walk counts once against your rate limit. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | --- # Post (/docs/api-reference/reddit/post) `GET https://api.insightsocial.app/v1/reddit/post` Returns one post from its URL including the body text, plus score, comment count, share count, author, author avatar, thumbnail, and creation timestamp. Use it when you have a post URL and want that one post's body. Search rows carry their own bodies now, so you rarely need to fan out per post. post/comments returns the discussion. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the Reddit post | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post › Comments (/docs/api-reference/reddit/post-comments) `GET https://api.insightsocial.app/v1/reddit/post/comments` Returns the full comment tree for a post, each comment with author, body, score, direct-reply count, nesting depth, timestamp, and its nested replies. Use it when you want the whole discussion rather than only top-level comments. Read data.truncated: true means the thread is incomplete, so keep paging while a cursor comes back. **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the Reddit post to fetch comments for | | `cursor` | string | no | Cursor to get more comments, or replies. | | `trim` | boolean | no | Accepted, but not honoured on this endpoint: the source that serves it does not offer a trimmed shape, so the response is the same either way. Kept because removing an accepted parameter would break existing calls. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | --- # Subreddit › Search (/docs/api-reference/reddit/subreddit-search) `GET https://api.insightsocial.app/v1/reddit/subreddit/search` Returns posts matching a query inside one subreddit, each with title, score, comment count, and permalink. This source sends no post body, so ext.selftext is null without include_body. Use it to search one community. Bodies cost extra here, so prefer search with query=subreddit:name plus your terms: bodies for one credit, though it returns a different set of rows. **Metered: 10–260 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `subreddit` | string | yes | Subreddit name (e.g. 'Fitness', not 'r/Fitness' or a full URL) | | `query` | string | no | Search query to find matching content | | `sort` | string | no | Sort order. For posts/media: relevance, hot, top, new, comments. For comments: relevance, top, new One of: relevance, hot, top, new, comments. | | `timeframe` | string | no | Only return posts from this window. Applied on sort=relevance, sort=top and sort=comments. It has no effect with sort=new, which is already ordered newest-first and returns recent posts regardless. One of: all, year, month, week, day, hour. | | `cursor` | string | no | Cursor to get more results. Get 'cursor' from previous response. | | `include_body` | boolean | no | Set to true to include each post's body (selftext) on the search page. Link posts have no body, so they are looked up and not charged for. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile (/docs/api-reference/reddit/profile) `GET https://api.insightsocial.app/v1/reddit/profile` Returns one Reddit account: total karma, the post/comment/award karma split, cake day, bio, avatar, banner, trophy count and profile title. Use it to size or vet an account before reading its posts. author.followers is null on purpose: Reddit publishes no public follower count, so karma is the reach signal. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Reddit username without the u/ prefix. Case-insensitive. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Posts (/docs/api-reference/reddit/profile-posts) `GET https://api.insightsocial.app/v1/reddit/profile/posts` Returns the posts one account has submitted, newest first, with the body at ext.selftext, score, comment count, upvote ratio, flair, media and ext.subreddit. Use it to read one person's submissions across communities. An account with no posts and one that does not exist both return an empty list, so call profile to tell them apart. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Reddit username without the u/ prefix. Case-insensitive. | | `sort` | string | no | Sort order for the account's submissions. `top` defaults to the all-time window; pass `timeframe` to narrow it. One of: hot, new, top. | | `timeframe` | string | no | Only return posts from this window. Applied on `sort=top`. Omitted `sort=top` is treated as `timeframe=all` so the documented call is not an empty page. One of: all, year, month, week, day, hour. | | `cursor` | string | no | Cursor to get more results. Get 'cursor' from the previous response. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Comments (/docs/api-reference/reddit/profile-comments) `GET https://api.insightsocial.app/v1/reddit/profile/comments` Returns an account's own comment history, newest first, each with the text, score, permalink, parent_id, the post it sits under and ext.subreddit. Use it when you need a real comment history rather than a sample. **Metered: 20–2000 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Reddit username without the u/ prefix. Case-insensitive. | | `limit` | integer | no | How many comments to return, 1-100, default 25. This is a depth control, not a page size: a higher limit reaches further back into the account's history at the same speed. | | `before` | string | no | Only return comments older than this date (YYYY-MM-DD). Use it to walk a long history in windows instead of raising limit. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Comments (/docs/api-reference/reddit/search-comments) `GET https://api.insightsocial.app/v1/reddit/search/comments` Returns comments matching a phrase from Reddit's comment index, each with its parent post inline: ext.post_title, ext.subreddit, ext.post_score and ext.post_url. Use it when the phrase lives in a reply rather than a title, and for author:username or subreddit:name sweeps. parent_id is null: the platform does not report thread position. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search phrase. Reddit's operators work here: `author:username` for one account's comments, `subreddit:name terms` to scope to a community, and quotes for an exact phrase. | | `sort` | string | no | Sort order for the comment hits. One of: relevance, top, new. | | `cursor` | string | no | Cursor to get more results. Get 'cursor' from the previous response. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Subreddits › Search (/docs/api-reference/reddit/subreddits-search) `GET https://api.insightsocial.app/v1/reddit/subreddits/search` Returns up to 25 communities matching a topic, each with the name at author.id, subscriber count, description and icon. include=details adds the creation date, weekly activity, rules and language. Use it to find which communities discuss a subject. Send include=details instead of one subreddit/details call per community, plus 1 per row filled, 26 at most. **Metered: 10–260 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Topic or keyword to find communities for. | | `cursor` | string | no | Cursor to get more results. Get 'cursor' from the previous response. | | `include` | string | no | Set to `details` (one token only) to fill `author.joined_at`, `author.ext.weekly_active_users`, `author.ext.weekly_contributions`, `author.ext.rules_text` and `author.ext.language` on every row in this one call (a row the details lookup's main source cannot answer gets only the creation date and the language, and is still billed). One of: details. | | `limit` | integer | no | Take the top N communities of the page (1 to 25). It is not a page size: the cursor still advances past the whole page, so rows beyond N are not returned by the next page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Media (/docs/api-reference/reddit/search-media) `GET https://api.insightsocial.app/v1/reddit/search/media` Returns up to 25 Reddit posts that carry an image, video or gallery, with the media at content.media_urls plus title, score, comment count and subreddit. Use it for visual posts on a topic without filtering a mixed page yourself. subreddit:name terms scopes it to one community; timeframe has no effect on sort=new. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search phrase. Reddit's operators work here: `subreddit:name terms` scopes the sweep to one community, and quotes force an exact phrase. | | `sort` | string | no | Sort by One of: relevance, new, top, comment_count. | | `timeframe` | string | no | Only return posts from this window. Applied on sort=relevance, sort=top and sort=comment_count. It has no effect with sort=new, which is already ordered newest-first. One of: all, day, week, month, year. | | `cursor` | string | no | Cursor to get more results. Get 'cursor' from the previous response. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post › Transcript (/docs/api-reference/reddit/post-transcript) `GET https://api.insightsocial.app/v1/reddit/post/transcript` Returns the transcript of a Reddit video post, both the raw caption file and a plain-text version, when Reddit publishes captions for it. Use it for video posts; when Reddit exposes no captions the transcript comes back empty and flagged rather than as an error. **100 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Reddit post URL or direct v.redd.it video URL. | | `language` | string | no | 2-letter language code. Defaults to `en`. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Omni Search (/docs/api-reference/reddit/omni-search) `GET https://api.insightsocial.app/v1/reddit/omni-search` Returns threads from across Reddit for one keyword with their top comments inline at top_comments, plus a roll-up of which subreddits are talking, their weekly active users and tone. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it for a customer-listening sweep in one call rather than running search then post/comments per thread; it is slow and relevance is loose. **Metered: 50–90 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Keyword or phrase to sweep across Reddit. | | `threads` | integer | no | How many top threads to expand comments for (1-8, default 8). | | `sort` | string | no | Search sort order (relevance \| new \| top \| comment_count). One of: relevance, new, top, comment_count. | | `timeframe` | string | no | Time window for the search (all \| day \| week \| month \| year). One of: all, day, week, month, year. | | `subreddit` | string | no | Scope the sweep to one subreddit (bare name, no r/ prefix). | | `cursor` | string | no | Opaque cursor from a prior response's next_cursor to page deeper. | | `include` | string | no | CSV subset of subreddits,comments (default both). The comments block is returned as `threads[].top_comments`. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search (/docs/api-reference/rumble/search) `GET https://api.insightsocial.app/v1/rumble/search` Returns Rumble videos and shorts matching a keyword: title, URL, thumbnail, channel, published date, and view count. Use it to find videos by keyword; use channel/videos when you already know the channel. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search query. | | `cursor` | string | no | Cursor from the previous response: the next page number (e.g. `2`). | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Channel › Videos (/docs/api-reference/rumble/channel-videos) `GET https://api.insightsocial.app/v1/rumble/channel/videos` Returns a Rumble channel's metadata and a page of its videos and shorts. Use it when you have a channel handle or URL and want its videos; use search to find videos across Rumble by keyword. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | no | Rumble channel handle. Provide at least one of: `handle`, `url`. | | `url` | string | no | Rumble channel URL. Provide at least one of: `handle`, `url`. | | `cursor` | string | no | Cursor from the previous response: the next page number (e.g. `2`). | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Video (/docs/api-reference/rumble/video) `GET https://api.insightsocial.app/v1/rumble/video` Returns one Rumble video's title, description, thumbnail, channel, publish date, view count, likes, dislikes, captions, and media metadata. Use it when you have a single video URL and need its full details; use video/comments for the discussion, or video/transcript for the spoken words. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Rumble video URL. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Video › Transcript (/docs/api-reference/rumble/video-transcript) `GET https://api.insightsocial.app/v1/rumble/video/transcript` Returns the transcript of a Rumble video when captions are available. If Rumble exposes no captions, transcript is null and nothing is charged. Use it when you need what was said in a video rather than its metadata; use video for the title, description, and engagement counts. **100 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Rumble video URL. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Video › Comments (/docs/api-reference/rumble/video-comments) `GET https://api.insightsocial.app/v1/rumble/video/comments` Returns the top-level comments on a Rumble video: comment text, author, created time, like and dislike counts, and reply count. Use it to read a video's comments; use video for the video's own details. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Rumble video URL. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile (/docs/api-reference/snapchat/profile) `GET https://api.insightsocial.app/v1/snapchat/profile` Returns a Snapchat user's public profile: display name, Bitmoji avatar URL, subscriber count, and bio description. Use it for a quick profile check; use spotlight/comments when you have a Spotlight snap URL instead. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Snapchat username | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Spotlight › Comments (/docs/api-reference/snapchat/spotlight-comments) `GET https://api.insightsocial.app/v1/snapchat/spotlight/comments` Returns comments on a Snapchat Spotlight snap: text, display name, reaction count, nested reply count, and timestamp. Use it when you have a Spotlight snap URL and want its comments; use profile for the poster's own public info. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the Snapchat Spotlight snap. | | `cursor` | string | no | Cursor from the previous response to fetch the next page. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Posts (/docs/api-reference/substack/posts) `GET https://api.insightsocial.app/v1/substack/posts` Returns a publication’s recent posts, or one post: title, full text and Markdown, author, reactions, comments, restacks, word count and whether it is paid. Use it with a publication URL (or custom domain) for its archive, or a /p/ post URL for one post. **Metered: 10–120 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | A Substack publication URL or custom domain, or one post URL (…/p/slug). | | `limit` | integer | no | Most recent posts to read, 1 to 100. Default 25. Billed per post returned. | | `start_date` | string | no | Only posts published on or after this date (YYYY-MM-DD). Applied to the `limit` most recent posts, so raise `limit` to reach further back. | | `end_date` | string | no | Only posts published on or before this date (YYYY-MM-DD). Applied to the `limit` most recent posts, so raise `limit` to reach further back. | | `only_free` | boolean | no | true for free (non-paywalled) posts only. | | `content_type` | string | no | Only one post type. Default all. One of: all, newsletter, podcast, thread. | | `min_comments` | integer | no | Only posts with at least this many comments. | | `min_reactions` | integer | no | Only posts with at least this many reactions. | | `min_word_count` | integer | no | Only posts with at least this many words. | | `include_content` | boolean | no | Include the article body (text and Markdown). Default true; false is faster. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile (/docs/api-reference/telegram/profile) `GET https://api.insightsocial.app/v1/telegram/profile` Returns a public Telegram channel or group's profile: display name, description, avatar URL, verification status, and subscriber count. Use it for a channel's subscriber count and basic info; use profile/posts for its recent posts. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Public Telegram handle, with or without the leading @, or a t.me channel URL | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Posts (/docs/api-reference/telegram/profile-posts) `GET https://api.insightsocial.app/v1/telegram/profile/posts` Returns one page of recent public posts from a Telegram channel or group, each with its text, publish time, view count, reaction breakdown, and media URLs. Use it to list a channel's recent posts; use post when you already have a single post's URL. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Public Telegram handle, with or without the leading @, or a t.me channel URL | | `cursor` | string | no | Cursor from the previous page's pagination block. Omit it for the most recent posts. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post (/docs/api-reference/telegram/post) `GET https://api.insightsocial.app/v1/telegram/post` Returns one public Telegram post: text, publish time, view count, the per-emoji reaction breakdown, and direct media URLs. Use it when you have a single t.me post URL; use profile/posts to list a channel's recent posts instead. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Public Telegram post URL, in the form https://t.me/{handle}/{id} | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile (/docs/api-reference/threads/profile) `GET https://api.insightsocial.app/v1/threads/profile` Returns a Threads account's public profile: bio, the external link set in that bio, follower count, profile picture URL, and verification status. Use it when you have a handle and want the account itself; user/posts returns what that account has posted. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Threads username without the @ symbol | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # User › Posts (/docs/api-reference/threads/user-posts) `GET https://api.insightsocial.app/v1/threads/user/posts` Returns a Threads user's most recent posts, usually about 15. Send limit above 15 (up to 50) to collect more of the same feed, with view counts and display names filled in. Use it for a recent-activity window of about 15 posts. Set limit above 15 for deeper history on the same feed, or use search for a keyword lookup. **Metered: 10–1650 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Threads username without the @ symbol | | `since` | string | no | Only posts published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Older posts are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Pinned posts sit out of date order and never end the walk. The page still costs what a page costs, so a daily poll pays for the pages it walks and no more. | | `stop_at_id` | string | no | The id (`post.id`) or URL (`post.url`) of the newest post you already hold. The page stops just before it: that post and everything after it are left off, `next_cursor` is not returned, and `pagination.stopped_at` is `known_id`. A pinned post never counts as the stop point. If it is not on this page the page is returned in full with its cursor, so keep walking. `pagination.stopped_at` is `end` when the list ran out first and `null` while there is more to walk. | | `trim` | boolean | no | Ask for a stripped record. This is a real shape change, not just less whitespace: a trimmed row carries only the id, text, shortcode, like count, timestamp and author, so media, reply count, share count, view count, topic tag, pinned flag and quoted post all come back null. Ignored when `limit` is above 15, because the deeper source has no trimmed mode. | | `limit` | integer | no | How many posts to collect, 1 to 50. Leave it off, or send 15 or less, and nothing changes: you get the bundled window. | | `include` | string | no | Send `engagement` to fill `engagement.views` and `post.author.display_name` on each post of the default window by looking it up on `/v1/threads/post` in the same call. Adds 4 to 12 seconds, never more than 15. One token only: `engagement`. One of: engagement. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post (/docs/api-reference/threads/post) `GET https://api.insightsocial.app/v1/threads/post` Returns one Threads post's text, like, reply and repost counts, every carousel slide or video URL, author, creation time, and topic tag at post.ext.topic_tag. Use it when you have a post URL and want that single post rather than a whole feed. If it is a quote post, the quoted post arrives at post.ext.quoted_post. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the Threads post, for example https://www.threads.com/@zuck/post/DZpPDXbCeTt. Both threads.com and threads.net are accepted. | | `trim` | boolean | no | Ask for a stripped record. This is a real shape change, not just less whitespace: a trimmed row carries only the id, text, shortcode, like count, timestamp and author, so media, reply count, share count, view count, topic tag, pinned flag and quoted post all come back null. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search (/docs/api-reference/threads/search) `GET https://api.insightsocial.app/v1/threads/search` Returns Threads posts matching a keyword, each with its text, like count, author, creation time, and the topic tag it was filed under at post.ext.topic_tag, plus a cursor for the next window. Use it to find posts by topic across Threads. Follow pagination.next_cursor to page deeper, or set limit (up to 100) to collect several windows in one call; start_date and end_date bound the period. **Metered: 10–340 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keyword or phrase to find Threads posts | | `start_date` | string | no | Inclusive start of the search period (YYYY-MM-DD). Pagination never walks past it, and a relaxed query drops any post older than it. Threads treats the date pair as a ranking hint rather than a filter, so the window is enforced on our side: a post we cannot date is treated as outside it. | | `end_date` | string | no | Inclusive end of the search period (YYYY-MM-DD). Pagination starts here and walks back in time, and a relaxed query drops any post newer than it. | | `trim` | boolean | no | Ask for a stripped record. This is a real shape change, not just less whitespace: a trimmed row carries only the id, text, shortcode, like count, timestamp and author, so media, reply count, share count, view count, topic tag, pinned flag and quoted post all come back null. | | `cursor` | string | no | Opaque cursor from a prior response's pagination.next_cursor, passed back verbatim, to fetch the next (older) result window. It carries your date bounds, so paging cannot escape the period you asked for. A request that sends a cursor is never query-relaxed. | | `limit` | integer | no | Collect AT LEAST this many unique posts in one call (1-100). This is a collection target, not a page size: the API walks whole result windows server-side until it has collected this many (or the query runs dry), so the response usually carries somewhat more than you asked for and never fewer unless the query ran out. Nothing you paid for is trimmed away. Omit for a single window. BUDGET FOR THE WALL CLOCK: the windows run one after another, at roughly 3.4 seconds each, so a high limit is a long request. Measured on production 07/09/2026, cache-cold: no limit 3.5s for 20 posts, limit=30 7.3s for 37, limit=50 10.2s for 55, limit=100 19.5s for 111. If your HTTP client defaults to a 10-second timeout, limit=50 and above will not fit inside it. Either raise the client timeout, or ask for a smaller limit and page with pagination.next_cursor, which costs the same credits for the same posts. | | `expand` | boolean | no | Set to false to search the exact phrase only. Ignored on a request that carries a cursor. | | `include` | string | no | Send `engagement` to fill `engagement.views` and `post.flags.pinned` on the first 20 posts by looking each one up on `/v1/threads/post` in the same call. Adds 3 to 12 seconds, never more than 12. One token only: `engagement`. Not accepted with a `limit` above 20; page with the cursor for more. One of: engagement. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `max_pages` | integer | no | 1 to 5 (default 1). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed as one call to this endpoint; the whole walk counts once against your rate limit. | --- # Post › Comments (/docs/api-reference/threads/post-comments) `GET https://api.insightsocial.app/v1/threads/post/comments` Returns the replies Threads bundles with a post, usually about 20. Send limit above 25 (up to 50) to collect more first-level replies from a second source. Use it to read the reaction under a post you already have the URL for. It is one window with no cursor. Set limit above 25 when the bundled window is not enough. **Metered: 10–100 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the Threads post whose replies you want, for example https://www.threads.com/@zuck/post/DZpPDXbCeTt. Both threads.com and threads.net are accepted. | | `trim` | boolean | no | Ask for a stripped record. This is a real shape change, not just less whitespace: a trimmed row carries only the id, text, shortcode, like count, timestamp and author, so media, reply count, share count, view count, topic tag, pinned flag and quoted post all come back null. Ignored when `limit` is above 25, because the deeper source has no trimmed mode. | | `limit` | integer | no | How many replies to collect, 1 to 50. Leave it off, or send 25 or less, and nothing changes: you get the bundled window. Threads publishes only part of a large conversation without a login, so a post declaring a thousand replies will still return about 50. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Users (/docs/api-reference/threads/search-users) `GET https://api.insightsocial.app/v1/threads/search/users` Returns Threads accounts matching a query, with handle, display name, avatar and verification status. Follower count, bio and the private flag stay null unless you send include=profile. Use it to find accounts by name or topic; search looks for posts instead of people. Send include=profile when you need follower count and bio on the same rows, per account filled. **Metered: 10–130 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keyword or phrase to find Threads users | | `include` | string | no | Send `profile` to fill `author.followers`, `author.bio`, `author.private` and `external_url` on each account by looking it up on `/v1/threads/profile` in the same call. Adds 3 to 6 seconds, never more than 12. One token only: `profile`. One of: profile. | | `limit` | integer | no | Take the top N accounts of the page, 1 to 12. | | `include_details` | boolean | no | Prefer `include=profile` in new code. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile (/docs/api-reference/tiktok/profile) `GET https://api.insightsocial.app/v1/tiktok/profile` Returns a TikTok account's public profile: display name, bio, follower count on author.followers (or the rounded figure with followers_approximate), likes, verification, and user id. Use it when you have a handle and want a quick snapshot of an account before pulling its videos or followers. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | no | TikTok username without the @ symbol. Provide at least one of: `handle`, `user_id`. | | `user_id` | string | no | TikTok numeric user ID. Use this for faster responses. Provide at least one of: `handle`, `user_id`. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Videos (/docs/api-reference/tiktok/profile-videos) `GET https://api.insightsocial.app/v1/tiktok/profile/videos` Returns a page of an account's recent public videos, each with caption, view, like, comment and share counts, and a thumbnail URL. Handle lookup covers login or age-walled profiles. Use it to list what one account has posted and page through it. Results come back in the source order, so sort_by does not reorder them. To find videos from many accounts by keyword, use search. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | no | TikTok username without the @ symbol. Provide at least one of: `handle`, `user_id`. | | `user_id` | string | no | TikTok user id. Use this for faster responses. Provide at least one of: `handle`, `user_id`. | | `since` | string | no | Only videos published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Older videos are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Pinned videos sit out of date order and never end the walk. The page still costs what a page costs, so a daily poll pays for the pages it walks and no more. | | `stop_at_id` | string | no | The id (`post.id`) or URL (`post.url`) of the newest video you already hold. The page stops just before it: that video and everything after it are left off, `next_cursor` is not returned, and `pagination.stopped_at` is `known_id`. A pinned video never counts as the stop point. If it is not on this page the page is returned in full with its cursor, so keep walking. `pagination.stopped_at` is `end` when the list ran out first and `null` while there is more to walk. | | `sort_by` | string | no | Accepted for compatibility and ignored on this endpoint: neither `latest` nor `popular` reorders the page. Videos come back in the source's own order, described above. To rank a creator's videos by views or likes, page through with `max_cursor` and sort the collected rows yourself. One of: latest, popular. | | `region` | string | no | Region (Country) you want the proxy in. Defaults to US. It does not filter the videos. The country TikTok registers each video to is post.ext.region. | | `trim` | boolean | no | Accepted for compatibility; the response is already the canonical shape, so this flag has no effect. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | --- # Post (/docs/api-reference/tiktok/post) `GET https://api.insightsocial.app/v1/tiktok/post` Returns one TikTok video in full: caption, engagement counts, author, sound, video metadata, and any on-screen text the creator typed with TikTok's text tool (post.ext.on_screen_texts). Use it when you already have a video URL and want that single video's numbers, rather than a list from profile/videos or search. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the TikTok video | | `region` | string | no | Two-letter country code for the proxy region (US, GB, FR, PH). The primary source does not take a region, so it is honoured only when a fallback source serves the request; a region-locked video (commonly from the Philippines) that fails on the primary is retried on the fallback with this value. | | `trim` | boolean | no | Accepted for compatibility; the response is already the canonical shape, so this flag has no effect. | | `download_media` | boolean | no | Set to true to also download the video/images and get back durable hosted media URLs under `data.post.ext.download_media_urls` (`[{ post_id, cdn_url, type, cached }]`). Use these for archiving; the raw `media_urls` are short-lived signed CDN links that expire. Adds a few seconds of latency while the media is fetched. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post › Comments (/docs/api-reference/tiktok/post-comments) `GET https://api.insightsocial.app/v1/tiktok/post/comments` Returns a page of comments on one TikTok video, each with the commenter's username, comment text, like count, reply count, and timestamp. Use it to read a video's comment section; to expand one thread, take a comment's id and call video/comment/replies. **Metered: 10–30 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the TikTok video to fetch comments for | | `cursor` | string | no | Cursor to get more comments. Get 'cursor' from previous response. | | `trim` | boolean | no | Accepted for compatibility; the response is already the canonical shape, so this flag has no effect. | | `sort` | string | no | Optional. Without it, comments come in TikTok's own order (a relevance ranking, not newest first). `recent` returns them newest first by published_at; `top` returns them by like count. TikTok has no newest-first order of its own, so `recent` sorts the comments that were read: set scan_pages to read more of the thread. One of: top, recent. | | `scan_pages` | integer | no | Optional, 1 to 3, default 1. Reads that many pages, drops comments repeated across pages, and returns them all in the `sort` order (TikTok's own order when sort is absent). | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | --- # Video › Comment › Replies (/docs/api-reference/tiktok/video-comment-replies) `GET https://api.insightsocial.app/v1/tiktok/video/comment/replies` Returns the replies under a single TikTok comment, with each reply's text, author, likes, and timestamps. Use it after post/comments: pass the comment_id it returned together with the same video url to expand one thread. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `comment_id` | string | yes | TikTok comment ID. This is the cid from the comments endpoint. | | `url` | string | yes | TikTok video URL. This is the url from the comments endpoint. | | `cursor` | integer | no | Cursor to get more replies. Get 'cursor' from previous response. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | --- # Comment (/docs/api-reference/tiktok/comment) `GET https://api.insightsocial.app/v1/tiktok/comment` Returns one TikTok comment with its current like count, reply count, pinned flag, author, and timestamp, found by link, by id, or by its text. Use it to re-check a single known comment without paging the whole section; use post/comments when you want the section itself. **Metered: 20–60 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `comment_url` | string | no | A TikTok comment URL: `https://www.tiktok.com/@{handle}/video/{videoId}?comment_id={cid}`, the `m.tiktok.com/v/{id}.html?...&share_comment_id={cid}` share form, or a `vm.tiktok.com/{code}` / `tiktok.com/t/{code}` shortlink. The `cid`/`comment_id`/`share_comment_id` query value may be numeric or the base64 form TikTok's share sheet emits (`?cid=NzY1...`): both are accepted; base64 is decoded automatically. Mutually exclusive with `post_url`+`comment_id`. Provide at least one of: `comment_url`, `post_url`. | | `post_url` | string | no | The post URL (`https://www.tiktok.com/@{handle}/video/{videoId}`). Combine with `comment_id`, or with `author_username`/`text_contains` for a search. Provide at least one of: `comment_url`, `post_url`. | | `comment_id` | string | no | The target comment's numeric id (the `cid` from the comments endpoint). Requires `post_url`. Note that TikTok share links carry a base64-encoded `cid`: pass such a link as `comment_url` instead (decoded automatically), or base64-decode the value to its digits before passing it here. | | `parent_comment_id` | string | no | The parent comment's numeric id: supply this when the target is a reply so it can be resolved directly via the native replies endpoint. | | `author_username` | string | no | Return up to `max` comments authored by this username (no comment id needed). Mutually exclusive with `text_contains` and any comment id. | | `text_contains` | string | no | Return up to `max` comments whose text contains this snippet (case-insensitive). Mutually exclusive with `author_username` and any comment id. | | `deep_scan` | boolean | no | Widen the scan budget for deeply-buried comments (raises the page ceiling and deadline). | | `position_hint` | string | no | Opaque token from a prior lookup's `lookup.position_hint`. Passing it back probes the comment's last-known location first, making a re-check of an already-found comment cheap. | | `max` | integer | no | For `author_username`/`text_contains` search: max matches to return (1-20, default 5). | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search (/docs/api-reference/tiktok/search) `GET https://api.insightsocial.app/v1/tiktok/search` Returns TikTok videos matching a keyword, each with caption, view and like counts, author details, a thumbnail, and post.ext.region (the country the video is registered to). Use it for a video keyword search you can page and sort. region= only sets the proxy; filter on post.ext.region for one country. limit=120 returns one page of up to 120. search/top is the Top tab. **Metered: 10–370 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keyword or phrase to find TikTok videos | | `date_posted` | string | no | Time Frame One of: yesterday, this-week, this-month, last-3-months, last-6-months, all-time. | | `sort_by` | string | no | Sort by One of: relevance, most-liked, date-posted. | | `region` | string | no | Sets the proxy country (ISO 3166-1 alpha-2). It does not filter the page to that country. Each row already carries post.ext.region, the country the video is registered to; filter on that leaf when you need only one country. | | `cursor` | integer | no | Cursor to get more videos. Get 'cursor' from previous response. Ignored when `limit` is set: that call is a single page. | | `trim` | boolean | no | Accepted for compatibility; the response is already the canonical shape, so this flag has no effect. | | `limit` | integer | no | Bulk labeled page: up to 120 videos in one call, each with post.ext.region. The value rounds up to the next multiple of 30 (30, 60, 90 or 120). The source can repeat a video; repeats are removed, so a 120 page is often a little under 120 unique videos. Omit limit for the default page of about 30, which pages with cursor and carries the same post.ext.region. Does not filter by country; drop rows yourself on post.ext.region. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `min_views` | integer | no | Keep only rows with at least this many views (`post.engagement.views`). A row whose view count is unknown is discarded, so every returned row meets the floor. | | `max_age_days` | integer | no | Keep only rows published within this many days (`post.published_at`), 1 to 3650. A row with no date is discarded. | | `country` | string | no | Comma-separated two-letter ISO 3166-1 codes, for example `US,GB`. Keep only videos TikTok registers to one of these countries (`post.ext.region`); a row with no country is discarded. This filters rows; it does not change where the search runs (that is `region`). | | `exclude_country` | string | no | Comma-separated two-letter ISO 3166-1 codes, for example `IN`. Discard videos TikTok registers to any of these countries (`post.ext.region`); a row with no country is kept. | | `sort_rows` | string | no | `views`: return the kept rows ordered by view count, highest first, across every page walked. Rows without a view count go last. One of: views. | | `max_pages` | integer | no | 1 to 5 (default 1). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed as one call to this endpoint; the whole walk counts once against your rate limit. | --- # Trending (/docs/api-reference/tiktok/trending) `GET https://api.insightsocial.app/v1/tiktok/trending` Returns a page of popular TikTok videos with caption, view and like counts, author and thumbnail. Every row carries post.ext.region, the country the video is registered to. Use it with feed=local for videos mostly from one country (57% measured). The default feed is largely worldwide whatever region you pass, so filter on post.ext.region. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `region` | string | yes | ISO 3166-1 alpha-2 country code (e.g., US, GB, DE). On the default feed it sets the country the request is routed through and does not filter videos; with `feed=local` it sets the country the For You feed is built for. Either way, check `ext.region` on each row for the country a video is actually from. | | `trim` | boolean | no | Accepted for compatibility; it has no effect with `feed=local`. | | `feed` | string | no | `global` (default): the web trending feed routed through `region`, mostly worldwide content, about 13 videos in 3 to 12 seconds. One of: global, local. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Hashtag (/docs/api-reference/tiktok/search-hashtag) `GET https://api.insightsocial.app/v1/tiktok/search/hashtag` Returns TikTok videos posted under a given hashtag, each with caption, view, like, comment and share counts, author details, and a thumbnail. Use it to track one tag such as a campaign or challenge; use search when you want a free text keyword rather than a tag. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `hashtag` | string | yes | Hashtag to search for without the # symbol | | `region` | string | no | Region the proxy will be set to. Note: this isn't going to grab you all tiktoks from this region, you're just setting the proxy there. | | `cursor` | integer | no | Cursor to get more videos. Get 'cursor' from previous response. | | `trim` | boolean | no | Accepted for compatibility; the response is already the canonical shape, so this flag has no effect. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `min_views` | integer | no | Keep only rows with at least this many views (`post.engagement.views`). A row whose view count is unknown is discarded, so every returned row meets the floor. | | `max_age_days` | integer | no | Keep only rows published within this many days (`post.published_at`), 1 to 3650. A row with no date is discarded. | | `sort_rows` | string | no | `views`: return the kept rows ordered by view count, highest first, across every page walked. Rows without a view count go last. One of: views. | | `max_pages` | integer | no | 1 to 5 (default 1). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed as one call to this endpoint; the whole walk counts once against your rate limit. | --- # Search › Top (/docs/api-reference/tiktok/search-top) `GET https://api.insightsocial.app/v1/tiktok/search/top` Returns the videos TikTok ranks highest for a keyword on its Top tab, each with caption, author, engagement counts, a music id, and post.ext.region (the country the video is registered to). Use it for TikTok's own relevance ranking. region= only sets the proxy; filter on post.ext.region for one country. search/users returns accounts, which this endpoint does not include. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keyword or phrase | | `publish_time` | string | no | Time Frame TikTok was posted One of: yesterday, this-week, this-month, last-3-months, last-6-months, all-time. | | `sort_by` | string | no | Sort by One of: relevance, most-liked, date-posted. | | `region` | string | no | Sets the proxy country (ISO 3166-1 alpha-2). It does not filter the page to that country. Each row already carries post.ext.region, the country the video is registered to; filter on that leaf when you need only one country. | | `cursor` | integer | no | Cursor to get more videos. Get 'cursor' from previous response. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `min_views` | integer | no | Keep only rows with at least this many views (`post.engagement.views`). A row whose view count is unknown is discarded, so every returned row meets the floor. | | `max_age_days` | integer | no | Keep only rows published within this many days (`post.published_at`), 1 to 3650. A row with no date is discarded. | | `country` | string | no | Comma-separated two-letter ISO 3166-1 codes, for example `US,GB`. Keep only videos TikTok registers to one of these countries (`post.ext.region`); a row with no country is discarded. This filters rows; it does not change where the search runs (that is `region`). | | `exclude_country` | string | no | Comma-separated two-letter ISO 3166-1 codes, for example `IN`. Discard videos TikTok registers to any of these countries (`post.ext.region`); a row with no country is kept. | | `sort_rows` | string | no | `views`: return the kept rows ordered by view count, highest first, across every page walked. Rows without a view count go last. One of: views. | | `max_pages` | integer | no | 1 to 5 (default 1). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed as one call to this endpoint; the whole walk counts once against your rate limit. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Users (/docs/api-reference/tiktok/search-users) `GET https://api.insightsocial.app/v1/tiktok/search/users` Returns TikTok accounts for a search term with username, name, avatar, followers, verification. include=profile adds bio, region, link and category. Use it to find accounts by name or topic. Add country=KR (or US, DE, …) when you need creators in one country. The video searches, search and search/top, give you posts rather than accounts. **Metered: 10–1310 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keyword or phrase to find TikTok users | | `cursor` | integer | no | Cursor to get more users. Get 'cursor' from previous response. | | `trim` | boolean | no | Accepted for compatibility; the response is already the canonical shape, so this flag has no effect. | | `include` | string | no | Set to `profile` (one token only) to fill `author.bio`, `author.location` (the ISO region), `external_url` and `author.ext.business_category` on every row in this one call. One of: profile. | | `limit` | integer | no | Take the top N rows of the page (1 to 30) after the search has run. On the global page it is not a page size: `next_cursor` still advances past the full page, so rows beyond N on this page are not returned by the next page. | | `country` | string | no | ISO 3166-1 alpha-2 account region to filter by (KR, US, DE, …). Omit it for the global default page. A code this endpoint does not accept is a free 400. One of: US, GB, CA, AU, NZ, IE, DE, FR, ES, IT, PT, NL, BE, CH, AT, SE, NO, DK, FI, IS, PL, CZ, SK, HU, RO, BG, GR, HR, RS, SI, UA, RU, TR, IL, AE, SA, QA, KW, EG, MA, ZA, NG, KE, GH, TZ, ET, BR, MX, AR, CO, CL, PE, VE, EC, UY, PY, BO, CR, PA, DO, JM, JP, KR, CN, TW, HK, MO, SG, MY, ID, TH, VN, PH, IN, PK, BD, LK, NP, MM, KH. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Similar (/docs/api-reference/tiktok/similar) `GET https://api.insightsocial.app/v1/tiktok/similar` Returns about 30 TikTok accounts similar to a given creator, each with username, display name, avatar, bio, verification, and follower, following, video and like counts. Use it to find lookalike creators for one you already know; it returns a single fixed list, so call it again on a result to go wider. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | TikTok username of the seed creator, without the @ symbol. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # User › Audience (/docs/api-reference/tiktok/user-audience) `GET https://api.insightsocial.app/v1/tiktok/user/audience` Returns where a creator's followers are: the top countries in their audience, with sampled follower counts and each country's share. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it for audience geography only. Age and gender are not available publicly on TikTok or any other platform. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | TikTok username without the @ symbol | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # User › Followers (/docs/api-reference/tiktok/user-followers) `GET https://api.insightsocial.app/v1/tiktok/user/followers` Returns a page of accounts that follow a TikTok user, each with username, display name, avatar, and its own follower count. Use it to list who follows an account and page through them; user/following returns the reverse, the accounts they follow. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | no | TikTok username without the @ symbol. Provide at least one of: `handle`, `user_id`. | | `user_id` | string | no | User id. Use this for faster response times. Provide at least one of: `handle`, `user_id`. | | `min_time` | integer | no | Used to paginate. Get 'min_time' from previous response. | | `trim` | boolean | no | Accepted for compatibility; the response is already the canonical shape, so this flag has no effect. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # User › Following (/docs/api-reference/tiktok/user-following) `GET https://api.insightsocial.app/v1/tiktok/user/following` Returns a page of accounts a TikTok user follows, each with username, display name, avatar, and its own follower count. Use it to list who an account follows and page through them; user/followers returns the reverse, the accounts that follow it. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | no | TikTok username without the @ symbol. Provide at least one of: `handle`, `user_id`. | | `user_id` | string | no | TikTok user id. Use this for faster responses. Provide at least one of: `handle`, `user_id`. | | `min_time` | integer | no | Used to paginate. Get 'min_time' from previous response. | | `trim` | boolean | no | Accepted for compatibility; the response is already the canonical shape, so this flag has no effect. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # User › Live (/docs/api-reference/tiktok/user-live) `GET https://api.insightsocial.app/v1/tiktok/user/live` Returns a TikTok account's live room in the platform's own shape: cover image, title, start time, status code, viewer and entry counts, stream ids, and the host's profile. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to read a creator's live room state, but check status and start time yourself, since a room may still be returned after the broadcast has ended. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | TikTok username without the @ symbol | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post › Transcript (/docs/api-reference/tiktok/post-transcript) `GET https://api.insightsocial.app/v1/tiktok/post/transcript` Returns the spoken text of a TikTok video from its captions, with an option to fall back to AI transcription when no captions exist. Use it when you need what was said rather than the numbers; post gives you the video's caption and metrics but no transcript. **100 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the TikTok video | | `language` | string | no | Language of the transcript. 2 letter language code, ie 'en', 'es', 'fr', 'de', 'it', 'ja', 'ko', 'zh' | | `use_ai_as_fallback` | string | no | Set to 'true' to fall back to AI transcription when the video has no captions. AI transcription covers videos up to 10 minutes (600 seconds): a longer video with no captions returns 400 INVALID_REQUEST with `error.details.reason: video_too_long`, `duration_seconds` and `max_seconds`, free and deterministic, so retrying returns the same answer. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Video › Screen Text (/docs/api-reference/tiktok/video-screen-text) `GET https://api.insightsocial.app/v1/tiktok/video/screen-text` Returns the text shown on the video itself: native text stickers plus AI OCR of the cover frame, so burned-in editor captions are caught too. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it for the on-screen overlay text; the caption lives on post and the spoken words on post/transcript. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the TikTok video or photo post (a `/video/` or `/photo/` link) | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Song (/docs/api-reference/tiktok/song) `GET https://api.insightsocial.app/v1/tiktok/song` Returns one TikTok sound's details: title, artist, duration, how many videos use it, and its cover image. Use it when you have a sound's clipId and want the sound itself; song/videos returns the videos made with it. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `clipId` | string | yes | TikTok sound/song clip ID | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Song › Videos (/docs/api-reference/tiktok/song-videos) `GET https://api.insightsocial.app/v1/tiktok/song/videos` Returns videos that use a given TikTok sound, each with caption, author details, and engagement counts. Use it to see how a sound is being used and page through those videos; song returns only the sound's own details. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `clipId` | string | yes | TikTok sound/song clip ID | | `cursor` | integer | no | The cursor to get the next page of results. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Hashtags › Popular (/docs/api-reference/tiktok/hashtags-popular) `GET https://api.insightsocial.app/v1/tiktok/hashtags/popular` Returns TikTok's trending-hashtag board for one of 27 markets over 7, 30 or 90 days: each hashtag's rank, window post and view counts, daily popularity curve, industry board and top creators. Use it for seed-free trend collection by market. One board is three hashtags; pass industry=all for the overall board plus all 15 industry boards in one call. **Metered: 60–960 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `countryCode` | string | no | Market to read the board for, as an ISO country code. One of the 27 markets TikTok publishes the board for. Defaults to US. One of: US, FR, DE, IT, ES, GB, AR, AU, BR, CA, CO, EG, ID, IL, JP, KR, MY, MX, PH, SA, SG, ZA, TW, TH, TR, AE, VN. | | `period` | string | no | Window in days: 7, 30 or 90. Defaults to 7. Each window is a different board, not a longer list. One of: 7, 30, 90. | | `industry` | string | no | Which board to read. Omit for the overall board (3 hashtags). Pass an industry slug for that industry's board (3 hashtags), or `all` for the overall board plus all 15 industry boards in one call. One of: all, education, vehicle-and-transportation, baby-kids-and-maternity, beauty-and-personal-care, tech-and-electronics, travel, household-products, pets, home-improvement, apparel-and-accessories, news-and-entertainment, games, food-and-beverage, sports-and-outdoor, health. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Videos › Popular (/docs/api-reference/tiktok/videos-popular) `GET https://api.insightsocial.app/v1/tiktok/videos/popular` Returns TikTok's Top Videos board for the US, Japan, Vietnam, Thailand or Indonesia over 7 or 30 days: each video's rank, lifetime and window views, organic views, engagement rate and creator. Use it for the videos TikTok ranks highest in those five markets. Other markets have no board and return a free 400; use hashtags/popular there. **Metered: 260–450 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `countryCode` | string | no | Market: US, JP, VN, TH or ID, the only markets TikTok publishes this board for. Defaults to US. One of: US, JP, VN, TH, ID. | | `period` | string | no | Window in days: 7 or 30. Defaults to 7. One of: 7, 30. | | `orderBy` | string | no | How TikTok ranks the board: views (the default), engagement, or six_second_views. One of: views, engagement, six_second_views. | | `limit` | integer | no | How many videos to return, 1-20, default 20. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Region (/docs/api-reference/tiktok/profile-region) `GET https://api.insightsocial.app/v1/tiktok/profile/region` Returns the two letter country code for a public TikTok profile, for example US or MX. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it when all you need is an account's country, for routing or de-duplicating by market; profile returns the full account. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | TikTok username without the @ symbol | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Adlibrary › Search (/docs/api-reference/tiktok/adlibrary-search) `GET https://api.insightsocial.app/v1/tiktok/adlibrary/search` Returns TikTok Ad Library ads matching a keyword or advertiser name, each with creative, title, advertiser, impressions and date; include=ad adds the brand, landing page and advertiser link. Use it to find ads by query or advertiser_name; pass an ad id from the results to adlibrary/ad for the full record. **Metered: 50–170 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | no | Keyword or phrase to search the TikTok Ad Library. Provide at least one of: `query`, `advertiser_name`. | | `advertiser_name` | string | no | Advertiser name as it appears in the TikTok Ad Library. Provide at least one of: `query`, `advertiser_name`. | | `cursor` | string | no | Cursor from the previous response to fetch the next page. | | `include` | string | no | Set to `ad` (one token only) to fill the registered brand name, the landing page, the advertiser's TikTok profile link and avatar (`post.ext.ad.brand_name`, `post.ext.ad.landing_page`, `post.ext.ad.profile_web_link`, `post.author.avatar_url`), plus the objectives, countries and source where the library has them, on every ad in this one call. One of: ad. | | `limit` | integer | no | Take the top N ads of the page (1 to 12) after the search has run. It is not a page size: `next_cursor` still advances past the full page. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Adlibrary › Ad (/docs/api-reference/tiktok/adlibrary-ad) `GET https://api.insightsocial.app/v1/tiktok/adlibrary/ad` Returns one TikTok Ad Library ad: creative video, title, advertiser account, landing page, brand name, and first-shown date. Use it when you already have an ad id or library URL; use adlibrary/search to find ads by keyword or advertiser name first. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `ad_id` | string | no | TikTok Ad Library ad id, or a library.tiktok.com ads/detail URL. Provide at least one of: `ad_id`, `url`. | | `url` | string | no | TikTok Ad Library ads/detail URL. Provide at least one of: `ad_id`, `url`. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Suggestions (/docs/api-reference/tiktok/search-suggestions) `GET https://api.insightsocial.app/v1/tiktok/search/suggestions` Returns the autocomplete suggestions TikTok's own search box shows for a partial query, as a list of suggestion rows. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it for keyword research, or to expand a seed term before running search or search/top. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Partial search query to autocomplete. | | `region` | string | no | ISO 3166-1 alpha-2 country code to localize suggestions. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Collection › Videos (/docs/api-reference/tiktok/collection-videos) `GET https://api.insightsocial.app/v1/tiktok/collection/videos` Returns the public videos inside a TikTok collection, each with caption, play like comment share and save counts, author, and a playable URL. Use it when you have a collection URL and want the videos saved there, paging with max_cursor. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the TikTok collection. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Playlists (/docs/api-reference/tiktok/profile-playlists) `GET https://api.insightsocial.app/v1/tiktok/profile/playlists` Returns the playlists a TikTok creator has published on their profile, each with its playlist id, its name, and the number of videos it holds. Use it to find a playlist id before calling playlist/videos. TikTok does not publish a view total for a playlist, so engagement stays null on every row. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | no | TikTok username without the @ symbol. Provide at least one of: `handle`, `user_id`. | | `user_id` | string | no | TikTok numeric user ID. Use this for faster responses. Provide at least one of: `handle`, `user_id`. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Playlist › Videos (/docs/api-reference/tiktok/playlist-videos) `GET https://api.insightsocial.app/v1/tiktok/playlist/videos` Returns the videos inside one of a creator's profile playlists, in playlist order, each with engagement counts, caption, author, and a playable URL. Use it with a playlist id from profile/playlists. Send the cursor back unchanged, because it is the platform's item index and not a count of the rows you hold. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `playlist_id` | string | yes | Playlist id from /v1/tiktok/profile/playlists (`post.id` on that response). | | `handle` | string | no | TikTok username of the playlist owner, without the @ symbol. Provide at least one of: `handle`, `user_id`. | | `user_id` | string | no | TikTok numeric user ID of the playlist owner. Use this for faster responses. Provide at least one of: `handle`, `user_id`. | | `cursor` | string | no | Cursor from the previous response to fetch the next page of videos. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # User › Liked (/docs/api-reference/tiktok/user-liked) `GET https://api.insightsocial.app/v1/tiktok/user/liked` Returns the videos a TikTok account has liked, newest first, each carrying its own third party author, engagement counts, caption, and a playable URL. Use it for what an account likes, not for who liked a post: that direction does not exist on TikTok. Most accounts hide the list, and a hidden list is a refunded not found. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | no | TikTok username without the @ symbol. Provide at least one of: `handle`, `user_id`. | | `user_id` | string | no | TikTok numeric user ID. Use this for faster responses. Provide at least one of: `handle`, `user_id`. | | `cursor` | string | no | Cursor from the previous response to fetch the next page of liked videos. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Location › Posts (/docs/api-reference/tiktok/location-posts) `GET https://api.insightsocial.app/v1/tiktok/location/posts` Returns public TikTok videos tagged at a place, each with engagement counts, caption, author, and a playable URL. Use it with TikTok's own place id from a place page URL. An unknown place is a refunded not found, while a real place with nothing recent returns an empty list. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `location_id` | string | yes | TikTok place id, as it appears in the place page URL. | | `cursor` | string | no | Cursor from the previous response to fetch the next page of videos. | | `region` | string | no | Two-letter country code for the proxy location. Defaults to US. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Effects (/docs/api-reference/tiktok/effects) `GET https://api.insightsocial.app/v1/tiktok/effects` Returns TikTok camera effects by id, each with its name, its designer, the views of videos made with it, and how many such videos exist. Use it to look several effects up in one call, and match results by the returned id. Repeated ids collapse and unknown ids drop, so the response can be shorter. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `ids` | string | yes | Comma-separated TikTok effect ids, up to 50 per request. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Effect › Videos (/docs/api-reference/tiktok/effect-videos) `GET https://api.insightsocial.app/v1/tiktok/effect/videos` Returns public TikTok videos created with a camera effect, each with engagement counts, caption, author, and a playable URL. Use it with an effect id from effects, and send the cursor back unchanged. TikTok picks its own page size here and ignores the one you ask for. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `effect_id` | string | yes | TikTok effect id, as returned by /v1/tiktok/effects. | | `cursor` | string | no | Cursor from the previous response to fetch the next page of videos. | | `region` | string | no | Two-letter country code for the proxy location. Defaults to US. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Music (/docs/api-reference/tiktok/search-music) `GET https://api.insightsocial.app/v1/tiktok/search/music` Returns TikTok sounds matching a keyword, each with title, artist, duration, cover art, a preview audio URL, and how many videos use it. Use it to size a sound trend, or to join a TikTok sound to Apple Music, Spotify and Amazon through the store ids on ext.dsp_ids. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keyword, matched against sound titles and creators. | | `cursor` | string | no | Cursor from the previous response to fetch the next page of sounds. | | `sort_by` | string | no | Result ordering: relevance (default), most-used, most-recent, shortest or longest. One of: relevance, most-used, most-recent, shortest, longest. | | `filter_by` | string | no | Which field the keyword is matched against: all (default), title or creators. One of: all, title, creators. | | `region` | string | no | Two-letter country code for the proxy location. Defaults to US. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Hashtag (/docs/api-reference/tiktok/hashtag) `GET https://api.insightsocial.app/v1/tiktok/hashtag` Returns TikTok's own record for a hashtag, with its display name, its description where one exists, the live total views under the tag, and how many videos carry it. Use it to size a tag before pulling its posts. Pass the tag name and the id lookup happens for you, or pass hashtag_id when you already hold it. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `hashtag` | string | no | Hashtag name, with or without the leading # symbol. Provide at least one of: `hashtag`, `hashtag_id`. | | `hashtag_id` | string | no | TikTok numeric hashtag id. Skips the name lookup when you already have it. Provide at least one of: `hashtag`, `hashtag_id`. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Ads › Top (/docs/api-reference/tiktok/ads-top) `GET https://api.insightsocial.app/v1/tiktok/ads/top` Returns TikTok's Creative Center leaderboard of top-performing ads for a market and time window, each with click-through rate, board rank, like count, industry, objective, and the ad video. Use it to find ad creative that actually performed. It is one window with no cursor, so set limit for depth and change country, period, or order_by for a different board. **Metered: 100–1000 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `country` | string | no | Creative Center market to read the board for. Defaults to US. This is the market the ads ran in, not the language they are in. One of: US, CA, MX, BR, GB, DE, FR, IT, ES, NL, PL, SE, TR, SA, AE, AU, JP, KR, ID, TH, VN, MY, PH, SG. | | `period` | string | no | Lookback window in days: 7, 30 or 180. Defaults to 30. A longer window is a different board, not more rows. One of: 7, 30, 180. | | `order_by` | string | no | How TikTok ranks the board: for_you (TikTok's own blend, the default), ctr, impression, like, cvr, play_2s_rate or play_6s_rate. One of: for_you, impression, ctr, play_2s_rate, play_6s_rate, cvr, like. | | `ad_format` | string | no | Restrict to Spark ads (ads boosted from an organic post) or Non-Spark ads. Defaults to all. One of: All ad types, Spark ads, Non-Spark ads. | | `ad_language` | string | no | Filter by the language of the ad copy. One of: en, ja, zh, vi, th, pt, id. | | `like_tier` | string | no | TikTok's engagement percentile band, 1 (top) to 5. Use it to read further down the ranking without raising limit. One of: 1, 2, 3, 4, 5. | | `industry` | string | no | Creative Center sub-category label, for example 'Skincare' or 'Cosmetics'. Use a sub-category, not a top-level group: the board returns nothing for the broad groups such as 'Financial Services' or 'Education'. Omit for all industries. | | `keyword` | string | no | Search the board by brand or product keyword. | | `limit` | integer | no | How many ads to return, 1-100, default 20. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Full (/docs/api-reference/tiktok/profile-full) `GET https://api.insightsocial.app/v1/tiktok/profile/full` Returns a TikTok profile plus its latest 10 videos in one call. Use it instead of calling profile and profile/videos. **Beta.** A composite: `data.profile` is a schema-2 Profile and `data.posts[].post` are schema-2 Posts, and the posts page with `cursor`. The rest of `data` is the source's own structure under our naming rules (snake_case keys, string ids). Fields outside the profile and posts may change. See [Schema 2](/docs/schema-2). **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | no | TikTok username or handle, with or without a leading @. One of the identity params is required. Provide at least one of: `handle`, `user_id`. | | `user_id` | string | no | Numeric TikTok user id. Use it instead of the handle when you already have the stable id, which survives a rename. One of the identity params is required. Provide at least one of: `handle`, `user_id`. | | `posts` | integer | no | How many of the fetched recent posts to return and compute the metrics over (1-100, default 25). | | `cursor` | string | no | Pass a prior response's posts_cursor to read the next page of posts. | | `include` | string | no | | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Product (/docs/api-reference/tiktokshop/product) `GET https://api.insightsocial.app/v1/tiktokshop/product` Returns one TikTok Shop product in full: description, price and discount, rating, review count, brand, specs, stock, seller details, shipping estimate, and images. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it when you have a product URL or ID and need the full listing; use products to list a storefront's catalogue, or search to find products by keyword. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | no | Full URL of the TikTok Shop product page. Provide this or `product_id`. Provide at least one of: `url`, `product_id`. | | `product_id` | string | no | Numeric TikTok Shop product id, as returned in `product.id` by tiktokshop/search, tiktokshop/products and tiktokshop/user/showcase. Provide this or `url`. Provide at least one of: `url`, `product_id`. | | `region` | string | no | ISO 3166-1 alpha-2 region for the lookup. Default US. Served: US, SG, MY, TH, VN, PH. GB and ID are temporarily unavailable (503, no charge). Other values are rejected before billing. One of: US, GB, ID, MY, TH, SG, VN, PH. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Product › Reviews (/docs/api-reference/tiktokshop/product-reviews) `GET https://api.insightsocial.app/v1/tiktokshop/product/reviews` Returns reviews on a TikTok Shop product: rating, text, author, buyer country, verified-purchase and incentivized-review flags, the purchased SKU, and a timestamp. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it when you have a product URL or ID and want what buyers say; use product for the listing itself. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | no | Full URL of the TikTok Shop product page. Provide this or `product_id`. Provide at least one of: `url`, `product_id`. | | `product_id` | string | no | The ID of the product (required if url is not provided). Provide at least one of: `url`, `product_id`. | | `region` | string | no | ISO 3166-1 alpha-2 region for the product (e.g. `US`). Important when the product is regionally scoped. One of: US, GB, DE, FR, IT, ID, MY, MX, PH, SG, ES, TH, VN, BR, JP, IE. | | `page` | integer | no | Page number to retrieve, 10 reviews per page. Prefer passing back `pagination.next_cursor`, which carries the same position and stays correct if the underlying paging mechanism changes. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | --- # Products (/docs/api-reference/tiktokshop/products) `GET https://api.insightsocial.app/v1/tiktokshop/products` Returns the products listed on a TikTok Shop storefront: title, images, canonical URL, price and discount, sold count, review count, rating, SKU ID, and the shop's own profile. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it when you have a storefront URL and want everything it sells; use search to find products across storefronts by keyword. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the TikTok Shop page | | `sort_by` | string | no | Sort products by best-selling (`top`) or newest (`new_releases`). Defaults to `top`. One of: top, new_releases. | | `region` | string | no | Region to get shop products from. A storefront is only reachable from its own market, so a GB shop needs `region=GB`; the default is US. One of: US, GB, DE, FR, IT, ID, MY, MX, PH, SG, ES, TH, VN, BR, JP, IE. | | `cursor` | string | no | Pagination cursor. Pass back `pagination.next_cursor` from the previous response. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search (/docs/api-reference/tiktokshop/search) `GET https://api.insightsocial.app/v1/tiktokshop/search` Returns TikTok Shop products matching a keyword across every storefront, each with price, rating, sold count, seller trust label, ship-from location, and promotion badges. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to find products by keyword across every storefront; use products when you already know the storefront. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keyword or phrase to find TikTok Shop products | | `page` | integer | no | Page number to retrieve. Prefer passing back `pagination.next_cursor`, which carries the same position. | | `region` | string | no | Region to search shop products in. Results, prices and currency follow the market; defaults to US. One of: US, GB, DE, FR, IT, ID, MY, MX, PH, SG, ES, TH, VN, BR, JP, IE. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # User › Showcase (/docs/api-reference/tiktokshop/user-showcase) `GET https://api.insightsocial.app/v1/tiktokshop/user/showcase` Returns the products featured in a TikTok user's public showcase: title, price and discount, rating, sold count, images, and SKU ID. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it when you have a creator's handle and want the products they promote; use product for one of those listings in full. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | The handle of the user | | `region` | string | no | Region the lookup is served from. Currently US only (an source limitation; other regions are rejected before billing). Support for more regions is planned source but has no date. One of: US. | | `cursor` | string | no | The cursor to the next page of products | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Sales (/docs/api-reference/tiktokshop/sales) `GET https://api.insightsocial.app/v1/tiktokshop/sales` Returns, per market, listings found, their units sold added up, an estimated sales figure in local currency, and the top listings by units for a keyword. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to compare a keyword's unit sales across several markets in one call; use search directly when you only need one market's listings. **Metered: 0–80 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Product keyword, sent unchanged to each market's search (1-200 characters). | | `regions` | string | no | Comma-separated markets, up to 8: US, GB, DE, FR, IT, ES, IE, ID, MY, MX, PH, SG, TH, VN, BR, JP. Default US. | | `top` | integer | no | How many top listings by units to return per market, 1 to 20. Default 5. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile (/docs/api-reference/truthsocial/profile) `GET https://api.insightsocial.app/v1/truthsocial/profile` Returns a Truth Social user's public profile: display name, follower count, following count, truth count, bio, and profile image URL. Use it for a quick profile snapshot; use user/posts for that account's recent truths. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Truth Social username without the @ symbol | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # User › Posts (/docs/api-reference/truthsocial/user-posts) `GET https://api.insightsocial.app/v1/truthsocial/user/posts` Returns a Truth Social user's recent truths: text content, like count, retruth count, reply count, media attachments, and creation timestamp. Use it to list an account's recent truths; use post when you already have a single truth's URL. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | no | Truth Social username without the @ symbol. Provide at least one of: `handle`, `user_id`. | | `user_id` | string | no | Truth Social user id. Use this for faster response times. Trumps is 107780257626128497. It is the 'id' field in the profile endpoint. Provide at least one of: `handle`, `user_id`. | | `trim` | boolean | no | Set to true for a trimmed down version of the response | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post (/docs/api-reference/truthsocial/post) `GET https://api.insightsocial.app/v1/truthsocial/post` Returns one Truth Social post: text content, like count, retruth count, reply count, media attachments, author info, and creation timestamp. Use it when you have a single post URL; use user/posts to list an account's recent truths. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the Truth Social post | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile (/docs/api-reference/twitch/profile) `GET https://api.insightsocial.app/v1/twitch/profile` Returns a Twitch streamer's public profile: display name, follower count, bio, profile image URL, broadcast language, and partner or affiliate status. Use it for a streamer's public profile; use user/videos for their past broadcasts and highlights. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Twitch username | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Clip (/docs/api-reference/twitch/clip) `GET https://api.insightsocial.app/v1/twitch/clip` Returns one Twitch clip's title, view count, duration, creator name, broadcaster name, game name, thumbnail URL, and creation timestamp. Use it when you have a clip URL and need its stats; use profile for the streamer's own public info. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the Twitch clip | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # User › Videos (/docs/api-reference/twitch/user-videos) `GET https://api.insightsocial.app/v1/twitch/user/videos` Returns up to 100 of a Twitch user's videos: id, slug, URL, embed URL, title, view count, language, duration, game info, broadcaster details, thumbnail, and available qualities. Use it to list a streamer's highlights and uploads; use clip for a single short clip's own details. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Twitch username. | | `filter_by` | string | no | Filter the returned videos by type: `HIGHLIGHT` or `UPLOAD`. (Archived past broadcasts are not currently available source.) One of: HIGHLIGHT, UPLOAD. | | `sort_by` | string | no | Sort order: `TIME` (newest first) or `VIEWS`. One of: TIME, VIEWS. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # User › Schedule (/docs/api-reference/twitch/user-schedule) `GET https://api.insightsocial.app/v1/twitch/user/schedule` Returns a Twitch user's upcoming stream schedule, each entry with start time, end time, title, description, and thumbnail URL. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to see when a streamer goes live next; use profile for their follower count and bio. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Twitch username. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile (/docs/api-reference/twitter/profile) `GET https://api.insightsocial.app/v1/twitter/profile` Returns an X (Twitter) account's public profile: follower count, following count, tweet count, bio, profile and banner image URLs, and verification status. Use it when you have a handle and want a quick account snapshot before pulling its tweets. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Twitter username without the @ symbol | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # User › Tweets (/docs/api-reference/twitter/user-tweets) `GET https://api.insightsocial.app/v1/twitter/user/tweets` Returns an account's most recent tweets in descending order, with full text, like, retweet, reply, bookmark and view counts, media, and creation time. Around 20 tweets per page. Use it to read a timeline: pages come newest first, retweets and self-threads included, and you can page deeper by sending pagination.next_cursor back as cursor. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Twitter username without the @ symbol | | `since` | string | no | Only tweets published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Older tweets are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Pinned tweets sit out of date order and never end the walk. The page still costs what a page costs, so a daily poll pays for the pages it walks and no more. | | `stop_at_id` | string | no | The id (`post.id`) or URL (`post.url`) of the newest tweet you already hold. The page stops just before it: that tweet and everything after it are left off, `next_cursor` is not returned, and `pagination.stopped_at` is `known_id`. A pinned tweet never counts as the stop point. If it is not on this page the page is returned in full with its cursor, so keep walking. `pagination.stopped_at` is `end` when the list ran out first and `null` while there is more to walk. | | `trim` | boolean | no | Accepted for backwards compatibility and ignored. It shrank the raw payload, which the canonical response never exposed, so it changed nothing you can see | | `cursor` | string | no | Cursor from the previous response's pagination.next_cursor to fetch the next page | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | --- # Tweet (/docs/api-reference/twitter/tweet) `GET https://api.insightsocial.app/v1/twitter/tweet` Returns one tweet in full: its text, like, retweet, reply and quote counts, media attachments, author info, and creation timestamp. Use it when you have a tweet URL and need its complete record, including the quote count that the user timeline does not carry. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the tweet. The numeric status id is what identifies it; the handle in the path is not checked | | `trim` | boolean | no | Accepted for backwards compatibility and ignored. It shrank the raw payload, which the canonical response never exposed, so it changed nothing you can see and cost the author details | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Community (/docs/api-reference/twitter/community) `GET https://api.insightsocial.app/v1/twitter/community` Returns an X (Twitter) community's details: name, description, member count, rules, and creation date. Use it when you have a community URL and want the group itself; community/tweets returns what has been posted inside it. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the Twitter/X community, in the form https://x.com/i/communities/{id} | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Community › Tweets (/docs/api-reference/twitter/community-tweets) `GET https://api.insightsocial.app/v1/twitter/community/tweets` Returns recent tweets posted inside an X (Twitter) community, each with its text, engagement counts, and author info. Use it to read a community's activity; it returns a single page, while community returns the group's own details. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the Twitter/X community, in the form https://x.com/i/communities/{id} | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Tweet › Transcript (/docs/api-reference/twitter/tweet-transcript) `GET https://api.insightsocial.app/v1/twitter/tweet/transcript` Returns the transcript of a video attached to a tweet, including auto-generated captions. Use it when a tweet carries video and you need the spoken words; tweet returns the media attachment but not its speech. **100 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the tweet containing a video | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Ai Search (/docs/api-reference/twitter/ai-search) `GET https://api.insightsocial.app/v1/twitter/ai-search` Returns a written answer to a plain-English question about X (Twitter), the X posts cited as sources, and how many searches the model ran. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it for open questions such as what an account has said about a topic this week. A thin answer means thin retrieval, not an empty corpus, so confirm coverage with search/tweets. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Natural-language prompt describing what you want to learn from X. The model autonomously searches X using the x_search tool with any handle / date filters you provide. | | `from_handles` | string | no | Comma-separated X handles (max 10). Restricts the search to posts from these accounts only. Mutually exclusive with exclude_handles. | | `exclude_handles` | string | no | Comma-separated X handles (max 10) to exclude from search results. Mutually exclusive with from_handles. | | `from_date` | string | no | ISO 8601 start date (YYYY-MM-DD). Limits the search window to posts on or after this date. | | `to_date` | string | no | ISO 8601 end date (YYYY-MM-DD). Limits the search window to posts on or before this date. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Tweets (/docs/api-reference/twitter/search-tweets) `GET https://api.insightsocial.app/v1/twitter/search/tweets` Returns tweets matching a keyword or phrase, each with the full text, like, retweet, reply, bookmark and view counts, media attachments, author info, and creation time. Use it to track a topic or brand across X. Operators like from:, quoted phrases, filter:images, and since:/until: work inside query; sort picks latest (default) or top, the cursor pages deeper. **Metered: 10–50 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Keyword or phrase to search tweets for. X operators inside this string narrow the search itself, for example since:YYYY-MM-DD, until:YYYY-MM-DD, from:handle, quoted phrases, min_faves:20, filter:images, and filter:videos. Quote a multi-word phrase when you want relevance rather than engagement ranking from sort=top | | `sort` | string | no | Result ranking: latest (default) for the newest matches first, top for the most popular matches. top ranks by engagement, not relevance, so an unquoted multi-term query can return popular posts that match none of your terms. Quote the phrase or add an operator to tighten it One of: latest, top. | | `cursor` | string | no | Cursor from the previous response's pagination.next_cursor to fetch the next page | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `min_views` | integer | no | Keep only rows with at least this many views (`post.engagement.views`). A row whose view count is unknown is discarded, so every returned row meets the floor. | | `max_age_days` | integer | no | Keep only rows published within this many days (`post.published_at`), 1 to 3650. A row with no date is discarded. | | `sort_rows` | string | no | `views`: return the kept rows ordered by view count, highest first, across every page walked. Rows without a view count go last. One of: views. | | `max_pages` | integer | no | 1 to 5 (default 1). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed as one call to this endpoint; the whole walk counts once against your rate limit. | --- # Tweet › Replies (/docs/api-reference/twitter/tweet-replies) `GET https://api.insightsocial.app/v1/twitter/tweet/replies` Returns the replies to a tweet, each with the reply text, author info, like and reply counts, and creation time. The tweet itself is not in the list. Use it with a tweet URL to read the conversation under it; the cursor walks deeper threads, and a tweet with no replies returns an empty list. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the tweet to fetch replies for | | `cursor` | string | no | Cursor from the previous response's pagination.next_cursor to fetch more replies | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | --- # User › Media (/docs/api-reference/twitter/user-media) `GET https://api.insightsocial.app/v1/twitter/user/media` Returns the tweets on an account's Media tab, only those carrying a photo or video, with media URLs, engagement counts, and creation time. View and bookmark counts are null here. Use it to pull an account's visual output without the text-only tweets user/tweets includes; the cursor pages deeper into the media grid. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Twitter username without the @ symbol | | `cursor` | string | no | Cursor from the previous response's pagination.next_cursor to fetch the next page | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # User › Followers (/docs/api-reference/twitter/user-followers) `GET https://api.insightsocial.app/v1/twitter/user/followers` Returns the accounts following an X (Twitter) user, each with the handle, display name, bio, follower and tweet counts, privacy flag, and join date. Use it to sample an account's audience. Page size is set by the source, around 70 accounts, and the cursor pages deeper; verification status is not readable here and returns null. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Twitter username without the @ symbol | | `cursor` | string | no | Cursor from the previous response's pagination.next_cursor to fetch the next page | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # User › Following (/docs/api-reference/twitter/user-following) `GET https://api.insightsocial.app/v1/twitter/user/following` Returns the accounts an X (Twitter) user follows, each with the handle, display name, bio, follower and tweet counts, privacy flag, and join date. Use it to map who an account pays attention to. Page size is set by the source and the cursor pages deeper; verification status is not readable here and returns null. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | Twitter username without the @ symbol | | `cursor` | string | no | Cursor from the previous response's pagination.next_cursor to fetch the next page | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Tweet › Retweeters (/docs/api-reference/twitter/tweet-retweeters) `GET https://api.insightsocial.app/v1/twitter/tweet/retweeters` Returns the accounts that retweeted a tweet, each with the handle, display name, bio, follower and tweet counts, privacy flag, and join date. Use it with a tweet URL to see who amplified it. Page size is set by the source and the cursor pages deeper; verification status is not readable here and returns null. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the tweet whose retweeters you want | | `cursor` | string | no | Cursor from the previous response's pagination.next_cursor to fetch the next page | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Users (/docs/api-reference/twitter/search-users) `GET https://api.insightsocial.app/v1/twitter/search/users` Returns X (Twitter) accounts matching a name, handle or keyword, with handle, display name, bio, avatar, follower, following and tweet counts, location, verification, join date, and a private flag. Use it to resolve a handle you half know before calling profile or user/tweets. There is no spelling correction, so verify the returned handle. About 20 accounts a page, and the cursor pages deeper. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Name, handle, or keyword to search accounts for | | `cursor` | string | no | Cursor from the previous response's pagination.next_cursor to fetch the next page | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Full (/docs/api-reference/twitter/profile-full) `GET https://api.insightsocial.app/v1/twitter/profile/full` Returns an X account's profile and its recent tweets in one call. Use it instead of calling profile and user/tweets yourself. **Beta.** A composite: `data.profile` is a schema-2 Profile and `data.posts[].post` are schema-2 Posts, and the posts page with `cursor`. The rest of `data` is the source's own structure under our naming rules (snake_case keys, string ids). Fields outside the profile and posts may change. See [Schema 2](/docs/schema-2). **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | X (Twitter) username or handle, with or without a leading @. One of the identity params is required. | | `posts` | integer | no | How many of the fetched recent posts to return and compute the metrics over (1-100, default 25). | | `cursor` | string | no | Pass a prior response's posts_cursor to read the next page of posts. | | `include` | string | no | | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post (/docs/api-reference/weibo/post) `GET https://api.insightsocial.app/v1/weibo/post` Returns one public Weibo post: its text, author, like, comment and repost counts, image count and publish time. Use it when you have a post id; to list an account’s posts use user/posts. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `post_id` | string | yes | Weibo post id (the number in the post URL). | | `include_long_text` | string | no | Whether to fetch the full text of a long post. Default true. One of: true, false. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile (/docs/api-reference/weibo/profile) `GET https://api.insightsocial.app/v1/weibo/profile` Returns a public Weibo account: name, bio, avatar, follower, following and post counts, verification, location and website. Use it when you have a numeric user id; for the account’s posts use user/posts. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `user_id` | string | yes | Numeric Weibo user id (weibo.com/u/). | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # User › Posts (/docs/api-reference/weibo/user-posts) `GET https://api.insightsocial.app/v1/weibo/user/posts` Returns a page of an account’s recent public posts, each with text, engagement counts and publish time. Use it to follow one account; for a single post use post, and for a keyword use search. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `user_id` | string | yes | Numeric Weibo user id (weibo.com/u/). | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post › Comments (/docs/api-reference/weibo/post-comments) `GET https://api.insightsocial.app/v1/weibo/post/comments` Returns a page of first-level comments on a post, each with text, author, likes, reply count and time. Use it when you have a post id and want the conversation under it. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `post_id` | string | yes | Weibo post id (the number in the post URL). | | `limit` | integer | no | Comments per page, 1 to 20. Default 10. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search (/docs/api-reference/weibo/search) `GET https://api.insightsocial.app/v1/weibo/search` Returns public Weibo posts matching a keyword, each with text, author, engagement counts and URL. Use it to find posts on a topic; for what is trending right now use trending. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keyword. | | `search_type` | string | no | Which posts to search: all (default), hot, original, verified, media or viewpoint. One of: all, hot, original, verified, media, viewpoint. | | `include_type` | string | no | Only posts with this media: all (default), pic, video, music or link. One of: all, pic, video, music, link. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Trending (/docs/api-reference/weibo/trending) `GET https://api.insightsocial.app/v1/weibo/trending` Returns the current Weibo hot-search ranking: each topic’s keyword, rank, heat and label, pinned topics marked. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to see what China is talking about right now; to read the posts on a topic use search. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search (/docs/api-reference/xiaohongshu/search) `GET https://api.insightsocial.app/v1/xiaohongshu/search` Returns Xiaohongshu notes matching a keyword: title, short summary, creator, likes, comments, saves, shares, and cover image. Use it to find notes by keyword; use post for a note's full body once you have its ID. **Metered: 50–1000 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keywords, for example 护肤 or camping. | | `limit` | integer | no | Rows to return, 1 to 20. Default 10. | | `cursor` | string | no | Continuation token from a previous response's pagination block. Omit for the first page. | | `sort` | string | no | Result ordering: relevance, latest, most_liked, most_commented, or most_collected. Default relevance. One of: relevance, latest, most_liked, most_commented, most_collected. | | `type` | string | no | Note type: all, image, or video. Default all. One of: all, image, video. | | `published` | string | no | Recency window: all, day, week, or half_year. Default all. One of: all, day, week, half_year. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Trending (/docs/api-reference/xiaohongshu/trending) `GET https://api.insightsocial.app/v1/xiaohongshu/trending` Returns the live Xiaohongshu hot search board: ranked topics with a hot value. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to see what is trending on Xiaohongshu right now; there is no keyword filter, and there is no next page. **Metered: 50–1000 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `limit` | integer | no | Topics to return, 1 to 20. Default 10. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile (/docs/api-reference/xiaohongshu/profile) `GET https://api.insightsocial.app/v1/xiaohongshu/profile` Returns a Xiaohongshu (RedNote) creator's profile: display name, RedNote ID, bio, avatar, followers, following, notes posted, likes received, and IP province. Use it to check a creator's follower count; use profile/posts for their notes. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | no | Xiaohongshu profile URL, for example https://www.xiaohongshu.com/user/profile/6a59f257000000000f03b000. Provide at least one of: `url`, `id`. | | `id` | string | no | User id, the author_user_id on a note. Not the RedNote id. Provide at least one of: `url`, `id`. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Posts (/docs/api-reference/xiaohongshu/profile-posts) `GET https://api.insightsocial.app/v1/xiaohongshu/profile/posts` Returns a Xiaohongshu creator's notes, newest first: title, short summary, engagement counts, and pinned flag. Use it to list a creator's notes; use post for one note's full body text. **Metered: 50–1000 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | no | Xiaohongshu profile URL. Provide at least one of: `url`, `id`. | | `id` | string | no | User id, the author_user_id on a note. Not the RedNote id. Provide at least one of: `url`, `id`. | | `limit` | integer | no | Rows to return, 1 to 20. Default 10. | | `cursor` | string | no | Continuation token from a previous response's pagination block. Omit for the first page. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post (/docs/api-reference/xiaohongshu/post) `GET https://api.insightsocial.app/v1/xiaohongshu/post` Returns one Xiaohongshu note in full: body text, images or video, creator, likes, comments, saves, shares, and the IP province. Use it when you have a note URL and need the full body text; search and profile/posts only carry a short summary. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Note URL, for example https://www.xiaohongshu.com/explore/6aac8143000000002a007389, or the bare note id. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Post › Comments (/docs/api-reference/xiaohongshu/post-comments) `GET https://api.insightsocial.app/v1/xiaohongshu/post/comments` Returns top-level comments on a Xiaohongshu note: text, commenter, likes, reply count, pin flag, and the commenter's IP province. Use it to read a note's top-level comments; reply threads are not expanded on this call. **Metered: 50–1000 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Note URL or the bare note id the comments sit under. | | `limit` | integer | no | Rows to return, 1 to 20. Default 10. | | `cursor` | string | no | Continuation token from a previous response's pagination block. Omit for the first page. | | `sort` | string | no | Comment ordering: relevance, latest, or most_liked. Default relevance. One of: relevance, latest, most_liked. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Channel (/docs/api-reference/youtube/channel) `GET https://api.insightsocial.app/v1/youtube/channel` Returns a YouTube channel's public profile: subscriber count on author.followers (rounded above 1,000), video and view totals, description, banner and avatar URLs, and author.ext.public_email. Use it when you have a handle, channel id, or channel URL and want an account snapshot before pulling its videos. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `channelId` | string | no | YouTube channel ID. Can pass a channelId, handle or url. Provide at least one of: `channelId`, `handle`, `url`. | | `handle` | string | no | YouTube channel handle without the @ symbol. Provide at least one of: `channelId`, `handle`, `url`. | | `url` | string | no | YouTube channel URL. Can pass a channelId, handle or url. Provide at least one of: `channelId`, `handle`, `url`. | | `hl` | string | no | Preferred response language for localized text (ISO 639-1, e.g. 'en', 'es', 'fr'). | | `forUsername` | string | no | Legacy YouTube username (pre-handle) to look up. | | `contact_email` | string | no | Optional. When 1, also returns author.ext.contact_email: when the bio lists two or more email addresses, the one the creator gives for contacting them personally, copied verbatim from the bio. Null when there is one address or none, when every address belongs to a manager, agency or brand, or when the choice was not confident. author.ext.public_email is unchanged. No extra credits. One of: 1. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Channel › About (/docs/api-reference/youtube/channel-about) `GET https://api.insightsocial.app/v1/youtube/channel/about` Returns the address behind a channel's View email address control on author.ext.public_email, its listed country, and the profile: id, handle, name, avatar, bio, URL, subscribers, join date, views. Use it for outreach when GET /v1/youtube/channel returned no email: that lane reads only the public description, this one reads the About tab control. Premium priced, so shortlist first. **250 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `channelId` | string | no | YouTube channel ID. Can pass a channelId, handle or url. Provide at least one of: `channelId`, `handle`, `url`. | | `handle` | string | no | YouTube channel handle without the @ symbol. Provide at least one of: `channelId`, `handle`, `url`. | | `url` | string | no | YouTube channel URL. Can pass a channelId, handle or url. Provide at least one of: `channelId`, `handle`, `url`. | | `contact_email` | string | no | Optional. When 1, also returns author.ext.contact_email: when the bio lists two or more email addresses, the one the creator gives for contacting them personally, copied verbatim from the bio. Null when there is one address or none, when every address belongs to a manager, agency or brand, or when the choice was not confident. author.ext.public_email is unchanged. No extra credits. One of: 1. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Channel › Videos (/docs/api-reference/youtube/channel-videos) `GET https://api.insightsocial.app/v1/youtube/channel/videos` Returns recent videos published by a channel, each with title, view count, duration, thumbnail, and publish date; pass includeExtras=true to add like and comment counts. Use it for a channel's regular uploads; channel/shorts covers Shorts, channel/lives covers streams, channel/community-posts covers text posts. **Metered: 10–20 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `channelId` | string | no | YouTube channel ID. Provide at least one of: `channelId`, `handle`. | | `handle` | string | no | YouTube channel handle without the @ symbol. Provide at least one of: `channelId`, `handle`. | | `since` | string | no | Only videos published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Older videos are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Pinned videos sit out of date order and never end the walk. The page still costs what a page costs, so a daily poll pays for the pages it walks and no more. | | `stop_at_id` | string | no | The id (`post.id`) or URL (`post.url`) of the newest video you already hold. The page stops just before it: that video and everything after it are left off, `next_cursor` is not returned, and `pagination.stopped_at` is `known_id`. A pinned video never counts as the stop point. If it is not on this page the page is returned in full with its cursor, so keep walking. `pagination.stopped_at` is `end` when the list ran out first and `null` while there is more to walk. | | `sort` | string | no | Sort by latest or popular One of: latest, popular. | | `includeExtras` | string | no | Set to `true` to add the like count and comment count (`post.engagement.likes` / `.comments`) and the video description (`post.ext.description`). For the full per-video detail use /v1/youtube/video. Slows the response slightly. | | `is_paid_promotions` | string | no | Set to 'true' to search YouTube's public paid product placement / sponsorship / endorsement surface: returns normal videos where the creator disclosed a paid promotion. | | `include` | string | no | Set to `channel` (one token only) to add the channel's subscriber count to every row (`post.ext.author_followers`, YouTube's own figure, rounded to three significant figures above 1,000) plus `post.author.display_name`, `.avatar_url` and `author.id` where missing. Adds well under a second (never more than 8). One of: channel. | | `exact_dates` | string | no | Exact publish dates are on by default and free. Set to `false` to skip the lookup: rows then carry the supplier's date estimate (a day-granular or coarser label truncated to midnight UTC), marked by `post.ext.published_precision` and `post.ext.published_label`, and the listing title, marked by `post.ext.title_maybe_translated`. One of: true, false. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Video (/docs/api-reference/youtube/video) `GET https://api.insightsocial.app/v1/youtube/video` Returns full details for one video: title, view, like and comment counts, description, tags, duration, channel info, and publish date. Use it when you have a video URL and need its complete record; the list endpoints return lighter entries without the description or tags. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the YouTube video | | `language` | string | no | Preferred response language (mapped to Accept-Language header; not guaranteed due to YouTube localization behavior). 2 letter language code, ie 'en', 'es', 'fr' etc. | | `hl` | string | no | Preferred response language for localized text (ISO 639-1, e.g. 'en', 'es', 'fr'). | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Video › Sponsors (/docs/api-reference/youtube/video-sponsors) `GET https://api.insightsocial.app/v1/youtube/video/sponsors` Returns whether a video carries a paid-promotion disclosure plus the brands likely sponsoring it, each with supporting evidence and a confidence score. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to spot sponsored videos and who paid; sponsors are inferred from the description, links, promo codes, and transcript, not stated by YouTube. **100 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the YouTube video or short | | `language` | string | no | 2 letter language code used for transcript lookup, ie 'en', 'es', 'fr' etc. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Video › Comments (/docs/api-reference/youtube/video-comments) `GET https://api.insightsocial.app/v1/youtube/video/comments` Returns comments on a video, each with the author name, comment text, like count, reply count, and publish timestamp. Use it for a video's top-level comments, then pass a comment's `id` as `continuationToken` to video/comment/replies to open a single thread. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the YouTube video to fetch comments for | | `order` | string | no | Order of comments: 'top' (default, YouTube's relevance ranking) or 'newest' (exact newest-first on `comment.published_at`; each next page continues strictly older with no overlap: the lane for date-window and recency pulls). One of: top, newest. | | `searchTerm` | string | no | Filter comments to those containing this search term. | | `format` | string | no | Text format for comment bodies: 'html' (default) or 'plainText'. One of: html, plainText. | | `max_results` | integer | no | Comments per page, 1-100. The default page is already 100, so set this only to shrink a page; out-of-range values are rejected with a free 400 rather than degrading the response. | | `channel_id` | string | no | YouTube channel id to fetch channel-level community comments for (instead of a video's comments). | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Sending it sets the newest-first sort shown in this endpoint's sort parameter (any other sort value is a free 400). The page still costs what a page costs, so you pay for the pages you walk and no more. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | --- # Video › Comment › Replies (/docs/api-reference/youtube/video-comment-replies) `GET https://api.insightsocial.app/v1/youtube/video/comment/replies` Returns the replies under one comment, each with the reply text, author details, like count, and publish date. Use it after video/comments: pass a comment's `id` as `continuationToken`, and keep paging until no replies remain to get the whole thread. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `continuationToken` | string | yes | Where to start. The id of a top-level comment: a top-level comment id (e.g. the `comment.id` of any row from /v1/youtube/video/comments). For the next page of replies, send `pagination.next_cursor` as `cursor` with the same continuationToken. | | `format` | string | no | Text format for reply bodies: 'html' (default) or 'plainText'. One of: html, plainText. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | Universal pagination cursor. Send `pagination.next_cursor` from the previous response back verbatim: the API maps it to this endpoint's native `continuationToken` (cursor style). You never construct, decode, or look up a cursor. Omit it for page 1. | --- # Search (/docs/api-reference/youtube/search) `GET https://api.insightsocial.app/v1/youtube/search` Returns YouTube search results for a keyword: videos, channels, playlists, shorts, and live streams, each with title, thumbnail, views, and channel. Use it for a general keyword search across every content kind; search/advanced returns videos only but adds date, license, and country filters. **Metered: 10–150 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keyword or phrase. | | `uploadDate` | string | no | Upload date filter. Reliable on its own (no `type`). With `type=shorts`, windows narrower than `this_year` are rejected with a 400 because YouTube returns an empty set for them; `this_year` is accepted but measured to return ~5x fewer unique Shorts across a paginated walk (the platform re-serves the same items under an advancing `continuationToken`), so the recommended pattern for Shorts recency is no `uploadDate` at all plus `includeExtras=true` and a client-side filter on `post.published_at`. With other `type` values the platform currently ignores the date filter and may mix content kinds; filter client-side there too. For an EXACT date window rather than these coarse buckets, use `/v1/youtube/search/advanced`, whose `published_after`/`published_before` take RFC-3339 bounds and are genuinely applied. One of: today, this_week, this_month, this_year. | | `sortBy` | string | no | Sort order: relevance or popular. `popular` cannot be combined with `type=shorts` (rejected with a 400. YouTube serves that combination unreliably); it also caps the page at ~20 results. It is also NOT a strict view-count sort: `popular` is a popularity-weighted ranking, and results do not come back in descending view order. Measured on 4 of 4 test keywords: a `new york` search returned 9.5M, then 502.9M, then 66K views in that order. If you need a view-faithful ordering, use `/v1/youtube/search/advanced` with `order=viewCount` and `includeExtras=true`: its ranking leads with the true top results, and the returned `engagement.views` lets you sort the page exactly client-side. One of: relevance, popular. | | `type` | string | no | Type of content to return. NOTE on Shorts results (`type=shorts`, and the Shorts an untyped search mixes in): the Shorts shelf item carries NO channel object source (0 of 98 items across 3 keywords on 10/08/2026, 25 of 25 without one on 26/09/2026, raw source; `includeExtras=true` does not add it). By default the free per-video lookup that supplies the exact date also fills `author.id` and `post.author.display_name` on those rows, at no extra credit. `post.author.username` (the @handle) and `post.author.avatar_url` stay null on them: send `include=engagement,channel` to fill both and the subscriber count. With `exact_dates=false` no lookup runs and the whole author stays null on Shorts. Video results are unaffected (20/20 carry the full author). `/v1/youtube/search/advanced` also carries `channel_id` on every result, though it has no Shorts filter. One of: videos, shorts, channels, playlists. | | `duration` | string | no | Video duration filter. Known issue: currently not applied by the platform and it can degrade the `type` filter; prefer filtering client-side on `post.content.duration_seconds`. One of: under_3_min, between_3_and_20_min, over_20_min. | | `region` | string | no | 2-letter country code of the country to put the proxy in. | | `includeExtras` | string | no | Set to `true` to add the like count and comment count (`post.engagement.likes` / `.comments`) and the video description (`post.ext.description`) to each video result. Left off, those three are null. Dates do not need it: `post.published_at` is exact by default either way (see `exact_dates`). For Shorts results it additionally supplies `post.content.duration_seconds`, which those items never carry otherwise. It does NOT add channel identity to Shorts results (see `type`) and it never adds a subscriber count. For both, send `include=engagement,channel` instead: the video join supplies every row's channel id, and the channel join adds the subscriber count, in this same call. For full per-video details use `/v1/youtube/video`. | | `include` | string | no | Set to `engagement`, `channel`, or both (`engagement,channel`) to fill more of every row in this one call. `engagement` fills `post.engagement.views`, `.likes` and `.comments`, `post.content.duration_seconds`, `author.id` and `post.author.display_name` where the row lacks them, and replaces a date derived from a label such as "4 months ago" with the exact publish time (removing `post.ext.published_precision`). `channel` puts the channel's subscriber count on `post.ext.author_followers` (YouTube's own figure, which it rounds to three significant figures above 1,000) and fills the channel's `post.author.display_name`, `.avatar_url` and `.username` where missing. Send both to cover rows that arrive without a channel id: the video join supplies it and the channel join then uses it. Channel and playlist result rows are not videos: the video join skips them. Each join adds 0.2 to 0.7 seconds on a fresh page (one lookup per 50 ids, never more than 8 seconds). | | `exact_dates` | string | no | Exact publish dates are on by default and free. Set to `false` to skip the lookup: rows then carry the supplier's date estimate (a day-granular or coarser label truncated to midnight UTC), marked by `post.ext.published_precision` and `post.ext.published_label`, and the listing title, marked by `post.ext.title_maybe_translated`. One of: true, false. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `min_views` | integer | no | Keep only rows with at least this many views (`post.engagement.views`). A row whose view count is unknown is discarded, so every returned row meets the floor. | | `max_age_days` | integer | no | Keep only rows published within this many days (`post.published_at`), 1 to 3650. A row with no date is discarded. | | `sort_rows` | string | no | `views`: return the kept rows ordered by view count, highest first, across every page walked. Rows without a view count go last. One of: views. | | `max_pages` | integer | no | 1 to 5 (default 1). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed as one call to this endpoint; the whole walk counts once against your rate limit. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | --- # Channel › Shorts (/docs/api-reference/youtube/channel-shorts) `GET https://api.insightsocial.app/v1/youtube/channel/shorts` Returns the Shorts published by a channel, each with title, view count, like count, and thumbnail. Use it when you want only a channel's Shorts; channel/videos returns its regular uploads instead. **Metered: 10–20 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `channelId` | string | no | Can pass channelId or handle. Provide at least one of: `channelId`, `handle`. | | `handle` | string | no | YouTube channel handle without the @ symbol. Provide at least one of: `channelId`, `handle`. | | `since` | string | no | Only shorts published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Older shorts are left off the page, and the page that reaches one ends the walk: `next_cursor` is not returned and `pagination.stopped_at` is `since`. Pinned shorts sit out of date order and never end the walk. The page still costs what a page costs, so a daily poll pays for the pages it walks and no more. | | `stop_at_id` | string | no | The id (`post.id`) or URL (`post.url`) of the newest short you already hold. The page stops just before it: that short and everything after it are left off, `next_cursor` is not returned, and `pagination.stopped_at` is `known_id`. A pinned short never counts as the stop point. If it is not on this page the page is returned in full with its cursor, so keep walking. `pagination.stopped_at` is `end` when the list ran out first and `null` while there is more to walk. | | `sort` | string | no | Sort by newest (descending publish date) or popular (descending view count). Both orderings are genuinely applied, unlike `/v1/youtube/search`'s `popular`, which is a popularity-weighted ranking rather than a view sort. One of: newest, popular. | | `include` | string | no | Set to `channel` (one token only) to add the channel's subscriber count to every row (`post.ext.author_followers`, YouTube's own figure, rounded to three significant figures above 1,000) plus `post.author.display_name`, `.avatar_url` and `author.id` where missing. Adds well under a second (never more than 8). One of: channel. | | `exact_dates` | string | no | On by default and free: every row is joined to the per-video lookup, one lookup per page (two on a page past 50 rows, side by side), adding about 0.2 to 0.7 seconds (never more than 3). Set to `false` to skip the lookup and keep the listing title, marked by `post.ext.title_maybe_translated`. The dates on this endpoint are exact either way. One of: true, false. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Community Post (/docs/api-reference/youtube/community-post) `GET https://api.insightsocial.app/v1/youtube/community-post` Returns one YouTube community post: its text, like count, comment count, attached images, and author info. Use it when you have a single community post URL; channel/community-posts lists them for a whole channel. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the YouTube community post | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Playlist (/docs/api-reference/youtube/playlist) `GET https://api.insightsocial.app/v1/youtube/playlist` Returns the videos in a playlist in playlist order: video id, title, thumbnail, channel and publish date. include=engagement adds views, likes, comments and duration. Use it when you have a playlist id. Same rows as playlist/items from the same source; this lane has a second source behind it, so prefer it when availability matters. **Metered: 10–110 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `playlist_id` | string | yes | YouTube playlist ID | | `cursor` | string | no | Pagination cursor from a previous response: fetches the next page. | | `channel_id` | string | no | YouTube channel id: pages that channel's uploads instead of a playlist. | | `include` | string | no | Set to `engagement`, `channel`, or both (`engagement,channel`) to fill more of every row in this one call. `engagement` fills `post.engagement.views`, `.likes` and `.comments`, `post.content.duration_seconds`, `author.id` and `post.author.display_name` where the row lacks them, and replaces a date derived from a label such as "4 months ago" with the exact publish time (removing `post.ext.published_precision`). `channel` puts the channel's subscriber count on `post.ext.author_followers` (YouTube's own figure, which it rounds to three significant figures above 1,000) and fills the channel's `post.author.display_name`, `.avatar_url` and `.username` where missing. Send both to cover rows that arrive without a channel id: the video join supplies it and the channel join then uses it. Each join adds 0.2 to 0.7 seconds on a fresh page (one lookup per 50 ids, never more than 8 seconds). | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Hashtag (/docs/api-reference/youtube/search-hashtag) `GET https://api.insightsocial.app/v1/youtube/search/hashtag` Returns videos posted under a hashtag, each with view count, channel info, and publish date. Use it to read a hashtag feed rather than a keyword query; set type to shorts to limit the results to Shorts. **Metered: 10–110 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `hashtag` | string | yes | Hashtag to search for without the # symbol | | `type` | string | no | Search for all types of content or only shorts One of: all, shorts. | | `include` | string | no | Set to `engagement`, `channel`, or both (`engagement,channel`) to fill more of every row in this one call. `engagement` fills `post.engagement.views`, `.likes` and `.comments`, `post.content.duration_seconds`, `author.id` and `post.author.display_name` where the row lacks them, and replaces a date derived from a label such as "4 months ago" with the exact publish time (removing `post.ext.published_precision`). `channel` puts the channel's subscriber count on `post.ext.author_followers` (YouTube's own figure, which it rounds to three significant figures above 1,000) and fills the channel's `post.author.display_name`, `.avatar_url` and `.username` where missing. Send both to cover rows that arrive without a channel id: the video join supplies it and the channel join then uses it. Each join adds 0.2 to 0.7 seconds on a fresh page (one lookup per 50 ids, never more than 8 seconds). | | `exact_dates` | string | no | Exact publish dates are on by default and free. Set to `false` to skip the lookup: rows then carry the supplier's date estimate (a day-granular or coarser label truncated to midnight UTC), marked by `post.ext.published_precision` and `post.ext.published_label`, and the listing title, marked by `post.ext.title_maybe_translated`. One of: true, false. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `min_views` | integer | no | Keep only rows with at least this many views (`post.engagement.views`). A row whose view count is unknown is discarded, so every returned row meets the floor. | | `max_age_days` | integer | no | Keep only rows published within this many days (`post.published_at`), 1 to 3650. A row with no date is discarded. | | `sort_rows` | string | no | `views`: return the kept rows ordered by view count, highest first, across every page walked. Rows without a view count go last. One of: views. | | `max_pages` | integer | no | 1 to 5 (default 1). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed as one call to this endpoint; the whole walk counts once against your rate limit. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Shorts › Trending (/docs/api-reference/youtube/shorts-trending) `GET https://api.insightsocial.app/v1/youtube/shorts/trending` Returns the Shorts trending on YouTube right now, each with view count, like count, channel info, and thumbnail. Use it for a snapshot of what is trending in Shorts; videos/trending covers regular videos and can be narrowed by country. **Metered: 50–150 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `include` | string | no | Set to `channel` (one token only) to add each row's channel stats in this one call. `channel` puts the channel's subscriber count on `post.ext.author_followers` (YouTube's own figure, which it rounds to three significant figures above 1,000) and fills the channel's `post.author.display_name`, `.avatar_url` and `.username` where missing. Each join adds 0.2 to 0.7 seconds on a fresh page (one lookup per 50 ids, never more than 8 seconds). One of: channel. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Video › Transcript (/docs/api-reference/youtube/video-transcript) `GET https://api.insightsocial.app/v1/youtube/video/transcript` Returns the spoken transcript of a video as timestamped segments, each with text, start time and duration, plus language, word count, and speech rate. Use it for a single video URL; when the video has no captions the reply is a 404 naming the reason, which is expected rather than a failure. **30 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the YouTube video | | `language` | string | no | 2 letter language code, ie 'en', 'es', 'fr' etc. If no caption track matches the language you specify, the request returns 404 RESOURCE_NOT_FOUND (reason `no_captions`). | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Channel › Playlists (/docs/api-reference/youtube/channel-playlists) `GET https://api.insightsocial.app/v1/youtube/channel/playlists` Returns the playlists on a channel's Playlists tab, each with playlist id, title, thumbnail, video count, channel info, and playlist URL. Use it to discover a channel's playlists, then pass a returned playlist id to playlist or playlist/items to read the videos inside one. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `channelId` | string | no | YouTube channel ID. Provide at least one of: `channelId`, `handle`. | | `handle` | string | no | YouTube channel handle (with or without @). Provide at least one of: `channelId`, `handle`. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Channel › Lives (/docs/api-reference/youtube/channel-lives) `GET https://api.insightsocial.app/v1/youtube/channel/lives` Returns the live and past streams on a channel's Live tab, each with title, URL, thumbnail, view count, publish time, and duration. Use it to track a channel's streaming output; channel/videos returns regular uploads and leaves the Live tab out. **Metered: 10–70 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `channelId` | string | no | YouTube channel ID. Provide at least one of: `channelId`, `handle`. | | `handle` | string | no | YouTube channel handle (with or without @). Provide at least one of: `channelId`, `handle`. | | `include` | string | no | Set to `engagement`, `channel`, or both (`engagement,channel`) to fill more of every row in this one call. `engagement` fills `post.engagement.views`, `.likes` and `.comments`, `post.content.duration_seconds`, `author.id` and `post.author.display_name` where the row lacks them, and replaces a date derived from a label such as "4 months ago" with the exact publish time (removing `post.ext.published_precision`). Each join adds 0.2 to 0.7 seconds on a fresh page (one lookup per 50 ids, never more than 8 seconds). | | `exact_dates` | string | no | Exact publish dates are on by default and free. Set to `false` to skip the lookup: rows then carry the supplier's date estimate (a day-granular or coarser label truncated to midnight UTC), marked by `post.ext.published_precision` and `post.ext.published_label`, and the listing title, marked by `post.ext.title_maybe_translated`. One of: true, false. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Channel › Community Posts (/docs/api-reference/youtube/channel-community-posts) `GET https://api.insightsocial.app/v1/youtube/channel/community-posts` Returns the posts on a channel's Posts tab, each with post id, URL, text, images, attached video, like count, publish time, and channel info. Use it to read a channel's text and image posts in bulk; community-post fetches one post when you already have its URL. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `channelId` | string | no | YouTube channel ID. Provide at least one of: `channelId`, `handle`. | | `handle` | string | no | YouTube channel handle (with or without @). Provide at least one of: `channelId`, `handle`. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Videos › Trending (/docs/api-reference/youtube/videos-trending) `GET https://api.insightsocial.app/v1/youtube/videos/trending` Returns the trending videos for a country and category, each with title, thumbnail, duration, view, like and comment counts, channel, and publish time. Use it to see what is popular in a given country right now; shorts/trending covers Shorts and takes no filters. **Metered: 10–60 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `region` | string | no | ISO 3166-1 alpha-2 country code (e.g. US, GB, KR). | | `category` | string | no | YouTube video category id (e.g. 10 = Music, 24 = Entertainment). | | `language` | string | no | Localization language (ISO 639-1) for titles/metadata. | | `max_results` | integer | no | Maximum number of videos to return (1-50). | | `cursor` | string | no | Pagination cursor from a previous response: fetches the next page. | | `include` | string | no | Set to `channel` (one token only) to add each row's channel stats in this one call. `channel` puts the channel's subscriber count on `post.ext.author_followers` (YouTube's own figure, which it rounds to three significant figures above 1,000) and fills the channel's `post.author.display_name`, `.avatar_url` and `.username` where missing. Each join adds 0.2 to 0.7 seconds on a fresh page (one lookup per 50 ids, never more than 8 seconds). One of: channel. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Playlist › Items (/docs/api-reference/youtube/playlist-items) `GET https://api.insightsocial.app/v1/youtube/playlist/items` Returns the videos of a playlist in playlist order, each with video id, title, thumbnail, the video's own channel, its publish date, and its position and insertion time on post.ext. Use it when you have a playlist id; add include=engagement for counts and durations. Same rows as playlist, which has a second source behind it. **Metered: 10–110 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `playlist_id` | string | yes | YouTube playlist id (the value after `list=` in a playlist URL). | | `cursor` | string | no | Pagination cursor from a previous response: fetches the next page. | | `channel_id` | string | no | YouTube channel id: pages that channel's uploads instead of a playlist. | | `include` | string | no | Set to `engagement`, `channel`, or both (`engagement,channel`) to fill more of every row in this one call. `engagement` fills `post.engagement.views`, `.likes` and `.comments`, `post.content.duration_seconds`, `author.id` and `post.author.display_name` where the row lacks them, and replaces a date derived from a label such as "4 months ago" with the exact publish time (removing `post.ext.published_precision`). `channel` puts the channel's subscriber count on `post.ext.author_followers` (YouTube's own figure, which it rounds to three significant figures above 1,000) and fills the channel's `post.author.display_name`, `.avatar_url` and `.username` where missing. Send both to cover rows that arrive without a channel id: the video join supplies it and the channel join then uses it. Each join adds 0.2 to 0.7 seconds on a fresh page (one lookup per 50 ids, never more than 8 seconds). | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Advanced (/docs/api-reference/youtube/search-advanced) `GET https://api.insightsocial.app/v1/youtube/search/advanced` Returns video search results with the full filter set: sort order, length, live status, license, category, country, language, and publish date window. Use it when a plain keyword search is too blunt; results are always videos, so use search when you also want channels or playlists back. **Metered: 10–110 credits.** We reserve the ceiling when the call starts and charge what it actually used when it finishes. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search query. | | `order` | string | no | Sort order: date, rating, relevance (default), title, videoCount, viewCount. `order=date` is exact (measured: zero inversions against the timestamps). `order=viewCount` is YouTube's popularity-weighted ranking: the top result is the true maximum but mid-list order is approximate, so for exact ranking add `includeExtras=true` and sort the page on `engagement.views` client-side. Both are far more faithful than `/v1/youtube/search`'s `sortBy=popular`, which is not view-ordered at all. | | `duration` | string | no | Video length: short (<4m), medium (4-20m), long (>20m), any. Genuinely applied (unlike `/v1/youtube/search`'s `duration`, which the platform ignores). `short` plus a client-side `post.content.duration_seconds <= 180` filter is the documented Shorts approximation: see the endpoint description for why it is an approximation and not a Shorts filter. | | `event_type` | string | no | Broadcast type: live, upcoming, completed. | | `license` | string | no | License filter: creativeCommon, youtube, any. | | `category` | string | no | YouTube video category id (e.g. 10 = Music). | | `region` | string | no | ISO 3166-1 alpha-2 country code. | | `language` | string | no | Preferred result language (ISO 639-1). | | `published_after` | string | no | RFC-3339 datetime lower bound (e.g. 2026-01-01T00:00:00Z). Exact and genuinely applied. This is the endpoint to use for date-window work. | | `published_before` | string | no | RFC-3339 datetime upper bound. Exact and genuinely applied. | | `channel_id` | string | no | Restrict results to a single channel id. | | `max_results` | integer | no | Maximum number of videos to return (1-50). Honored exactly: a page of 50 returns 50 (live-verified). | | `cursor` | string | no | Pagination cursor from a previous response: fetches the next page. | | `safe_search` | string | no | Safe-search filter: none, moderate, strict. | | `video_caption` | string | no | Caption filter: any, closedCaption, none. | | `video_definition` | string | no | Quality filter: any, high, standard. | | `video_dimension` | string | no | Dimension filter: 2d, 3d, any. | | `video_embeddable` | string | no | Restrict to embeddable videos: true, any. | | `video_type` | string | no | Type filter: any, episode, movie. | | `topic_id` | string | no | Restrict to a Freebase topic id (e.g. /m/04rlf for music). | | `location` | string | no | Latitude,longitude center for a geo search (e.g. 37.42307,-122.08427). Must be used together with location_radius. | | `location_radius` | string | no | Radius around location with a unit suffix (e.g. 50km, 10mi). Must be used together with location. | | `includeExtras` | string | no | Set to `true` to add the view, like and comment counts (`post.engagement.views` / `.likes` / `.comments`) and the video length (`post.content.duration_seconds`) to every result. Left off, those four are null. YouTube's search index returns snippets only, so the counts come from a second lookup. | | `include` | string | no | Set to `channel` (one token only) to add each row's channel stats in this one call. `channel` puts the channel's subscriber count on `post.ext.author_followers` (YouTube's own figure, which it rounds to three significant figures above 1,000) and fills the channel's `post.author.display_name`, `.avatar_url` and `.username` where missing. Each join adds 0.2 to 0.7 seconds on a fresh page (one lookup per 50 ids, never more than 8 seconds). One of: channel. | | `since` | string | no | Only rows published on or after this date: YYYY-MM-DD (midnight UTC) or an ISO 8601 timestamp. Rows older than it are left off each page. This list is not in date order, so the walk does not stop at the date: keep following `next_cursor`, and each page costs what a page costs even when every row on it is filtered out. A row with no date is kept. | | `until` | string | no | Only rows published on or before this date: YYYY-MM-DD (that whole UTC day is included) or an ISO 8601 timestamp. Newer rows are left off each page and the walk does not stop, so older pages follow. A row with no date is kept. | | `min_views` | integer | no | Keep only rows with at least this many views (`post.engagement.views`). A row whose view count is unknown is discarded, so every returned row meets the floor. | | `max_age_days` | integer | no | Keep only rows published within this many days (`post.published_at`), 1 to 3650. A row with no date is discarded. | | `sort_rows` | string | no | `views`: return the kept rows ordered by view count, highest first, across every page walked. Rows without a view count go last. One of: views. | | `max_pages` | integer | no | 1 to 5 (default 1). Walk up to this many pages in one call and return the rows from all of them (after any filter). Each page walked is billed as one call to this endpoint; the whole walk counts once against your rate limit. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Suggestions (/docs/api-reference/youtube/search-suggestions) `GET https://api.insightsocial.app/v1/youtube/search/suggestions` Returns the autocomplete suggestions YouTube's own search box shows for a partial query, as a plain list of strings. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it for keyword research, or to expand a seed term before running search or search/advanced. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Partial search query to autocomplete. | | `region` | string | no | ISO 3166-1 alpha-2 country code to localize suggestions. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Video › Audio (/docs/api-reference/youtube/video-audio) `GET https://api.insightsocial.app/v1/youtube/video/audio` Returns the downloadable audio streams for a video, each with a direct media URL, mime type, bitrate, audio quality, sample rate, channels, and duration. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it to grab a video's audio on its own; the URLs are time-limited, so fetch them straight away rather than storing them. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the YouTube video. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Video › Files (/docs/api-reference/youtube/video-files) `GET https://api.insightsocial.app/v1/youtube/video/files` Returns the downloadable video streams for a video, each with a direct media URL, mime type, resolution, quality label, frame rate, bitrate, and duration. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it when you need the video file itself; video/audio returns audio-only streams and video/thumbnails returns still images. **50 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the YouTube video. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Video › Subtitles (/docs/api-reference/youtube/video-subtitles) `GET https://api.insightsocial.app/v1/youtube/video/subtitles` Returns the caption track files for a video, each with a language code and name, file format, and a direct download URL, including auto-generated tracks. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it when you want subtitle files to download; video/transcript gives you the words themselves, already split into timed segments. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the YouTube video. | | `format` | string | no | Subtitle file format filter (e.g. srt, vtt, ttml, json3, srv1). | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Video › Thumbnails (/docs/api-reference/youtube/video-thumbnails) `GET https://api.insightsocial.app/v1/youtube/video/thumbnails` Returns a video's thumbnail images at every available size, each with a direct image URL, width, height, aspect ratio, and image format. **Beta.** The source's own structure under our naming rules (snake_case keys, string ids, ISO times where the source gives a timestamp), not one of the schema-2 entities. Fields may change. See [Schema 2](/docs/schema-2). Use it when you need a video's artwork on its own, for example to pick the largest image for your own listing or preview. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `url` | string | yes | Full URL of the YouTube video. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile › Full (/docs/api-reference/youtube/profile-full) `GET https://api.insightsocial.app/v1/youtube/profile/full` Returns a channel's profile and up to 30 recent videos in one call. Use it instead of calling channel and channel/videos yourself; if the videos cannot be fetched you still get the profile. **Beta.** A composite: `data.profile` is a schema-2 Profile and `data.posts[].post` are schema-2 Posts, and the posts page with `cursor`. The rest of `data` is the source's own structure under our naming rules (snake_case keys, string ids). Fields outside the profile and posts may change. See [Schema 2](/docs/schema-2). **50 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | no | YouTube username or handle, with or without a leading @. One of the identity params is required. Provide at least one of: `handle`, `channelId`, `url`. | | `channelId` | string | no | YouTube channel id, the 24-character string starting with UC. One of the identity params is required. Provide at least one of: `handle`, `channelId`, `url`. | | `url` | string | no | Full YouTube profile or page URL. One of the identity params is required. Provide at least one of: `handle`, `channelId`, `url`. | | `posts` | integer | no | How many of the fetched recent posts to return and compute the metrics over (1-100, default 25). | | `cursor` | string | no | Pass a prior response's posts_cursor to read the next page of posts. | | `include` | string | no | | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Answer (/docs/api-reference/zhihu/answer) `GET https://api.insightsocial.app/v1/zhihu/answer` Returns one Zhihu answer: its text, author, the question it answers and its publish and update times. Use it when you have an answer id; for every answer to a question use question/answers. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `answer_id` | string | yes | Zhihu answer id (zhihu.com/answer/). | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Question (/docs/api-reference/zhihu/question) `GET https://api.insightsocial.app/v1/zhihu/question` Returns a Zhihu question: its title, views, comments, answer and follower counts, and when it was asked. Use it to size a question; to read what people answered use question/answers. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `question_id` | string | yes | Zhihu question id (zhihu.com/question/). | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Question › Answers (/docs/api-reference/zhihu/question-answers) `GET https://api.insightsocial.app/v1/zhihu/question/answers` Returns up to 20 answers to a question, each with its text, author, votes, comments, saves and times. Use it to read a question’s top or newest answers; for one answer by id use answer. It returns one page. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `question_id` | string | yes | Zhihu question id (zhihu.com/question/). | | `limit` | integer | no | Answers to return, 1 to 20. Default 5. | | `order` | string | no | default (Zhihu’s ranking) or updated (recently updated first). One of: default, updated. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Profile (/docs/api-reference/zhihu/profile) `GET https://api.insightsocial.app/v1/zhihu/profile` Returns a public Zhihu account: name, headline, avatar, follower count, and how many answers and articles it wrote. Use it when you have the handle from a zhihu.com/people/ URL. **10 credits** per call. Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `handle` | string | yes | The handle in the profile URL, zhihu.com/people/. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Search › Articles (/docs/api-reference/zhihu/search-articles) `GET https://api.insightsocial.app/v1/zhihu/search/articles` Returns Zhihu column articles matching a keyword, each with title, text, author, votes and comments. Use it to find long-form writing on a topic. **10 credits** per call. Paginates by `cursor`: see [Pagination](https://www.insightsocial.app/docs/pagination). Send your key in the `x-api-key` header. | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `query` | string | yes | Search keyword. | | `sort` | string | no | upvoted_count (most upvoted) or created_time (newest). Omit for Zhihu’s own ranking. One of: upvoted_count, created_time. | | `time_interval` | string | no | Only articles published within this window. One of: a_day, a_week, a_month, three_months, half_a_year, a_year. | | `limit` | integer | no | Articles per page, 1 to 20. Default 20. | | `cursor` | string | no | The `pagination.next_cursor` from the previous page, sent back unchanged. Omit it for the first page. | | `dry_run` | string | no | Set to 1 to get the price of this call without making it: data.dry_run carries credits_min and credits_max. Nothing is charged. One of: 1. | --- # Credits (/docs/api-reference/account/credits) `GET https://api.insightsocial.app/v1/credits` The calling key owner's balance and this period's usage. Free. Send your key in the `x-api-key` header. --- # Endpoints (/docs/api-reference/account/endpoints) `GET https://api.insightsocial.app/v1/endpoints` This catalogue, with per-endpoint prices. Free, and needs no key. Send your key in the `x-api-key` header. --- # OpenAPI (/docs/api-reference/account/openapi.json) `GET https://api.insightsocial.app/v1/openapi.json` The OpenAPI 3.1 spec for every endpoint, for generating clients and agent tools. Free, and needs no key. Send your key in the `x-api-key` header.