关于 StoriesFly: Instagram and TikTok data
StoriesFly 提供 TypeScript 客户端,通过 API 获取公开的 Instagram 和 TikTok 数据,面向构建数据工作流的开发者。其类型化方法涵盖个人资料、媒体、社交列表、搜索和下载,无需登录平台账户。
主要功能
- 01获取 Instagram 个人资料和内容
- 02获取 TikTok 个人资料与视频
- 03读取评论、关注者和列表
- 04搜索话题标签和地点
- 05下载媒体文件
- 06分页读取列表结果
适用场景
- 01查看公开的 Instagram 个人资料和帖子
- 02获取 TikTok 视频及账户数据
- 03分批收集关注者或关注列表
- 04从帖子链接下载媒体
MCP 相关
StoriesFly MCP 服务在 `https://storiesfly.com/mcp` 向 AI 助手提供相同的 Instagram 和 TikTok 数据。ChatGPT 和 Claude 连接该地址,并通过 Telegram 登录。
配置
远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。
https://storiesfly.com/mcpclaude mcp add --transport http com-storiesfly https://storiesfly.com/mcp{"mcpServers":{"com-storiesfly":{"type":"http","url":"https://storiesfly.com/mcp"}}}在 Cursor 中打开{"servers":{"com-storiesfly":{"type":"http","url":"https://storiesfly.com/mcp"}}}在 VS Code 中打开{"mcpServers":{"com-storiesfly":{"type":"http","url":"https://storiesfly.com/mcp"}}}{"mcpServers":{"com-storiesfly":{"serverUrl":"https://storiesfly.com/mcp"}}}命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。
工具
详细介绍
storiesfly
用于 StoriesFly API 的 TypeScript 客户端。它通过 HTTP 读取 Instagram 和 TikTok 的公开数据。Instagram 数据包括:个人资料、快拍、精选快拍、帖子、Reels、评论、关注者、话题标签、地点和搜索。TikTok 数据包括:个人资料、视频、快拍、关注者和关注列表、转发,以及下载无水印视频。你无需登录 Instagram 或 TikTok,也无需处理 Cookie 或会话。只需发送 API 密钥并按请求付费。同样的数据也可通过 StoriesFly MCP 服务在 ChatGPT 和 Claude 中使用:参见在 ChatGPT 和 Claude 中使用(MCP)。
此软件包没有运行时依赖。它需要 Node 18 或更高版本(使用全局 fetch),仅支持 ESM,并自带类型定义。46 个数据调用和免费的 account 调用各自都有对应方法,而且每个响应都有类型定义。
安装
npm install storiesfly
获取密钥
- 使用 Telegram 登录 https://storiesfly.com/developers/dashboard。
- 在那里创建密钥。新账户会获得 100 次免费请求,因此你可以在付费前试用所有调用。
请将密钥保存在你的服务器上。任何持有密钥的人都可以消耗你的余额。
快速开始
获取账户当前发布的快拍:
import { StoriesFly } from "storiesfly";
const client = new StoriesFly({ apiKey: process.env.STORIESFLY_API_KEY! });
const { user, stories } = await client.user.stories({ username: "instagram" });
console.log(`${stories.length} stories up from account ${user.id}`);
for (const story of stories) {
console.log(story.media_type, story.taken_at_iso, story.media_url);
}
须知:
- 账户通过
{ username }或{ id }标识,不能同时使用两者。帖子通过{ url }、{ code }或{ id }标识。类型定义会强制执行此规则。 - ID 是字符串。Instagram 的 ID 超过 2^53,使用 JSON 数字会导致数值被舍入。
- 字段名采用
snake_case,与 API 返回的格式一致。响应中没有的值为null,不会是0或""。 - 响应中的媒体链接是经过签名的 Instagram CDN 链接。它们会在几天内过期,因此请尽快下载所需内容。
获取帖子及其图片和视频链接:
const post = await client.post.get({ url: "https://www.instagram.com/p/DcyLOLIEdy7/" });
console.log(post.caption, post.likes);
for (const item of post.media) {
console.log(item.type, item.video_url ?? item.image_url); // one item per slide of a carousel
}
获取无水印的 TikTok 视频和 TikTok 账户的关注者:
const video = await client.tiktok.video({ url: "https://www.tiktok.com/@khaby.lame/video/7697307919943060767" });
console.log(video.caption, video.views, video.video_url); // video_url has no watermark
const profile = await client.tiktok.user.profile({ username: "khaby.lame" });
console.log(profile.followers);
for await (const page of client.paginate(client.tiktok.user.followers, { username: "khaby.lame" }, { maxPages: 2 })) {
for (const follower of page.users) console.log(follower.username);
}
选项
new StoriesFly({
apiKey: "...", // required, sent as X-Api-Key
baseUrl: "...", // default https://storiesfly.com/api/v1
fetch: myFetch, // default: the global fetch
timeoutMs: 60_000, // per attempt; 0 turns the timeout off
maxRetries: 2, // for rate_limited answers; 0 turns retries off
});
每个调用还接受一个最后位置参数,其中包含 signal(一个 AbortSignal)、timeoutMs、maxRetries 和 withMeta。
分页
返回列表的调用会返回一页数据,其中包含 next_cursor 和 has_more。client.paginate 会为你获取后续列表页。它是一个异步迭代器:调用方法、返回页面,然后使用上一页的 next_cursor 再次调用,直到 has_more 为 false:
for await (const page of client.paginate(client.user.followers, { username: "instagram" }, { maxPages: 4 })) {
for (const follower of page.users) console.log(follower.username);
}
- 每一页都是一次调用,并按一次请求计费。大型账户会有很多页,因此除非你打算读取整个列表,否则请设置
maxPages。 paginate接受方法本身(client.user.followers)、其参数和选项:maxPages、signal和withMeta。在参数中传入cursor,即可从已有页面开始遍历。- 只有接受
cursor的方法才能分页,编译器会拒绝其他方法。方法表中的Paged列会标明哪些方法支持分页;导出的ENDPOINTS中每个端点也都有一个paged标志。 - 手动分页时,将
next_cursor作为cursor传回,并保持其他参数不变。游标是不透明的。若传入的游标并非由 API 返回,则会得到bad_request。 - 每页大小遵循 Instagram 的设定,并因端点而异。规范为三个方法固定了大小:
user.posts每页最多返回 9 个项目,user.followers和user.following每页最多返回 25 个项目。方法表中的Paged列会在规范给出页大小的地方显示该值。
TikTok
TikTok 调用位于 client.tiktok 下。账号由 { username } 指定。
await client.tiktok.video({ url }); // one video or photo post, short links too
await client.tiktok.user.profile({ username }); // followers, following, likes, video count
await client.tiktok.user.videos({ username }); // newest first, paged
await client.tiktok.user.stories({ username }); // stories up now
await client.tiktok.user.followers({ username }); // paged
await client.tiktok.user.following({ username }); // paged
await client.tiktok.user.reposts({ username }); // paged
videos、followers、following和reposts接受cursor,并返回next_cursor,因此可以使用client.paginate对它们进行分页。每页大小由 TikTok 决定,且因端点而异。- 隐藏粉丝或关注列表的账号会返回一个带有
hidden: true的空页面。 - 数字在不精确时为
null。文件链接是临时的:请立即保存文件。 - 在
tiktok.user.videos的后续页面中,video_url为null。要获取单个视频的文件,请调用tiktok.video,或使用其url调用download。
下载
client.download({ url }) 接受 Instagram 帖子、Reel 或 IGTV 的链接,或 TikTok 视频或图片帖子的链接(也支持短链接),并返回文件链接:视频(TikTok 视频不带水印)、轮播帖或图片帖中的所有图片、封面,以及 TikTok 视频的配乐。它只返回文件;数字请使用 post.get 或 tiktok.video 获取。费用按每次调用固定收取:Instagram 链接每 1,000 次收费 $0.50,TikTok 链接每 1,000 次收费 $0.20。
const { platform, media } = await client.download({ url: "https://www.instagram.com/reel/DdY3MUJCfby/" });
for (const file of media) console.log(file.type, file.url);
文件链接是临时的,请立即下载。client.account() 也会通过 download_price_usd 显示下载价格。
在 ChatGPT 和 Claude 中使用(MCP)
AI 助手也可以通过 https://storiesfly.com/mcp 获取相同的数据,无需编写代码。详情:https://storiesfly.com/developers/mcp。
- 在 Claude Code 中:
claude mcp add --transport http storiesfly https://storiesfly.com/mcp - ChatGPT 和 Claude 连接到同一地址,并使用 Telegram 登录。
调用费用:响应元数据
调用会解析为已解析的响应正文。要同时查看计费标头,请将 { withMeta: true } 作为最后一个参数传入,调用将解析为 { data, meta }:
const { data, meta } = await client.user.profile({ username: "instagram" }, { withMeta: true });
console.log(data.followers);
console.log(meta.unitsCharged, meta.balanceRemaining, meta.freeUnitsRemaining);
meta 字段 | 标头 | 含义 |
|---|---|---|
status | HTTP 状态。 | |
requestId | X-Request-Id | 调用的 ID。联系支持团队时请提供此 ID。 |
unitsCharged | X-Units-Charged | 此次调用扣除的单位数。免费或已退款时为 0。 |
balanceRemaining | X-Balance-Remaining | 调用后的余额,以美元计,精确到小数点后四位。该值是字符串,不会因浮点数运算而被舍入。 |
freeUnitsRemaining | X-Free-Units-Remaining | 剩余免费单位数。 |
rateLimitLimit | X-RateLimit-Limit | 账号每秒可发送的请求数。 |
rateLimitRemaining | X-RateLimit-Remaining | 当前秒内剩余的请求数。 |
响应中未包含的标头值为 null。元数据按调用返回,不会保存在客户端上(没有 lastResponse),因此同时运行的调用不会覆盖彼此的数值。使用 paginate 时,{ withMeta: true } 会为每一页返回 { data, meta }。
错误及计费说明
每次未返回数据的调用都会抛出 StoriesFlyError。请检查 code,而非 message:
import { StoriesFly, StoriesFlyError } from "storiesfly";
try {
await client.user.stories({ username: "some_private_account" });
} catch (err) {
if (!(err instanceof StoriesFlyError)) throw err;
console.log(err.status); // HTTP status; 0 when no answer came
console.log(err.code); // "private_account"
console.log(err.requestId); // quote it to support
console.log(err.retryAfterMs); // from Retry-After, when the API sent one
console.log(err.meta?.unitsCharged); // what this failed call was charged
}
其他字段:fields(当 code 为 bad_request 时,包含被拒绝参数的名称)、linkUrl(当 code 为 telegram_required 时,包含用于将账户关联到 Telegram 的链接)、url(调用的 URL;其中不含密钥)和 body(解析后的错误响应体)。
答案会计费:200、not_found 和 private_account。参数错误、速率限制和余额不足均不收费;我方故障会退款。err.meta.unitsCharged 和 X-Units-Charged 会说明一次调用扣除了多少单位。
| 状态 | code | 是否计费 | 说明 |
|---|---|---|---|
| 200 | 是 | 数据。 | |
| 404 | not_found | 是 | 未找到与此请求对应的内容。 |
| 403 | private_account | 是 | 调用已到达一个私密账户。 |
| 400 | bad_request | 否 | 缺少参数或参数无效。fields 会指出具体参数。 |
| 401 | unauthorized | 否 | 缺少密钥或密钥错误。 |
| 402 | credits_exhausted | 否 | 积分已用完。请充值。 |
| 403 | telegram_required | 否 | 密钥对应的账户尚未关联 Telegram。linkUrl 中包含关联链接。 |
| 404 | unknown_endpoint | 否 | 不存在此端点。 |
| 429 | rate_limited | 否 | 每秒请求过多。 |
| 500 | internal_error | 已退款 | 我方故障。 |
| 503 | upstream_unavailable | 已退款 | 数据暂时不可用。请稍后重试。 |
客户端还会自行添加三个错误代码。当未收到响应时,status 为 0:timeout(在 timeoutMs 内未收到响应)、network_error(fetch 失败;错误中包含 cause)和 invalid_response(响应不符合文档规定的格式,例如代理返回的错误页面;会保留 status 和响应体)。
重试
429最多重试maxRetries次(默认值为 2)。客户端会等待 API 返回的Retry-After指定的时间;如果 API 未提供该值,则等待半秒,并在之后逐渐延长等待时间。若Retry-After超过 30 秒,则不会等待:会立即抛出错误,并设置retryAfterMs。402永不重试:再次调用也不会改变结果。- 其他错误、超时和网络错误均不会重试。
500或503会退款,因此你可以自行重试。
定价
预付费,每 1,000 次请求 $1.50 起。详见 https://storiesfly.com/developers/pricing。
一个单位对应一次请求。每次调用扣除 1 个单位,包括列表的一页。解析分享链接扣除 2 个单位,account 不扣费。download 按每次调用的固定价格收费(见下载),且只要你有可用单位,就会使用 1 个免费单位。免费单位会优先于余额使用。每个账户的每秒请求数均有限制。
方法
每个方法都是一次 API v1 调用。此表由 OpenAPI 规范(openapi.json)生成,并由测试验证。
| 调用 | 端点 | 单位数 | 分页 | 返回内容 |
|---|---|---|---|---|
client.user.profile() | GET /user/profile | 1 | 个人资料,包含认证徽章、简介、链接和计数 | |
client.user.about() | GET /user/about | 1 | 国家、加入月份和曾用用户名 | |
client.user.posts() | GET /user/posts | 1 | 是,最多 9 页 | 帖子及其点赞数、评论数、浏览量、分享数、收藏数和转发数 |
client.user.reels() | GET /user/reels | 1 | 是 | Reels,包含说明文字、视频和播放次数 |
client.user.tagged() | GET /user/tagged | 1 | 是 | 账号被标记的帖子 |
client.user.pinned() | GET /user/pinned | 1 | 置顶在个人资料顶部的帖子 | |
client.user.stories() | GET /user/stories | 1 | 当前有效的快拍,以及每段视频的时长 | |
client.user.highlights() | GET /user/highlights | 1 | 是 | 精选集封面和标题 |
client.user.followers() | GET /user/followers | 1 | 是,最多 25 页 | 关注者,逐页显示 |
client.user.following() | GET /user/following | 1 | 是,最多 25 页 | 已关注的账号,逐页显示 |
client.user.followersSearch() | GET /user/followers/search | 1 | 在关注者中查找某人 | |
client.user.followingSearch() | GET /user/following/search | 1 | 在已关注的账号中查找某人 | |
client.user.reposts() | GET /user/reposts | 1 | 是 | 该账号转发的帖子和 Reels |
client.user.suggested() | GET /user/suggested | 1 | Instagram 推荐的相似账号 | |
client.post.get() | GET /post | 1 | 单条帖子:媒体链接、说明文字、浏览量、点赞数、评论数、分享数、收藏数和转发数 | |
client.post.comments() | GET /post/comments | 1 | 是 | 评论及其点赞数和回复数 |
client.post.commentReplies() | GET /post/comment-replies | 1 | 是 | 对评论的回复 |
client.post.likers() | GET /post/likers | 1 | 点赞该帖子的账号 | |
client.post.usertags() | GET /post/usertags | 1 | 帖子中标记的人 | |
client.post.music() | GET /post/music | 1 | Reels 使用的曲目 | |
client.comment.likers() | GET /comment/likers | 1 | 是 | 点赞评论的账号 |
client.story.get() | GET /story | 1 | 通过链接或 ID 获取单条快拍 | |
client.highlight.get() | GET /highlight | 1 | 通过链接或 ID 获取精选集中的所有快拍 | |
client.hashtag.get() | GET /hashtag | 1 | 话题标签及其帖子数 | |
client.hashtag.posts() | GET /hashtag/posts | 1 | 是 | 话题标签下的帖子:热门、最新或 Reels |
client.location.get() | GET /location | 1 | 地点名称、地址、城市和坐标 | |
client.location.posts() | GET /location/posts | 1 | 是 | 在某地点标记的帖子:热门或最新 |
client.location.search() | GET /location/search | 1 | 按名称或某个坐标点附近查找地点 | |
client.search.users() | GET /search/users | 1 | 是 | 按名称或用户名查找账号 |
client.search.hashtags() | GET /search/hashtags | 1 | 是 | 查找话题标签及其帖子数 |
client.search.places() | GET /search/places | 1 | 按名称查找地点 | |
client.search.music() | GET /search/music | 1 | 是 | 按曲名或艺术家查找曲目 |
client.search.reels() | GET /search/reels | 1 | 是 | 按文本查找 Reels |
client.search.top() | GET /search/top | 1 | 是 | 与文本相关的热门帖子 |
client.track.get() | GET /track | 1 | 曲目标题、艺术家、时长和音频链接 | |
client.resolve() | GET /resolve | 1,分享链接为 2 | 将帖子、Reels 或分享链接转换为数据 | |
client.shop.storefront() | GET /shop/storefront | 1 | 账号商店:系列和首批商品 | |
client.shop.products() | GET /shop/products | 1 | 是 | 商店系列中的商品 |
client.tiktok.video() | GET /tiktok/video | 1 | 单条 TikTok 视频,无水印,包含浏览量、点赞数、评论数和分享数 | |
client.tiktok.user.profile() | GET /tiktok/user/profile | 1 | TikTok 个人资料,包含关注者数、关注数、点赞数和视频数 | |
client.tiktok.user.videos() | GET /tiktok/user/videos | 1 | 是 | TikTok 账号的视频及其数据,逐页显示;文件链接位于第一页 |
client.tiktok.user.stories() | GET /tiktok/user/stories | 1 | 当前有效的 TikTok 快拍 | |
client.tiktok.user.followers() | GET /tiktok/user/followers | 1 | 是 | TikTok 关注者,逐页显示;列表设为隐藏时会返回空页 |
client.tiktok.user.following() | GET /tiktok/user/following | 1 | 是 | TikTok 已关注的账号,逐页显示;列表设为隐藏时会返回空页 |
client.tiktok.user.reposts() | GET /tiktok/user/reposts | 1 | 是 | TikTok 账号转发的视频,逐页显示,并包含视频作者 |
client.download() | GET /download | 1 | 通过链接获取 Instagram 帖子或 Reels 的文件,或 TikTok 视频文件 | |
client.account() | GET /account | 0 | 你的套餐、余额、免费单位数和使用情况 | |
| 参数简述: |
- TikTok 调用(
tiktok.user.*)接收{ username },tiktok.video和download接收{ url }。分页的 TikTok 调用还接收cursor。 - 账号调用(
user.*、shop.storefront)接收{ username }或{ id }。带游标的调用还接收cursor。user.followersSearch和user.followingSearch还接收q。 - 帖子调用(
post.*)接收{ url }、{ code }(链接中/p/或/reel/后面的部分)或{ id }。post.comments接收cursor。post.commentReplies还接收comment_id。 story.get和highlight.get接收{ url }或{ id }。hashtag.posts接收name、sort(top、recent或reels;翻页时保持一致)和cursor。location.posts接收id、sort(top或recent)和cursor。location.search接收q,或lat和lng。search.*调用接收长度为 1 到 100 个字符的q。post.get返回完整帖子:media(帖子图片和视频的链接,轮播中的每张幻灯片对应一个条目)、标题、所有者、时间和各项数据。其结构与user.posts、user.reels、hashtag.posts及其他帖子列表中的条目相同;对于帖子链接,resolve也会返回该内容。
文档
- API 文档,每个端点均有独立页面:https://storiesfly.com/developers/docs
- OpenAPI 3.1 规范:https://storiesfly.com/openapi.json(此仓库中包含副本
openapi.json和openapi.yaml) - API 更新日志:https://storiesfly.com/developers/changelog
- 此软件包的更新日志:CHANGELOG.md
- 示例:
stories.mjs(账号的快拍)、followers.mjs(使用paginate获取关注者)和post.mjs(包含媒体链接和数据的帖子)。使用STORIESFLY_API_KEY=your_key node examples/stories.mjs instagram运行。
从 0.x 迁移
1.0 是针对 API v1 的重写版本。旧方法已移除,基础 URL 已更改,旧版套餐和每日配额在 v1 中也不复存在。
| 0.x | 1.0 |
|---|---|
getStories(username) | user.stories({ username })。它会返回 { user, stories }。个人资料和精选动态需要分别调用(user.profile、user.highlights),且每次调用都会扣费。 |
getProfile(username) | user.profile({ username })。它还会提供 id、bio、verified 和 external_url。 |
getHighlights(id) | highlight.get({ id }),会返回精选动态中的所有快拍。user.highlights 会列出账号的精选动态。 |
getMedia(url) | post.get({ url })。它会返回完整帖子:media 包含图片和视频链接(image_url、video_url,轮播中的每张图片各占一项),以及说明文字、所有者、时间和各项计数。 |
download(url) | 1.1 中的 download({ url }) 会返回帖子、短视频或 TikTok 视频的文件链接。post.get 也会提供媒体链接。 |
getTrackerEvents、listTrackedAccounts、addTrackedAccount、removeTrackedAccount、checkShadowban、getOutboundEngagement | 不属于 API v1,因此 1.0 中不提供。 |
其他变更:
baseUrl现在默认设为https://storiesfly.com/api/v1。bearerToken选项已移除:v1 只接受通过X-Api-Key传入的密钥,不接受其他方式。- 方法按资源分组:
client.user.profile()、client.post.get()、client.search.users()。每个方法都接收一个参数对象。 - 响应使用 API 自身的
snake_case名称;缺失的值以null表示:fullName现在是full_name,profilePicUrl是profile_pic_url,isPrivate是is_private,takenAt是taken_at。 StoriesFlyError包含code、requestId和retryAfterMs。0.x 中的rateLimit、retryAfterSec、requiresUpgrade、isRateLimited和method字段已移除。请将err.code与rate_limited、credits_exhausted等值进行比较。现在遇到429时会自动重试。- 旧版套餐(DEV、Starter、Growth、Scale 和 Tracker 套餐)、每日配额以及每分钟 60 次请求的突发限制均已移除。v1 按请求计费,从预付余额中扣除;新账号可免费请求 100 次。请参阅定价。
- 将密钥对应的账号关联到 Telegram 后,旧密钥即可用于 v1。在此之前,v1 会返回
telegram_required,且err.linkUrl中包含关联链接。 - Tracker、影子封禁和对外互动分析端点仍可通过 HTTP 响应,但 1.0 不会封装这些端点。如需继续使用,请保留
storiesfly@0或自行调用这些端点。0.x 调用的旧版/api/stories、/api/profile、/api/highlights、/api/media和/api/download端点,将继续为现有免费密钥提供服务,直至 2026-12-31。详情:https://storiesfly.com/developers/docs/legacy。
常见问题
需要登录 Instagram 或 TikTok 吗? 不需要。你只需提供 StoriesFly API 密钥,无需其他信息。无需管理 Cookie 或会话。
可以获取 TikTok 账号的粉丝列表吗?
可以。client.tiktok.user.followers({ username }) 会分页返回列表,client.paginate 可自动遍历各页。隐藏粉丝列表的账号会返回一个包含 hidden: true 的空页面。
可以下载无水印的 TikTok 视频吗?
可以。client.tiktok.video({ url }) 会返回无水印的 video_url,而 client.download({ url }) 会返回 Instagram 或 TikTok 链接对应的文件链接。
可以通过 ChatGPT 或 Claude 使用吗?
可以,通过 MCP 服务 https://storiesfly.com/mcp 使用。请参阅通过 ChatGPT 和 Claude 使用(MCP)。
如何计费? 按请求计费,从预付余额中扣除。新账号可免费请求 100 次,仅限一次。请参阅定价。
无关联关系
这是 StoriesFly API 的客户端。它只读取公开数据;对于私密账号,会返回 private_account。它不是 Instagram 或 Meta 的产品,也与其无关联或背书关系。
开发
npm install
npm test # clean, build, compile the tests and run them
npm run openapi:check # validate openapi.json and openapi.yaml, and check that they match
npm run readme:check # check the method table above against the spec
要更新规范:将 openapi.json 替换为新文件,运行 npm run openapi:sync 和 npm run readme:sync,然后运行测试。
许可证
MIT。请参阅 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。