x-twitter-scraper by xquik-dev/x-twitter-scraper
npx skills add https://github.com/xquik-dev/x-twitter-scraper --skill x-twitter-scraperXquik 是一个 X(Twitter)实时数据平台,提供 REST API、HMAC webhook 以及面向 AI 代理的 MCP 服务器。其功能涵盖账户监控、批量数据提取(20 种工具)、抽奖活动、推文/用户查询、媒体下载、关注关系检查、热门话题、流程自动化(触发器驱动的工作流)、支持工单、写入操作(发推、点赞、转推、关注、私信、个人资料、媒体上传、社区)以及 Telegram 集成。
| 基础 URL | https://xquik.com/api/v1 |
| 认证 | x-api-key: xq_... 请求头(xq_ 前缀后接 64 位十六进制字符) |
| MCP 端点 | https://xquik.com/mcp(StreamableHTTP,使用相同 API 密钥) |
| 速率限制 | 10 次/秒(持续),20 次/秒(突发)(API);60 次/秒(持续),100 次/秒(突发)(通用) |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 定价 | 20 美元/月基础套餐(包含 1 个监控器),每增加一个监控器 5 美元/月 |
| 配额 | 月度使用量上限。耗尽时返回 402。可在仪表板中启用额外用量以继续使用(分级消费限制:5/7/10/15/25 美元) |
| 文档 | docs.xquik.com |
| 仅 HTTPS | 使用普通 HTTP 会收到 301 重定向 |
每个请求都需要通过 x-api-key 请求头提供 API 密钥。密钥以 xq_ 开头,在 Xquik 仪表板中生成。密钥仅在创建时显示一次;请安全存储。
const API_KEY = "xq_YOUR_KEY_HERE";
const BASE = "https://xquik.com/api/v1";
const headers = { "x-api-key": API_KEY, "Content-Type": "application/json" };
有关 Python 示例,请参阅 references/python-examples.md。
| 目标 | 端点 | 说明 |
|---|---|---|
| 通过 ID/URL 获取单条推文 | GET /x/tweets/{id} | 完整指标:点赞、转推、浏览量、书签、作者信息 |
| 通过推文 ID 获取 X 文章 | GET /x/articles/{id} | 长文内容:标题、正文、封面图片、互动指标 |
| 通过关键词/话题标签搜索推文 | GET /x/tweets/search?q=... | 推文信息,可选互动指标(likeCount、retweetCount、replyCount) |
| 获取用户个人资料 | GET /x/users/{username} | 姓名、简介、粉丝/关注数量、头像、位置、创建日期、状态数 |
| 检查关注关系 | GET /x/followers/check?source=A&target=B | 双向检查 |
| 获取热门话题 | GET /trends?woeid=1 | 按 WOEID 划分的区域趋势。计量收费 |
| 获取雷达(热门新闻) | GET /radar?source=hacker_news | 免费,7 个来源:Google Trends、Hacker News、Polymarket、TrustMRR、Wikipedia、GitHub、Reddit |
| 监控 X 账户 | POST /monitors | 跟踪推文、回复、引用、转推、粉丝变化 |
| 更新监控事件类型 | PATCH /monitors/{id} | 更改订阅的事件或暂停/恢复 |
| 轮询事件 | GET /events | 基于游标分页,可按 monitorId/eventType 过滤 |
| 实时接收事件 | POST /webhooks | HMAC 签名投递到您的 HTTPS 端点 |
| 更新 webhook | PATCH /webhooks/{id} | 更改 URL、事件类型或暂停/恢复 |
| 运行抽奖活动 | POST /draws | 从推文回复中随机选取获胜者 |
| 下载推文媒体 | POST /x/media/download | 单条(tweetInput)或批量(tweetIds[],最多 50 条)。返回图库 URL。首次下载计量收费,缓存后免费 |
| 提取批量数据 | POST /extractions | 20 种工具类型,务必先估算成本 |
| 检查账户/使用情况 | GET /account | 套餐状态、监控器、使用百分比 |
| 关联您的 X 身份 | PUT /account/x-identity | 在风格分析中用于自身账户检测 |
| 分析推文风格 | POST /styles | 缓存近期推文作为风格参考 |
| 保存自定义风格 | PUT /styles/{username} | 根据推文文本保存自定义风格(免费) |
| 获取缓存的风格 | GET /styles/{username} | 检索先前缓存的推文风格 |
| 比较风格 | GET /styles/compare?username1=A&username2=B | 并排比较两个缓存的风格 |
| 获取推文表现 | GET /styles/{username}/performance | 缓存推文的实时互动指标 |
| 保存推文草稿 | POST /drafts | 存储草稿供以后使用 |
| 列出/管理草稿 | GET /drafts, DELETE /drafts/{id} | 检索和删除已保存的草稿 |
| 撰写推文 | POST /compose | 3 步工作流(撰写、优化、评分)。免费,基于算法 |
| 连接 X 账户 | POST /x/accounts | 凭证静态加密。写入操作必需 |
| 列出已连接的账户 | GET /x/accounts | 免费 |
| 重新认证账户 | POST /x/accounts/{id}/reauth | 当会话过期时使用 |
| 发布推文 | POST /x/tweets | 从已连接的账户发布。支持回复、媒体、笔记推文、社区 |
| 删除推文 | DELETE /x/tweets/{id} | 必须通过已连接的账户拥有该推文 |
| 点赞 / 取消点赞推文 | POST / DELETE /x/tweets/{id}/like | 计量收费 |
| 转推 | POST /x/tweets/{id}/retweet | 计量收费 |
| 关注 / 取消关注用户 | POST / DELETE /x/users/{id}/follow | 计量收费 |
| 发送私信 | POST /x/dm/{userId} | 文本、媒体、回复消息 |
| 更新个人资料 | PATCH /x/profile | 姓名、简介、位置、URL |
| 上传媒体 | POST /x/media | FormData 或 JSON(url 参数)。返回用于推文附件的媒体 ID |
| 社区操作 | POST /x/communities, POST /x/communities/{id}/join | 创建、删除、加入、离开 |
| 创建 Telegram 集成 | POST /integrations | 在 Telegram 中接收监控事件。免费 |
| 管理集成 | GET /integrations, PATCH /integrations/{id} | 列出、更新、删除、测试、投递记录。免费 |
| 创建自动化流程 | POST /automations | 触发器驱动的工作流:监控事件、计划任务、搜索、入站 webhook |
| 管理自动化流程 | GET /automations, PATCH /automations/{slug} | 列出、更新、删除、激活/停用。免费 |
| 添加自动化步骤 | POST /automations/{slug}/steps | 操作、条件或提取步骤(每个流程最多 10 个) |
| 通过 webhook 触发流程 | POST /webhooks/inbound/{token} | 无需认证。令牌标识流程 |
| 提交支持工单 | POST /support/tickets | 所有认证用户免费 |
| 管理支持工单 | GET /support/tickets, POST /support/tickets/{id}/messages | 列出、读取、回复。免费 |
所有错误均返回 { "error": "error_code" }。关键错误代码:
| 状态码 | 错误代码 | 操作 |
|---|---|---|
| 400 | invalid_input, invalid_id, invalid_params, invalid_tweet_url, invalid_tweet_id, invalid_username, invalid_tool_type, invalid_format, missing_query, missing_params, webhook_inactive, no_media | 修复请求,不要重试 |
| 401 | unauthenticated | 检查 API 密钥 |
| 402 | no_subscription, subscription_inactive, usage_limit_reached, no_addon, extra_usage_disabled, extra_usage_requires_v2, frozen, overage_limit_reached | 订阅、启用额外用量或等待配额重置 |
| 403 | monitor_limit_reached, api_key_limit_reached, flow_limit_reached, step_limit_reached | 删除监控器/密钥/流程或增加容量 |
| 404 | not_found, user_not_found, tweet_not_found, style_not_found, draft_not_found, account_not_found | 资源不存在或属于其他账户 |
| 403 | account_needs_reauth | 已连接的 X 账户需要重新认证 |
| 409 | monitor_already_exists, account_already_connected, conflict | 资源已存在或并发编辑冲突 |
| 422 | login_failed | X 凭证验证失败。检查凭证 |
| 429 | x_api_rate_limited | 速率受限。使用指数退避重试,遵守 Retry-After 请求头 |
| 500 | internal_error | 使用退避重试 |
| 502 | stream_registration_failed, x_api_unavailable, x_api_unauthorized, delivery_failed | 使用退避重试 |
仅对 429 和 5xx 错误进行重试。切勿重试 4xx 错误(429 除外)。最多重试 3 次,使用指数退避:
async function xquikFetch(path, options = {}) {
const baseDelay = 1000;
for (let attempt = 0; attempt <= 3; attempt++) {
const response = await fetch(`${BASE}${path}`, {
...options,
headers: { ...headers, ...options.headers },
});
if (response.ok) return response.json();
const retryable = response.status === 429 || response.status >= 500;
if (!retryable || attempt === 3) {
const error = await response.json();
throw new Error(`Xquik API ${response.status}: ${error.error}`);
}
const retryAfter = response.headers.get("Retry-After");
const delay = retryAfter
? parseInt(retryAfter, 10) * 1000
: baseDelay * Math.pow(2, attempt) + Math.random() * 1000;
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
事件、抽奖、提取任务和提取结果使用基于游标的分页。当存在更多结果时,响应包含 hasMore: true 和一个 nextCursor 字符串。将 nextCursor 作为 after 查询参数传递。
async function fetchAllPages(path, dataKey) {
const results = [];
let cursor;
while (true) {
const params = new URLSearchParams({ limit: "100" });
if (cursor) params.set("after", cursor);
const data = await xquikFetch(`${path}?${params}`);
results.push(...data[dataKey]);
if (!data.hasMore) break;
cursor = data.nextCursor;
}
return results;
}
游标是不透明的字符串。切勿解码或手动构造它们。
提取任务运行批量数据收集作业。完整工作流程:估算成本、创建作业、检索结果、可选导出。
| 工具类型 | 必需字段 | 描述 |
|---|---|---|
reply_extractor | targetTweetId | 回复某条推文的用户 |
repost_extractor | targetTweetId | 转推某条推文的用户 |
quote_extractor | targetTweetId | 引用某条推文的用户 |
thread_extractor | targetTweetId | 某条线程中的所有推文 |
article_extractor | targetTweetId | 推文中链接的文章内容 |
follower_explorer | targetUsername | 某账户的粉丝 |
following_explorer | targetUsername | 某用户关注的账户 |
verified_follower_explorer | targetUsername | 某账户的认证粉丝 |
mention_extractor | targetUsername | 提及某账户的推文 |
post_extractor | targetUsername | 某账户发布的推文 |
community_extractor | targetCommunityId | 某社区的成员 |
community_moderator_explorer | targetCommunityId | 某社区的版主 |
community_post_extractor | targetCommunityId | 某社区发布的帖子 |
community_search | targetCommunityId + searchQuery | 在社区内搜索帖子 |
list_member_extractor | targetListId | 某列表的成员 |
list_post_extractor | targetListId | 某列表的帖子 |
list_follower_explorer | targetListId | 某列表的关注者 |
space_explorer | targetSpaceId | 某 Space 的参与者 |
people_search | searchQuery | 按关键词搜索用户 |
tweet_search_extractor | searchQuery | 按关键词或话题标签搜索并提取推文(批量,最多 1,000 条) |
// 步骤 1:运行前估算成本(如果只需要样本,可传递 resultsLimit)
const estimate = await xquikFetch("/extractions/estimate", {
method: "POST",
body: JSON.stringify({
toolType: "follower_explorer",
targetUsername: "elonmusk",
resultsLimit: 1000, // 可选:限制为 1,000 条结果而非全部
}),
});
// 响应:{ allowed: true, estimatedResults: 195000000, usagePercent: 12, projectedPercent: 98 }
if (!estimate.allowed) {
console.log("提取将超出月度配额");
return;
}
// 步骤 2:创建提取作业(传递相同的 resultsLimit 以匹配估算)
const job = await xquikFetch("/extractions", {
method: "POST",
body: JSON.stringify({
toolType: "follower_explorer",
targetUsername: "elonmusk",
resultsLimit: 1000,
}),
});
// 响应:{ id: "77777", toolType: "follower_explorer", status: "completed", totalResults: 195000 }
// 步骤 3:轮询直至完成(大型作业可能返回 "running" 状态)
while (job.status === "pending" || job.status === "running") {
await new Promise((r) => setTimeout(r, 2000));
job = await xquikFetch(`/extractions/${job.id}`);
}
// 步骤 4:检索分页结果(每页最多 1,000 条)
let cursor;
const allResults = [];
while (true) {
const path = `/extractions/${job.id}${cursor ? `?after=${cursor}` : ""}`;
const page = await xquikFetch(path);
allResults.push(...page.results);
// 每条结果:{ xUserId, xUsername, xDisplayName, xFollowersCount, xVerified, xProfileImageUrl }
if (!page.hasMore) break;
cursor = page.nextCursor;
}
// 步骤 5:导出为 CSV/XLSX/Markdown(50,000 行限制)
const exportUrl = `${BASE}/extractions/${job.id}/export?format=csv`;
const csvResponse = await fetch(exportUrl, { headers });
const csvData = await csvResponse.text();
在构建结合多种提取工具(例如市场调研)的应用程序时,请按顺序运行它们并遵守速率限制:
async function marketResearchPipeline(username) {
// 1. 获取用户资料
const user = await xquikFetch(`/x/users/${username}`);
// 2. 提取其近期推文
const postsJob = await xquikFetch("/extractions", {
method: "POST",
body: JSON.stringify({ toolType: "post_extractor", targetUsername: username }),
});
// 3. 搜索相关对话
const tweets = await xquikFetch(`/x/tweets/search?q=from:${username}`);
// 4. 对于热门推文,提取回复进行情感分析
for (const tweet of tweets.tweets.slice(0, 5)) {
const estimate = await xquikFetch("/extractions/estimate", {
method: "POST",
body: JSON.stringify({ toolType: "reply_extractor", targetTweetId: tweet.id }),
});
if (estimate.allowed) {
const repliesJob = await xquikFetch("/extractions", {
method: "POST",
body: JSON.stringify({ toolType: "reply_extractor", targetTweetId: tweet.id }),
});
// 处理回复...
}
}
// 5. 获取热门话题作为背景
const trends = await xquikFetch("/trends?woeid=1");
return { user, posts: postsJob, tweets, trends };
}
通过可配置的过滤器,从推文回复中运行透明、可审计的抽奖活动。
POST /draws,包含 tweetUrl(必需)和可选过滤器:
| 字段 | 类型 | 描述 |
|---|---|---|
tweetUrl | string | 必需。 完整推文 URL:https://x.com/user/status/ID |
winnerCount | number | 要选择的获胜者数量(默认为 1) |
backupCount | number | 要选择的备用获胜者数量 |
uniqueAuthorsOnly | boolean | 每个作者仅计一个条目 |
mustRetweet | boolean | 要求参与者必须转推 |
mustFollowUsername | string | 参与者必须关注的用户名 |
filterMinFollowers | number | 最低粉丝数 |
filterAccountAgeDays | number | 最低账户年龄(天数) |
filterLanguage | string | 语言代码(例如 "en") |
requiredKeywords | string[] | 回复中必须出现的关键词 |
requiredHashtags | string[] | 回复中必须出现的话题标签(例如 ["#giveaway"]) |
requiredMentions | string[] | 回复中必须提及的用户名(例如 ["@xquik"]) |
// 步骤 1:使用过滤器创建抽奖
const draw = await xquikFetch("/draws", {
method: "POST",
body: JSON.stringify({
tweetUrl: "https://x.com/burakbayir/status/1893456789012345678",
winnerCount: 3,
backupCount: 2,
uniqueAuthorsOnly: true,
mustRetweet: true,
mustFollowUsername: "burakbayir",
filterMinFollowers: 50,
filterAccountAgeDays: 30,
filterLanguage: "en",
requiredHashtags: ["#giveaway"],
}),
});
// 响应:
// {
// id: "42",
// tweetId: "1893456789012345678",
// tweetUrl: "https://x.com/burakbayir/status/1893456789012345678",
// tweetText: "Giveaway! RT + Follow to enter...",
// tweetAuthorUsername: "burakbayir",
// tweetLikeCount: 5200,
// tweetRetweetCount: 3100,
// tweetReplyCount: 890,
// tweetQuoteCount: 45,
// status: "completed",
// totalEntries: 890,
// validEntries: 312,
// createdAt: "2026-02-24T10:00:00.000Z",
// drawnAt: "2026-02-24T10:01:00.000Z"
// }
// 步骤 2:获取包含获胜者的抽奖详情
const details = await xquikFetch(`/draws/${draw.id}`);
// details.winners: [
// { position: 1, authorUsername: "winner1", tweetId: "...", isBackup: false },
// { position: 2, authorUsername: "winner2", tweetId: "...", isBackup: false },
// { position: 3, authorUsername: "winner3", tweetId: "...", isBackup: false },
// { position: 4, authorUsername: "backup1", tweetId: "...", isBackup: true },
// { position: 5, authorUsername: "backup2", tweetId: "...", isBackup: true },
// ]
// 步骤 3:导出结果
const exportUrl = `${BASE}/draws/${draw.id}/export?format=csv`;
Webhook 通过 HMAC-SHA256 签名将事件投递到您的 HTTPS 端点。每次投递都是一个 POST 请求,包含 X-Xquik-Signature 请求头和包含 eventType、username 及 data 的 JSON 主体。
import express from "express";
import { createHmac, timingSafeEqual, createHash } from "node:crypto";
const WEBHOOK_SECRET = process.env.XQUIK_WEBHOOK_SECRET;
const processedHashes = new Set(); // 生产环境中使用 Redis/DB
function verifySignature(payload, signature, secret) {
const expected = "sha256=" + createHmac("sha256", secret).update(payload).digest("hex");
return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
const app = express();
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.headers["x-xquik-signature"];
const payload = req.body.toString();
// 1. 验证 HMAC 签名(恒定时间比较)
if (!signature || !verifySignature(payload, signature, WEBHOOK_SECRET)) {
return res.status(401).send("签名无效");
}
// 2. 去重(重试可能导致同一事件投递两次)
const payloadHash = createHash("sha256").update(payload).digest("hex");
if (processedHashes.has(payloadHash)) {
return res.status(200).send("已处理");
}
processedHashes.add(payloadHash);
// 3. 解析并按事件类型路由
const event = JSON.parse(payload);
// event.eventType: "tweet.new" | "tweet.reply" | "tweet.quote" | "tweet.retweet" | "follower.gained" | "follower.lost"
// event.username: 被监控账户的用户名
// event.data: 推文数据({ tweetId, text, metrics })或粉丝数据({ followerId, followerUsername, followerName, followerFollowersCount, followerVerified })
// 4. 在 10 秒内响应(如果处理慢,则异步处理)
res.status(200).send("OK");
});
app.listen(3000);
有关 Flask(Python)webhook 处理程序,请参阅 references/python-examples.md。
Webhook 安全规则:
200;将慢速处理排队为异步任务通过 GET /webhooks/{id}/deliveries 检查投递状态,以监控成功和失败的尝试。
完整的端到端流程:创建监控器、注册 webhook、处理事件。
// 1. 创建监控器
const monitor = await xquikFetch("/monitors", {
method: "POST",
body: JSON.stringify({
username: "elonmusk",
eventTypes: ["tweet.new", "tweet.reply", "tweet.quote", "follower.gained"],
}),
});
// 响应:{ id: "7", username: "elonmusk", xUserId: "44196397", eventTypes: [...], createdAt: "..." }
// 2. 注册 webhook
const webhook = await xquikFetch("/webhooks", {
method: "POST",
body: JSON.stringify({
url: "https://your-server.com/webhook",
eventTypes: ["tweet.new", "tweet.reply"],
}),
});
// 重要:保存 webhook.secret。它仅显示一次!
// 3. 轮询事件(webhook 的替代方案)
const events = await xquikFetch("/events?monitorId=7&limit=50");
// 响应:{ events: [...], hasMore: false }
事件类型:tweet.new、tweet.quote、tweet.reply、tweet.retweet、follower.gained、follower.lost。
位于 https://xquik.com/mcp 的 MCP 服务器使用代码执行沙盒模型,包含 2 个工具(explore + xquik)。代理编写异步 JavaScript 箭头函数,这些函数在沙盒环境中运行,并自动注入认证。使用 StreamableHTTP 传输。CLI/IDE 客户端使用 API 密钥认证(x-api-key 请求头);Web 客户端(Claude.ai、ChatGPT 开发者模式)使用 OAuth 2.1。沙盒涵盖 12 个类别中的所有 97 个 REST API 端点。支持的平台:Claude.ai、Claude Desktop、Claude Code、ChatGPT(自定义 GPT、Agents SDK、开发者模式)、Codex CLI、Cursor、VS Code、Windsurf、OpenCode。
有关各平台的设置配置,请阅读 references/mcp-setup.md。有关工具详情、选择规则、常见错误和不支持的操作,请阅读 references/mcp-tools.md。
| MCP 服务器 | REST API
---|---|---
最适合 | AI 代理、IDE 集成 | 自定义应用、脚本、后端服务
模型 | 2 个工具(explore + xquik),带代码执行沙盒 | 97 个独立端点
类别 | 12 个:账户、自动化、机器人、撰写、提取、集成、媒体、监控、支持、twitter、x-accounts、x-write | 相同
覆盖范围 | 完整 — xquik 工具可调用任何 REST 端点 | 直接 HTTP 调用
文件导出 | 不可用 | CSV、XLSX、Markdown
REST 独有功能 | - | API 密钥管理、文件导出(CSV/XLSX/MD)、账户区域设置更新、自动化步骤位置批量更新
常见的多步骤序列(均通过 xquik 工具调用 REST 端点):
POST /monitors -> POST /webhooks -> POST /webhooks/{id}/testGET /account(检查预算)-> POST /drawsPOST /extractions/estimate -> POST /extractions -> GET /extractions/{id}GET /x/tweets/{id}(指标)-> POST /extractions 配合 thread_extractorGET /x/users/{username} -> GET /x/tweets/search?q=from:username -> GET /x/tweets/{id}POST /compose(step=compose)-> AI 询问后续问题 ->(step=refine)-> AI 起草 ->(step=score)-> 迭代POST /styles(获取并缓存)-> GET /styles/{username}(参考)-> POST /compose 配合 styleUsernamePOST /styles 针对两个账户 -> GET /styles/comparePOST /styles(缓存推文)-> GET /styles/{username}/performance(实时指标)POST /compose -> POST /drafts -> GET /drafts -> DELETE /drafts/{id}POST /x/media/download(返回永久托管的 URL)GET /radar(7 个来源,免费)-> POST /compose 配合热门话题POST /subscribe(返回 Stripe URL)POST /x/accounts(连接)-> POST /x/tweets 配合 account + text(可选先执行 POST /x/media)POST /x/tweets/{id}/like、POST /x/tweets/{id}/retweet、POST /x/users/{id}/followPOST /integrations(type=telegram, chatId, eventTypes)-> POST /integrations/{id}/testPOST /automations(name, triggerType, triggerConfig)-> POST /automations/{slug}/steps(添加操作)-> PATCH /automations/{slug}(激活)POST /support/tickets(subject, body)-> GET /support/tickets/{id}(检查状态)-> POST /support/tickets/{id}/messages(回复)402 usage_limit_reached(如果启用了额外用量且达到消费限制,则返回 402 overage_limit_reached)GET /account 返回 usagePercent(0-100)2026-02-24T10:30:00.000Z{ "error": "error_code" }nextCursor 作为 after 查询参数传递,切勿解码csv、xlsx、md,通过 GET /extractions/{id}/export?format=csv 或 GET /draws/{id}/export?format=csv&type=winners有关本指南之外的更多详细信息:
references/mcp-tools.md:MCP 工具选择规则、工作流模式、常见错误和不支持的操作references/api-endpoints.md:所有 REST API 端点,包含方法、路径、参数和响应结构references/python-examples.md:所有 JavaScript 示例的 Python 等效代码(重试、提取、抽奖、webhook)references/webhooks.md:扩展的 webhook 示例、使用 ngrok 进行本地测试、投递状态监控references/mcp-setup.md:适用于 10 种 IDE 和 AI 代理平台的 MCP 服务器配置references/extractions.md:提取工具详情、导出列references/types.md:所有 REST API 和 MCP 输出对象的 TypeScript 类型定义每周安装量
355
代码仓库
GitHub 星标数
21
首次出现
2026年2月27日
安全审计
安装于
codex351
opencode351
cursor350
gemini-cli349
github-copilot349
cline348
Xquik is an X (Twitter) real-time data platform providing a REST API, HMAC webhooks, and an MCP server for AI agents. It covers account monitoring, bulk data extraction (20 tools), giveaway draws, tweet/user lookups, media downloads, follow checks, trending topics, flow automations (trigger-driven workflows), support tickets, write actions (tweet, like, retweet, follow, DM, profile, media upload, communities), and Telegram integrations.
|
---|---
Base URL | https://xquik.com/api/v1
Auth | x-api-key: xq_... header (64 hex chars after xq_ prefix)
MCP endpoint | https://xquik.com/mcp (StreamableHTTP, same API key)
Rate limits | 10 req/s sustained, 20 burst (API); 60 req/s sustained, 100 burst (general)
Pricing | $20/month base (1 monitor included), $5/month per extra monitor
Quota | Monthly usage cap. 402 when exhausted. Enable extra usage from dashboard for overage (tiered spending limits: $5/$7/$10/$15/$25)
Docs | docs.xquik.com
HTTPS only | Plain HTTP gets 301 redirect
Every request requires an API key via the x-api-key header. Keys start with xq_ and are generated from the Xquik dashboard. The key is shown only once at creation; store it securely.
const API_KEY = "xq_YOUR_KEY_HERE";
const BASE = "https://xquik.com/api/v1";
const headers = { "x-api-key": API_KEY, "Content-Type": "application/json" };
For Python examples, see references/python-examples.md.
| Goal | Endpoint | Notes |
|---|---|---|
| Get a single tweet by ID/URL | GET /x/tweets/{id} | Full metrics: likes, retweets, views, bookmarks, author info |
| Get an X Article by tweet ID | GET /x/articles/{id} | Long-form post: title, body, cover image, engagement metrics |
| Search tweets by keyword/hashtag | GET /x/tweets/search?q=... | Tweet info with optional engagement metrics (likeCount, retweetCount, replyCount) |
| Get a user profile | GET /x/users/{username} | Name, bio, follower/following counts, profile picture, location, created date, statuses count |
All errors return { "error": "error_code" }. Key error codes:
| Status | Code | Action |
|---|---|---|
| 400 | invalid_input, invalid_id, invalid_params, invalid_tweet_url, invalid_tweet_id, invalid_username, invalid_tool_type, invalid_format, missing_query, , , |
Retry only 429 and 5xx. Never retry 4xx (except 429). Max 3 retries with exponential backoff:
async function xquikFetch(path, options = {}) {
const baseDelay = 1000;
for (let attempt = 0; attempt <= 3; attempt++) {
const response = await fetch(`${BASE}${path}`, {
...options,
headers: { ...headers, ...options.headers },
});
if (response.ok) return response.json();
const retryable = response.status === 429 || response.status >= 500;
if (!retryable || attempt === 3) {
const error = await response.json();
throw new Error(`Xquik API ${response.status}: ${error.error}`);
}
const retryAfter = response.headers.get("Retry-After");
const delay = retryAfter
? parseInt(retryAfter, 10) * 1000
: baseDelay * Math.pow(2, attempt) + Math.random() * 1000;
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
Events, draws, extractions, and extraction results use cursor-based pagination. When more results exist, the response includes hasMore: true and a nextCursor string. Pass nextCursor as the after query parameter.
async function fetchAllPages(path, dataKey) {
const results = [];
let cursor;
while (true) {
const params = new URLSearchParams({ limit: "100" });
if (cursor) params.set("after", cursor);
const data = await xquikFetch(`${path}?${params}`);
results.push(...data[dataKey]);
if (!data.hasMore) break;
cursor = data.nextCursor;
}
return results;
}
Cursors are opaque strings. Never decode or construct them manually.
Extractions run bulk data collection jobs. The complete workflow: estimate cost, create job, retrieve results, optionally export.
| Tool Type | Required Field | Description |
|---|---|---|
reply_extractor | targetTweetId | Users who replied to a tweet |
repost_extractor | targetTweetId | Users who retweeted a tweet |
quote_extractor | targetTweetId | Users who quote-tweeted a tweet |
thread_extractor |
// Step 1: Estimate cost before running (pass resultsLimit if you only need a sample)
const estimate = await xquikFetch("/extractions/estimate", {
method: "POST",
body: JSON.stringify({
toolType: "follower_explorer",
targetUsername: "elonmusk",
resultsLimit: 1000, // optional: limit to 1,000 results instead of all
}),
});
// Response: { allowed: true, estimatedResults: 195000000, usagePercent: 12, projectedPercent: 98 }
if (!estimate.allowed) {
console.log("Extraction would exceed monthly quota");
return;
}
// Step 2: Create extraction job (pass same resultsLimit to match estimate)
const job = await xquikFetch("/extractions", {
method: "POST",
body: JSON.stringify({
toolType: "follower_explorer",
targetUsername: "elonmusk",
resultsLimit: 1000,
}),
});
// Response: { id: "77777", toolType: "follower_explorer", status: "completed", totalResults: 195000 }
// Step 3: Poll until complete (large jobs may return status "running")
while (job.status === "pending" || job.status === "running") {
await new Promise((r) => setTimeout(r, 2000));
job = await xquikFetch(`/extractions/${job.id}`);
}
// Step 4: Retrieve paginated results (up to 1,000 per page)
let cursor;
const allResults = [];
while (true) {
const path = `/extractions/${job.id}${cursor ? `?after=${cursor}` : ""}`;
const page = await xquikFetch(path);
allResults.push(...page.results);
// Each result: { xUserId, xUsername, xDisplayName, xFollowersCount, xVerified, xProfileImageUrl }
if (!page.hasMore) break;
cursor = page.nextCursor;
}
// Step 5: Export as CSV/XLSX/Markdown (50,000 row limit)
const exportUrl = `${BASE}/extractions/${job.id}/export?format=csv`;
const csvResponse = await fetch(exportUrl, { headers });
const csvData = await csvResponse.text();
When building applications that combine multiple extraction tools (e.g., market research), run them sequentially and respect rate limits:
async function marketResearchPipeline(username) {
// 1. Get user profile
const user = await xquikFetch(`/x/users/${username}`);
// 2. Extract their recent posts
const postsJob = await xquikFetch("/extractions", {
method: "POST",
body: JSON.stringify({ toolType: "post_extractor", targetUsername: username }),
});
// 3. Search for related conversations
const tweets = await xquikFetch(`/x/tweets/search?q=from:${username}`);
// 4. For top tweets, extract replies for sentiment analysis
for (const tweet of tweets.tweets.slice(0, 5)) {
const estimate = await xquikFetch("/extractions/estimate", {
method: "POST",
body: JSON.stringify({ toolType: "reply_extractor", targetTweetId: tweet.id }),
});
if (estimate.allowed) {
const repliesJob = await xquikFetch("/extractions", {
method: "POST",
body: JSON.stringify({ toolType: "reply_extractor", targetTweetId: tweet.id }),
});
// Process replies...
}
}
// 5. Get trending topics for context
const trends = await xquikFetch("/trends?woeid=1");
return { user, posts: postsJob, tweets, trends };
}
Run transparent, auditable giveaway draws from tweet replies with configurable filters.
POST /draws with a tweetUrl (required) and optional filters:
| Field | Type | Description |
|---|---|---|
tweetUrl | string | Required. Full tweet URL: https://x.com/user/status/ID |
winnerCount | number | Winners to select (default 1) |
backupCount | number | Backup winners to select |
uniqueAuthorsOnly | boolean | Count only one entry per author |
// Step 1: Create draw with filters
const draw = await xquikFetch("/draws", {
method: "POST",
body: JSON.stringify({
tweetUrl: "https://x.com/burakbayir/status/1893456789012345678",
winnerCount: 3,
backupCount: 2,
uniqueAuthorsOnly: true,
mustRetweet: true,
mustFollowUsername: "burakbayir",
filterMinFollowers: 50,
filterAccountAgeDays: 30,
filterLanguage: "en",
requiredHashtags: ["#giveaway"],
}),
});
// Response:
// {
// id: "42",
// tweetId: "1893456789012345678",
// tweetUrl: "https://x.com/burakbayir/status/1893456789012345678",
// tweetText: "Giveaway! RT + Follow to enter...",
// tweetAuthorUsername: "burakbayir",
// tweetLikeCount: 5200,
// tweetRetweetCount: 3100,
// tweetReplyCount: 890,
// tweetQuoteCount: 45,
// status: "completed",
// totalEntries: 890,
// validEntries: 312,
// createdAt: "2026-02-24T10:00:00.000Z",
// drawnAt: "2026-02-24T10:01:00.000Z"
// }
// Step 2: Get draw details with winners
const details = await xquikFetch(`/draws/${draw.id}`);
// details.winners: [
// { position: 1, authorUsername: "winner1", tweetId: "...", isBackup: false },
// { position: 2, authorUsername: "winner2", tweetId: "...", isBackup: false },
// { position: 3, authorUsername: "winner3", tweetId: "...", isBackup: false },
// { position: 4, authorUsername: "backup1", tweetId: "...", isBackup: true },
// { position: 5, authorUsername: "backup2", tweetId: "...", isBackup: true },
// ]
// Step 3: Export results
const exportUrl = `${BASE}/draws/${draw.id}/export?format=csv`;
Webhooks deliver events to your HTTPS endpoint with HMAC-SHA256 signatures. Each delivery is a POST with X-Xquik-Signature header and JSON body containing eventType, username, and data.
import express from "express";
import { createHmac, timingSafeEqual, createHash } from "node:crypto";
const WEBHOOK_SECRET = process.env.XQUIK_WEBHOOK_SECRET;
const processedHashes = new Set(); // Use Redis/DB in production
function verifySignature(payload, signature, secret) {
const expected = "sha256=" + createHmac("sha256", secret).update(payload).digest("hex");
return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
const app = express();
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.headers["x-xquik-signature"];
const payload = req.body.toString();
// 1. Verify HMAC signature (constant-time comparison)
if (!signature || !verifySignature(payload, signature, WEBHOOK_SECRET)) {
return res.status(401).send("Invalid signature");
}
// 2. Deduplicate (retries can deliver the same event twice)
const payloadHash = createHash("sha256").update(payload).digest("hex");
if (processedHashes.has(payloadHash)) {
return res.status(200).send("Already processed");
}
processedHashes.add(payloadHash);
// 3. Parse and route by event type
const event = JSON.parse(payload);
// event.eventType: "tweet.new" | "tweet.reply" | "tweet.quote" | "tweet.retweet" | "follower.gained" | "follower.lost"
// event.username: monitored account username
// event.data: tweet data ({ tweetId, text, metrics }) or follower data ({ followerId, followerUsername, followerName, followerFollowersCount, followerVerified })
// 4. Respond within 10 seconds (process async if slow)
res.status(200).send("OK");
});
app.listen(3000);
For Flask (Python) webhook handler, see references/python-examples.md.
Webhook security rules:
200 within 10 seconds; queue slow processing for asyncCheck delivery status via GET /webhooks/{id}/deliveries to monitor successful and failed attempts.
Complete end-to-end: create monitor, register webhook, handle events.
// 1. Create monitor
const monitor = await xquikFetch("/monitors", {
method: "POST",
body: JSON.stringify({
username: "elonmusk",
eventTypes: ["tweet.new", "tweet.reply", "tweet.quote", "follower.gained"],
}),
});
// Response: { id: "7", username: "elonmusk", xUserId: "44196397", eventTypes: [...], createdAt: "..." }
// 2. Register webhook
const webhook = await xquikFetch("/webhooks", {
method: "POST",
body: JSON.stringify({
url: "https://your-server.com/webhook",
eventTypes: ["tweet.new", "tweet.reply"],
}),
});
// IMPORTANT: Save webhook.secret. It is shown only once!
// 3. Poll events (alternative to webhooks)
const events = await xquikFetch("/events?monitorId=7&limit=50");
// Response: { events: [...], hasMore: false }
Event types: tweet.new, tweet.quote, tweet.reply, tweet.retweet, follower.gained, follower.lost.
The MCP server at https://xquik.com/mcp uses a code-execution sandbox model with 2 tools (explore + xquik). The agent writes async JavaScript arrow functions that run in a sandboxed environment with auth injected automatically. StreamableHTTP transport. API key auth (x-api-key header) for CLI/IDE clients; OAuth 2.1 for web clients (Claude.ai, ChatGPT Developer Mode). The sandbox covers all 97 REST API endpoints across 12 categories. Supported platforms: Claude.ai, Claude Desktop, Claude Code, ChatGPT (Custom GPT, Agents SDK, Developer Mode), Codex CLI, Cursor, VS Code, Windsurf, OpenCode.
For setup configs per platform, read references/mcp-setup.md. For tool details with selection rules, common mistakes, and unsupported operations, read references/mcp-tools.md.
| MCP Server | REST API
---|---|---
Best for | AI agents, IDE integrations | Custom apps, scripts, backend services
Model | 2 tools (explore + xquik) with code-execution sandbox | 97 individual endpoints
Categories | 12: account, automations, bot, composition, extraction, integrations, media, monitoring, support, twitter, x-accounts, x-write | Same
Coverage | Full — xquik tool calls any REST endpoint | Direct HTTP calls
File export | Not available | CSV, XLSX, Markdown
Unique to REST | - | API key management, file export (CSV/XLSX/MD), account locale update, automation step position batch updates
Common multi-step sequences (all via xquik tool calling REST endpoints):
POST /monitors -> POST /webhooks -> POST /webhooks/{id}/testGET /account (check budget) -> POST /drawsPOST /extractions/estimate -> POST /extractions -> GET /extractions/{id}GET /x/tweets/{id} (metrics) -> POST /extractions with 402 usage_limit_reached when included allowance exhausted (or 402 overage_limit_reached if extra usage is active and spending limit reached)GET /account returns usagePercent (0-100)2026-02-24T10:30:00.000Z{ "error": "error_code" }nextCursor as the after query parameter, never decodecsv, xlsx, md via GET /extractions/{id}/export?format=csv or GET /draws/{id}/export?format=csv&type=winnersFor additional detail beyond this guide:
references/mcp-tools.md : MCP tool selection rules, workflow patterns, common mistakes, and unsupported operationsreferences/api-endpoints.md : All REST API endpoints with methods, paths, parameters, and response shapesreferences/python-examples.md : Python equivalents of all JavaScript examples (retry, extraction, draw, webhook)references/webhooks.md : Extended webhook examples, local testing with ngrok, delivery status monitoringreferences/mcp-setup.md : MCP server configuration for 10 IDEs and AI agent platformsreferences/extractions.md : Extraction tool details, export columnsreferences/types.md : TypeScript type definitions for all REST API and MCP output objectsWeekly Installs
355
Repository
GitHub Stars
21
First Seen
Feb 27, 2026
Security Audits
Gen Agent Trust HubPassSocketWarnSnykWarn
Installed on
codex351
opencode351
cursor350
gemini-cli349
github-copilot349
cline348
Python PDF处理教程:合并拆分、提取文本表格、创建PDF文件
55,400 周安装
Bats测试模式:Shell脚本单元测试指南与最佳实践
3,300 周安装
Vue JSX 最佳实践指南:与React JSX差异详解及代码迁移方案
3,300 周安装
Hyperliquid 交易技能:在链上 DEX 交易永续合约与现货,AI 代理自动执行
3,300 周安装
Nuxt UI v4 组件库 | Vue 3 & Nuxt 4+ 开发必备 | 基于 Tailwind CSS
3,300 周安装
UI/UX Pro Max 设计指南:避免AI生成感,打造高级SaaS/着陆页设计原则与实现
3,400 周安装
Three.js 动画教程 - 从程序化动画到关键帧系统完整指南
3,300 周安装
| Check follow relationship | GET /x/followers/check?source=A&target=B | Both directions |
| Get trending topics | GET /trends?woeid=1 | Regional trends by WOEID. Metered |
| Get radar (trending news) | GET /radar?source=hacker_news | Free, 7 sources: Google Trends, Hacker News, Polymarket, TrustMRR, Wikipedia, GitHub, Reddit |
| Monitor an X account | POST /monitors | Track tweets, replies, quotes, retweets, follower changes |
| Update monitor event types | PATCH /monitors/{id} | Change subscribed events or pause/resume |
| Poll for events | GET /events | Cursor-paginated, filter by monitorId/eventType |
| Receive events in real time | POST /webhooks | HMAC-signed delivery to your HTTPS endpoint |
| Update webhook | PATCH /webhooks/{id} | Change URL, event types, or pause/resume |
| Run a giveaway draw | POST /draws | Pick random winners from tweet replies |
| Download tweet media | POST /x/media/download | Single (tweetInput) or bulk (tweetIds[], up to 50). Returns gallery URL. First download metered, cached free |
| Extract bulk data | POST /extractions | 20 tool types, always estimate cost first |
| Check account/usage | GET /account | Plan status, monitors, usage percent |
| Link your X identity | PUT /account/x-identity | Required for own-account detection in style analysis |
| Analyze tweet style | POST /styles | Cache recent tweets for style reference |
| Save custom style | PUT /styles/{username} | Save custom style from tweet texts (free) |
| Get cached style | GET /styles/{username} | Retrieve previously cached tweet style |
| Compare styles | GET /styles/compare?username1=A&username2=B | Side-by-side comparison of two cached styles |
| Get tweet performance | GET /styles/{username}/performance | Live engagement metrics for cached tweets |
| Save a tweet draft | POST /drafts | Store drafts for later |
| List/manage drafts | GET /drafts, DELETE /drafts/{id} | Retrieve and delete saved drafts |
| Compose a tweet | POST /compose | 3-step workflow (compose, refine, score). Free, algorithm-backed |
| Connect an X account | POST /x/accounts | Credentials encrypted at rest. Required for write actions |
| List connected accounts | GET /x/accounts | Free |
| Re-authenticate account | POST /x/accounts/{id}/reauth | When session expires |
| Post a tweet | POST /x/tweets | From a connected account. Supports replies, media, note tweets, communities |
| Delete a tweet | DELETE /x/tweets/{id} | Must own the tweet via connected account |
| Like / Unlike a tweet | POST / DELETE /x/tweets/{id}/like | Metered |
| Retweet | POST /x/tweets/{id}/retweet | Metered |
| Follow / Unfollow a user | POST / DELETE /x/users/{id}/follow | Metered |
| Send a DM | POST /x/dm/{userId} | Text, media, reply to message |
| Update profile | PATCH /x/profile | Name, bio, location, URL |
| Upload media | POST /x/media | FormData or JSON (url param). Returns media ID for tweet attachment |
| Community actions | POST /x/communities, POST /x/communities/{id}/join | Create, delete, join, leave |
| Create Telegram integration | POST /integrations | Receive monitor events in Telegram. Free |
| Manage integrations | GET /integrations, PATCH /integrations/{id} | List, update, delete, test, deliveries. Free |
| Create automation flow | POST /automations | Trigger-driven workflows: monitor events, schedules, search, inbound webhooks |
| Manage automation flows | GET /automations, PATCH /automations/{slug} | List, update, delete, activate/deactivate. Free |
| Add automation steps | POST /automations/{slug}/steps | Action, condition, or extraction steps (max 10 per flow) |
| Trigger flow via webhook | POST /webhooks/inbound/{token} | No auth needed. Token identifies the flow |
| Open support ticket | POST /support/tickets | Free for all authenticated users |
| Manage support tickets | GET /support/tickets, POST /support/tickets/{id}/messages | List, read, reply. Free |
missing_paramswebhook_inactiveno_media| Fix the request, do not retry |
| 401 | unauthenticated | Check API key |
| 402 | no_subscription, subscription_inactive, usage_limit_reached, no_addon, extra_usage_disabled, extra_usage_requires_v2, frozen, overage_limit_reached | Subscribe, enable extra usage, or wait for quota reset |
| 403 | monitor_limit_reached, api_key_limit_reached, flow_limit_reached, step_limit_reached | Delete a monitor/key/flow or add capacity |
| 404 | not_found, user_not_found, tweet_not_found, style_not_found, draft_not_found, account_not_found | Resource doesn't exist or belongs to another account |
| 403 | account_needs_reauth | Connected X account needs re-authentication |
| 409 | monitor_already_exists, account_already_connected, conflict | Resource already exists or concurrent edit conflict |
| 422 | login_failed | X credential verification failed. Check credentials |
| 429 | x_api_rate_limited | Rate limited. Retry with exponential backoff, respect Retry-After header |
| 500 | internal_error | Retry with backoff |
| 502 | stream_registration_failed, x_api_unavailable, x_api_unauthorized, delivery_failed | Retry with backoff |
targetTweetId |
| All tweets in a thread |
article_extractor | targetTweetId | Article content linked in a tweet |
follower_explorer | targetUsername | Followers of an account |
following_explorer | targetUsername | Accounts followed by a user |
verified_follower_explorer | targetUsername | Verified followers of an account |
mention_extractor | targetUsername | Tweets mentioning an account |
post_extractor | targetUsername | Posts from an account |
community_extractor | targetCommunityId | Members of a community |
community_moderator_explorer | targetCommunityId | Moderators of a community |
community_post_extractor | targetCommunityId | Posts from a community |
community_search | targetCommunityId + searchQuery | Search posts within a community |
list_member_extractor | targetListId | Members of a list |
list_post_extractor | targetListId | Posts from a list |
list_follower_explorer | targetListId | Followers of a list |
space_explorer | targetSpaceId | Participants of a Space |
people_search | searchQuery | Search for users by keyword |
tweet_search_extractor | searchQuery | Search and extract tweets by keyword or hashtag (bulk, up to 1,000) |
mustRetweet| boolean |
| Require participants to have retweeted |
mustFollowUsername | string | Username participants must follow |
filterMinFollowers | number | Minimum follower count |
filterAccountAgeDays | number | Minimum account age in days |
filterLanguage | string | Language code (e.g., "en") |
requiredKeywords | string[] | Words that must appear in the reply |
requiredHashtags | string[] | Hashtags that must appear (e.g., ["#giveaway"]) |
requiredMentions | string[] | Usernames that must be mentioned (e.g., ["@xquik"]) |
thread_extractorGET /x/users/{username} -> GET /x/tweets/search?q=from:username -> GET /x/tweets/{id}POST /compose (step=compose) -> AI asks follow-ups -> (step=refine) -> AI drafts -> (step=score) -> iteratePOST /styles (fetch & cache) -> GET /styles/{username} (reference) -> POST /compose with styleUsernamePOST /styles for both accounts -> GET /styles/comparePOST /styles (cache tweets) -> GET /styles/{username}/performance (live metrics)POST /compose -> POST /drafts -> GET /drafts -> DELETE /drafts/{id}POST /x/media/download (returns permanent hosted URLs)GET /radar (7 sources, free) -> POST /compose with trending topicPOST /subscribe (returns Stripe URL)POST /x/accounts (connect) -> POST /x/tweets with account + text (optionally POST /x/media first)POST /x/tweets/{id}/like, POST /x/tweets/{id}/retweet, POST /x/users/{id}/followPOST /integrations (type=telegram, chatId, eventTypes) -> POST /integrations/{id}/testPOST /automations (name, triggerType, triggerConfig) -> POST /automations/{slug}/steps (add actions) -> PATCH /automations/{slug} (activate)POST /support/tickets (subject, body) -> GET /support/tickets/{id} (check status) -> POST /support/tickets/{id}/messages (reply)