hooks.social
TikTok, Instagram, and TikTok Shop data over one simple API. A fresh response that returns data spends prepaid credits. A cached response is free. A failed response is free. Credits do not expire.
Base URL: https://api.hooks.social. Machine-readable contract: /openapi.json. Service status: /status.
Authentication
Send Authorization: Bearer hk_live_… and a User-Agent. The key is shown once, from POST /v1/keys or when you sign up.
Send a User-Agent. curl and browser agents already do. python3 -c and urllib.request need a header such as hooks-social.
Envelope
{
"data": {},
"error": null,
"meta": {
"credits": 1,
"cached": false,
"latency_ms": 840,
"request_id": "…"
}
}
Endpoints
| Method | Path | Query |
|---|---|---|
| GET | /v1/tiktok/profile | username |
| GET | /v1/tiktok/video | id or url |
| GET | /v1/tiktok/posts | username, cursor, count (max 15) |
| GET | /v1/tiktok/comments | video_id, cursor, count |
| GET | /v1/tiktok/comments/replies | video_id, comment_id, cursor, count |
| GET | /v1/tiktok/search/videos | q, cursor, search_id, count |
| GET | /v1/tiktok/search/users | q, cursor, search_id |
| GET | /v1/tiktok/hashtag | name |
| GET | /v1/tiktok/hashtag/videos | id or name, cursor, count |
| GET | /v1/tiktok/sound | id |
| GET | /v1/tiktok/sound/videos | id, cursor, count |
| GET | /v1/tiktokshop/product | id or url, and market (us, uk, de, fr, it, es, ie). 1 credit only when the product has a title and a price. |
| GET | /v1/tiktokshop/search | q, market. 1 credit only when at least one card has an id and a title. |
| GET | /v1/tiktokshop/store | id or url, market. 1 credit only when the shop has a name or at least one card with an id and a title. |
| GET | /v1/tiktokshop/trending | rank (sold, rising, new), days (1, 7, 30), category, trend, limit. US. Best sellers, fastest risers and new products, with units and estimated revenue. 1 credit when products come back. |
| GET | /v1/instagram/profile | username. full_name, avatar_url (HD when present), and verified are null when the source omitted them. items are the timeline plus the first Reels page. |
| GET | /v1/instagram/post | shortcode or url. Likes, comments, taken_at; play_count for a recent Reel |
| GET | /v1/instagram/comments | shortcode, cursor |
| GET | /v1/instagram/posts | username, cursor. Every post: likes, comments, taken_at; videos add view_count, and play_count on page 1. Shares and saves are visible only to the owner |
| GET | /v1/instagram/reels | username, cursor. play_count, likes, comments, taken_at |
| GET | /v1/instagram/audio/reels | audio_id, cursor |
| GET | /v1/instagram/related | username |
| GET | /v1/instagram/search | q (two or more words work best), cursor |
| GET | /v1/youtube/player | id or url. A video that can't be read costs 0. |
| GET | /v1/youtube/transcript | id or url. An empty transcript costs 0. |
| GET | /v1/me | balance, timezone, and email settings for this key |
| GET | /v1/demo/:platform/profile | handle. No key. Profile and recent posts. 20 per visitor per UTC day. |
| GET | /v1/demo/:platform/comments | handle, post. No key. Top comments on a post from a lookup. 5 per visitor per UTC day. |
| POST | /v1/keys | JSON name. Returns the full key once. |
| GET | /v1/keys | prefix, name, created, last used |
| DELETE | /v1/keys/:id | revoke |
| GET | /v1/ledger | cursor, or format=csv |
| POST | /v1/checkout | JSON pack: starter, builder, or scale |
| GET | /v1/resolve | url |
| GET | /v1/watchlist | checked at 06:00 in the account timezone |
| GET, POST | /v1/snapshots | history is free. A capture costs 1 credit for a fresh profile and 1 for fresh posts. A cache hit is free. A profile or posts response with meta.degraded or meta.credits 0 is free. A degraded profile is still stored. Instagram averages use the merged profile items and skip nulls. |
| GET | /v1/snapshots/:id | one stored snapshot, free |
| GET | /v1/compare | platform+handle, or accounts=tiktok:nasa,instagram:nike. Free. |
| GET | /v1/exports/posts | CSV or JSON from a stored snapshot. Free. |
| GET | /v1/exports/snapshots | snapshot history. Free. |
| GET | /v1/exports/compare | compare result. Free. |
| GET | /v1/exports/boards/:id | board spreadsheet. Free. |
| GET, POST | /v1/boards | named collections of TikTok and Instagram accounts. Free. |
| POST | /v1/boards/:id/refresh | snapshot every account. Up to 2 credits each. A cache hit is free. A degraded profile or posts response is free. |
| GET, POST | /v1/automations | daily or weekly refresh. Free to save. A run costs up to 2 credits per account. A cache hit is free. A degraded profile or posts response is free. |
TikTok Shop
Shop routes return the numbers on the page. A field that was not there is JSON null. A real zero stays zero. discount is a percent string such as 53% only when the original price is higher than the sale price. Reviewer names are the masked strings already shown. Creator videos are not included.
market is required: us, uk, de, fr, it, es, or ie. US and UK are served. DE, FR, IT, ES, and IE are not available: the call returns not_supported_market immediately and costs nothing. A fresh product costs 1 credit only when it has a title and a price. Search costs 1 credit only when at least one card has an id and a title. A store costs 1 credit only when it has a shop name or one such card. Anything else is free and is not cached. A response served from cache is free. UK store and search can also return not_supported_market when that page is not available.
curl -H "Authorization: Bearer hk_live_…" \ "https://api.hooks.social/v1/tiktokshop/product?id=1729495699318543725&market=us" curl -H "Authorization: Bearer hk_live_…" \ "https://api.hooks.social/v1/tiktokshop/search?q=phone%20case&market=us" curl -H "Authorization: Bearer hk_live_…" \ "https://api.hooks.social/v1/tiktokshop/store?id=7494313903714501997&market=us"
A daily watch on a product stores sold_count. Add it with platform tiktokshop and handle us:1729495699318543725, or the shop product URL. Boards and automations cover TikTok and Instagram accounts.
Instagram media fields
Profile, posts, and reels return the same media object. likes, comments, view_count, play_count, and taken_at are JSON null when the number isn't available. They are not zero unless the count really is zero. views repeats view_count. plays repeats play_count.
play_countis the play count shown on the Reels grid. Photos are null.view_countis the video view count from the profile timeline. It is a different number fromplay_count, and is null for reels that are not on the timeline.taken_atis when the post was published. It can be null for some reels. A snapshot copies a null posts-pagetaken_atfrom the profile read.product_typeisclipsfor a reel,feedfor a single post, orcarousel_containerfor a carousel.media_typeisimage,video, orcarousel.display_urlis the cover.
A profile lookup returns the bio, avatar, follower counts, verified flag, external URL, post count and recent posts and reels. A profile read is reused for 2 minutes, so a snapshot's posts call doesn't pay twice.
YouTube
GET /v1/youtube/player and GET /v1/youtube/transcript take id or a video URL. A video that is unavailable costs 0. A playable player or a non-empty transcript costs 1 credit. They are also MCP tools.
GET /v1/youtube/search?q= searches videos, with optional period (today, week, month or year) and sort=popular. GET /v1/youtube/channel?handle=@name (or id=UC…) lists a channel's latest uploads with views and dates. 1 credit when videos come back.
What is working now
GET /v1/tiktok/viral?q=&days=7 returns the most viewed videos about a topic or hashtag from the last 1, 7 or 30 days. GET /v1/tiktok/trends?q=&days=7 returns the sounds, hashtags and creators that show up most in recent videos for a niche, or for the whole platform when q is empty, weighted by views; licensed sounds include how many TikTok videos use them. 1 credit each.
Ads
GET /v1/meta/ads?q=brand (or page_id=, optional country=) returns active ads from the Meta Ad Library: copy, headline, call to action, link, format, platforms and start date, grouped by advertiser with the matching brand first. 1 credit when ads come back.
A fresh response with counts costs 1 credit. A response served from cache costs nothing. When a response comes back without any counts, it is marked degraded: true and costs nothing. Posts pages after the first still cost 1 even though those counts are null. A snapshot of a degraded profile is still stored and marked degraded. Other failures are free.
MCP
The API is also an MCP server at https://api.hooks.social/mcp (Streamable HTTP), with a tool for every TikTok, Instagram, TikTok Shop, YouTube and Meta ads data endpoint (including viral, trends, YouTube search and channels), plus get_balance, watchlist_list, watchlist_add, watchlist_changes, snapshot_capture, snapshot_history and compare_accounts. A tool costs the same as the HTTP call: a fresh success spends credits, and cache hits and failed calls are free. Boards, exports and automations are HTTP only. Tool answers leave out image and video links unless a call sets include_media.
Tools that send headers: Authorization: Bearer hk_live_…. For example claude mcp add --transport http hooks https://api.hooks.social/mcp --header "Authorization: Bearer $HOOKS_KEY".
Chat apps that only take a URL (ChatGPT, Claude): use the private link https://api.hooks.social/mcp/hk_live_…. Make a separate key for it, so you can revoke the link without touching your code.
Credits and limits
- A fresh call that returns data costs 1 credit. A response with
meta.cached: trueis free. A genuine empty (no comments, or a search with no results) is HTTP 200 at 0 credits. A partial payload (partial: true) and a stale last-known response (stale: true, withstale_age_s) also cost 0. Failed calls are free, and so is adegradedresponse. - Each key has a per-minute limit (default 60). Cache hits still count toward that limit. The anonymous demo counts a returned profile toward the daily lookup limit even when the response is cached and free.
- HTTP 402 means the balance cannot cover the call. 429 means the minute window is full.
Errors
Errors come back in error with a code and a plain message: invalid_request, unauthorized, no_credits (402), rate_limited (429), not_found, private, not_supported_market when a TikTok Shop page is not served for that market, and unavailable when data couldn't be fetched this time. None of them cost credits.