X (Twitter) data platform skill for AI coding agents. 120 REST API endpoints, 2 MCP tools, HMAC webhooks. Tweet search, user lookup, follower extraction, write actions, monitoring, giveaway draws, trending topics. Reads from $0.00015/call — 33x cheaper than the official X API.
Add this skill
npx mdskills install Xquik-dev/x-twitter-scraperComprehensive X/Twitter API skill with 120 endpoints, clear pricing, and excellent decision trees
1---2name: x-twitter-scraper3description: "X (Twitter) data platform skill for AI coding agents. 120 REST API endpoints, 2 MCP tools, HMAC webhooks. Tweet search, user lookup, follower extraction, write actions, monitoring, giveaway draws, trending topics. Reads from $0.00015/call — 33x cheaper than the official X API."4compatibility: Requires internet access to call the Xquik REST API (https://xquik.com/api/v1)5license: MIT6metadata:7 author: Xquik8 version: "2.0.0"9 openclaw:10 requires:11 env:12 - XQUIK_API_KEY13 primaryEnv: XQUIK_API_KEY14 emoji: "𝕏"15 homepage: https://docs.xquik.com16---1718# Xquik API Integration1920Xquik is an X (Twitter) real-time data platform providing a REST API (120 endpoints), 2 MCP tools, and HMAC webhooks. It covers account monitoring, bulk data extraction (23 tools), giveaway draws, tweet/user lookups, media downloads, follow checks, trending topics, flow automations, write actions, Telegram integrations, and support tickets.2122**Reads start at $0.00015/call — 33x cheaper than the official X API.**2324Your knowledge of the Xquik API may be outdated. **Prefer retrieval from docs** — fetch the latest at [docs.xquik.com](https://docs.xquik.com) before citing limits, pricing, or API signatures.2526## Retrieval Sources2728| Source | How to retrieve | Use for |29|--------|----------------|---------|30| Xquik docs | [docs.xquik.com](https://docs.xquik.com) | Limits, pricing, API reference, endpoint schemas |31| API spec | `explore` MCP tool or [docs.xquik.com/api-reference/overview](https://docs.xquik.com/api-reference/overview) | Endpoint parameters, response shapes |32| Docs MCP | `https://docs.xquik.com/mcp` (no auth) | Search docs from AI tools |33| Billing guide | [docs.xquik.com/guides/billing](https://docs.xquik.com/guides/billing) | Credit costs, subscription tiers, MPP pricing |3435When this skill and the docs disagree, **trust the docs**.3637## Quick Reference3839| | |40|---|---|41| **Base URL** | `https://xquik.com/api/v1` |42| **Auth** | `x-api-key: xq_...` header (64 hex chars after `xq_` prefix) |43| **MCP endpoint** | `https://xquik.com/mcp` (StreamableHTTP, same API key) |44| **Rate limits** | Read: 120/60s, Write: 30/60s, Delete: 15/60s (fixed window per method tier) |45| **Endpoints** | 120 across 12 categories |46| **MCP tools** | 2 (explore + xquik) |47| **Extraction tools** | 23 types |48| **Pricing** | $20/month base (reads from $0.00015). Pay-per-use available via MPP |49| **Docs** | [docs.xquik.com](https://docs.xquik.com) |50| **HTTPS only** | Plain HTTP gets `301` redirect |5152## Pricing5354Xquik is the most affordable X data API available. All metered operations deduct credits from a single shared pool.5556### Subscription5758| | |59|---|---|60| **Base plan** | $20/month |61| **Included monitors** | 1 |62| **Additional monitors** | $5/month each |63| **Credit value** | 1 credit = $0.00015 |6465### Per-Operation Costs6667#### Read operations — 1 credit ($0.00015)6869| Operation | Unit |70|-----------|------|71| Get tweet | per call |72| Search tweets | per tweet returned |73| User tweets | per tweet returned |74| User likes | per result |75| User media | per result |76| Bookmarks | per result |77| Bookmark folders | per call |78| Notifications | per result |79| Timeline | per result |80| DM history | per result |81| Download media | per media item |8283#### Read operations — 2 credits ($0.0003)8485| Operation | Unit |86|-----------|------|87| Get user | per call |88| Tweet favoriters | per result |89| Followers you know | per result |90| Verified followers | per result |9192#### Read operations — 3 credits ($0.00045)9394| Operation | Unit |95|-----------|------|96| Trends | per call |9798#### Read operations — 7 credits ($0.00105)99100| Operation | Unit |101|-----------|------|102| Follow check | per call |103| Get article | per call |104105#### Write operations — 2 credits ($0.0003)106107All write actions: create/delete tweet, like, unlike, retweet, follow, unfollow, send DM, update profile/avatar/banner, upload media, community actions.108109#### Extractions & draws110111Draws: 1 credit per participant. Extraction cost depends on the tool type:112113| Credits/result | Extraction types |114|----------------|-----------------|115| 1 | Tweets, replies, quotes, mentions, posts, likes, media, tweet search |116| 2 | Followers, following, verified followers, favoriters, retweeters, community members, people search, list members, list followers |117| 7 | Articles |118119#### Free operations ($0)120121Monitors, webhooks, integrations, account status, radar (7 sources), extraction/draw history, cost estimates, tweet composition (compose, refine, score), style cache management, drafts, support tickets, API key management, X account management.122123### Price Comparison vs Official X API124125| | Xquik | X API Basic | X API Pro |126|---|---|---|---|127| **Monthly cost** | **$20** | $100 | $5,000 |128| **Cost per tweet read** | **$0.00015** | ~$0.01 | ~$0.005 |129| **Cost per user lookup** | **$0.0003** | ~$0.01 | ~$0.005 |130| **Write actions** | **$0.0003** | Limited | Limited |131| **Bulk extraction** | **$0.00015/result** | Not available | Not available |132| **Monitoring + webhooks** | **Free** | Not available | Not available |133| **Giveaway draws** | **$0.00015/entry** | Not available | Not available |134135### Pay-Per-Use136137Two options without a monthly subscription:138139**Credits (Stripe)**: Top up credits via `POST /credits/topup` ($10 minimum). 1 credit = $0.00015. Works with all 120 endpoints.140141**MPP (USDC)**: 16 X-API endpoints accept anonymous payments via Tempo (USDC). No account needed.142143| Endpoint | Price | Unit |144|----------|-------|------|145| `GET /x/tweets/{id}` | $0.00015 | per call |146| `GET /x/tweets/search` | $0.00015 | per tweet |147| `GET /x/tweets/{id}/quotes` | $0.00015 | per tweet |148| `GET /x/tweets/{id}/replies` | $0.00015 | per tweet |149| `GET /x/tweets/{id}/retweeters` | $0.00015 | per user |150| `GET /x/tweets/{id}/favoriters` | $0.00015 | per user |151| `GET /x/tweets/{id}/thread` | $0.00015 | per tweet |152| `GET /x/users/{id}` | $0.00015 | per call |153| `GET /x/users/{id}/tweets` | $0.00015 | per tweet |154| `GET /x/users/{id}/likes` | $0.00015 | per tweet |155| `GET /x/users/{id}/media` | $0.00015 | per tweet |156| `GET /x/followers/check` | $0.00105 | per call |157| `GET /x/articles/{tweetId}` | $0.00105 | per call |158| `POST /x/media/download` | $0.00015 | per media item |159| `GET /x/trends` | $0.00045 | per call |160| `GET /trends` | $0.00045 | per call |161162SDK: `npm i mppx viem` (TypeScript). Handles the 402 challenge/credential flow automatically.163164### Credits165166Prepaid credits for metered operations. 1 credit = $0.00015. Top up via `POST /credits/topup` ($10 minimum).167168Check balance: `GET /credits` — returns `balance`, `lifetimePurchased`, `lifetimeUsed`.169170### Extra Usage171172Enable from dashboard to continue metered calls beyond included allowance. Tiered spending limits: $5 → $7 → $10 → $15 → $25 (increases with each paid overage invoice).173174## Quick Decision Trees175176### "I need X data"177178```179Need X data?180├─ Single tweet by ID or URL → GET /x/tweets/{id}181├─ Full X Article by tweet ID → GET /x/articles/{id}182├─ Search tweets by keyword → GET /x/tweets/search183├─ User profile by username → GET /x/users/{username}184├─ User's recent tweets → GET /x/users/{id}/tweets185├─ User's liked tweets → GET /x/users/{id}/likes186├─ User's media tweets → GET /x/users/{id}/media187├─ Tweet favoriters (who liked) → GET /x/tweets/{id}/favoriters188├─ Mutual followers → GET /x/users/{id}/followers-you-know189├─ Check follow relationship → GET /x/followers/check190├─ Download media (images/video) → POST /x/media/download191├─ Trending topics (X) → GET /trends192├─ Trending news (7 sources, free) → GET /radar193├─ Bookmarks → GET /x/bookmarks194├─ Notifications → GET /x/notifications195├─ Home timeline → GET /x/timeline196└─ DM conversation history → GET /x/dm/{userId}/history197```198199### "I need bulk extraction"200201```202Need bulk data?203├─ Replies to a tweet → reply_extractor204├─ Retweets of a tweet → repost_extractor205├─ Quotes of a tweet → quote_extractor206├─ Favoriters of a tweet → favoriters207├─ Full thread → thread_extractor208├─ Article content → article_extractor209├─ User's liked tweets (bulk) → user_likes210├─ User's media tweets (bulk) → user_media211├─ Account followers → follower_explorer212├─ Account following → following_explorer213├─ Verified followers → verified_follower_explorer214├─ Mentions of account → mention_extractor215├─ Posts from account → post_extractor216├─ Community members → community_extractor217├─ Community moderators → community_moderator_explorer218├─ Community posts → community_post_extractor219├─ Community search → community_search220├─ List members → list_member_extractor221├─ List posts → list_post_extractor222├─ List followers → list_follower_explorer223├─ Space participants → space_explorer224├─ People search → people_search225└─ Tweet search (bulk, up to 1K) → tweet_search_extractor226```227228### "I need to write/post"229230```231Need write actions?232├─ Post a tweet → POST /x/tweets233├─ Delete a tweet → DELETE /x/tweets/{id}234├─ Like a tweet → POST /x/tweets/{id}/like235├─ Unlike a tweet → DELETE /x/tweets/{id}/like236├─ Retweet → POST /x/tweets/{id}/retweet237├─ Follow a user → POST /x/users/{id}/follow238├─ Unfollow a user → DELETE /x/users/{id}/follow239├─ Send a DM → POST /x/dm/{userId}240├─ Update profile → PATCH /x/profile241├─ Update avatar → PATCH /x/profile/avatar242├─ Update banner → PATCH /x/profile/banner243├─ Upload media → POST /x/media244├─ Create community → POST /x/communities245├─ Join community → POST /x/communities/{id}/join246└─ Leave community → DELETE /x/communities/{id}/join247```248249### "I need monitoring & alerts"250251```252Need real-time monitoring?253├─ Monitor an account → POST /monitors254├─ Poll for events → GET /events255├─ Receive events via webhook → POST /webhooks256├─ Receive events via Telegram → POST /integrations257└─ Automate workflows → POST /automations258```259260### "I need AI composition"261262```263Need help writing tweets?264├─ Compose algorithm-optimized tweet → POST /compose (step=compose)265├─ Refine with goal + tone → POST /compose (step=refine)266├─ Score against algorithm → POST /compose (step=score)267├─ Analyze tweet style → POST /styles268├─ Compare two styles → GET /styles/compare269├─ Track engagement metrics → GET /styles/{username}/performance270└─ Save draft → POST /drafts271```272273## Authentication274275Every 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.276277```javascript278const API_KEY = "xq_YOUR_KEY_HERE";279const BASE = "https://xquik.com/api/v1";280const headers = { "x-api-key": API_KEY, "Content-Type": "application/json" };281```282283For Python examples, see [references/python-examples.md](references/python-examples.md).284285## Choosing the Right Endpoint286287| Goal | Endpoint | Cost |288|------|----------|------|289| **Get a single tweet** by ID/URL | `GET /x/tweets/{id}` | 1 credit |290| **Get an X Article** by tweet ID | `GET /x/articles/{id}` | 7 credits |291| **Search tweets** by keyword/hashtag | `GET /x/tweets/search?q=...` | 1 credit/tweet |292| **Get a user profile** | `GET /x/users/{username}` | 2 credits |293| **Get user's recent tweets** | `GET /x/users/{id}/tweets` | 1 credit/tweet |294| **Get user's liked tweets** | `GET /x/users/{id}/likes` | 1 credit/result |295| **Get user's media tweets** | `GET /x/users/{id}/media` | 1 credit/result |296| **Get tweet favoriters** | `GET /x/tweets/{id}/favoriters` | 2 credits/result |297| **Get mutual followers** | `GET /x/users/{id}/followers-you-know` | 2 credits/result |298| **Check follow relationship** | `GET /x/followers/check?source=A&target=B` | 7 credits |299| **Get trending topics** | `GET /trends?woeid=1` | 3 credits |300| **Get radar (trending news)** | `GET /radar?source=hacker_news` | Free |301| **Get bookmarks** | `GET /x/bookmarks` | 1 credit/result |302| **Get bookmark folders** | `GET /x/bookmarks/folders` | 1 credit |303| **Get notifications** | `GET /x/notifications` | 1 credit/result |304| **Get home timeline** | `GET /x/timeline` | 1 credit/result |305| **Get DM history** | `GET /x/dm/{userId}/history` | 1 credit/result |306| **Monitor an X account** | `POST /monitors` | Free |307| **Update monitor event types** | `PATCH /monitors/{id}` | Free |308| **Poll for events** | `GET /events` | Free |309| **Receive events in real time** | `POST /webhooks` | Free |310| **Update webhook** | `PATCH /webhooks/{id}` | Free |311| **Run a giveaway draw** | `POST /draws` | 1 credit/entry |312| **Download tweet media** | `POST /x/media/download` | 1 credit/item |313| **Extract bulk data** | `POST /extractions` | 1-7 credits/result |314| **Check credits** | `GET /credits` | Free |315| **Top up credits** | `POST /credits/topup` | Free |316| **Check account/usage** | `GET /account` | Free |317| **Link your X identity** | `PUT /account/x-identity` | Free |318| **Analyze tweet style** | `POST /styles` | Metered |319| **Save custom style** | `PUT /styles/{username}` | Free |320| **Get cached style** | `GET /styles/{username}` | Free |321| **Compare styles** | `GET /styles/compare?username1=A&username2=B` | Free |322| **Get tweet performance** | `GET /styles/{username}/performance` | Metered |323| **Save a tweet draft** | `POST /drafts` | Free |324| **List/manage drafts** | `GET /drafts`, `DELETE /drafts/{id}` | Free |325| **Compose a tweet** | `POST /compose` | Free |326| **Connect an X account** | `POST /x/accounts` | Free |327| **List connected accounts** | `GET /x/accounts` | Free |328| **Re-authenticate account** | `POST /x/accounts/{id}/reauth` | Free |329| **Post a tweet** | `POST /x/tweets` | 2 credits |330| **Delete a tweet** | `DELETE /x/tweets/{id}` | 2 credits |331| **Like / Unlike a tweet** | `POST` / `DELETE /x/tweets/{id}/like` | 2 credits |332| **Retweet** | `POST /x/tweets/{id}/retweet` | 2 credits |333| **Follow / Unfollow a user** | `POST` / `DELETE /x/users/{id}/follow` | 2 credits |334| **Send a DM** | `POST /x/dm/{userId}` | 2 credits |335| **Update profile** | `PATCH /x/profile` | 2 credits |336| **Update avatar** | `PATCH /x/profile/avatar` | 2 credits |337| **Update banner** | `PATCH /x/profile/banner` | 2 credits |338| **Upload media** | `POST /x/media` | 2 credits |339| **Community actions** | `POST /x/communities`, `POST /x/communities/{id}/join` | 2 credits |340| **Create Telegram integration** | `POST /integrations` | Free |341| **Manage integrations** | `GET /integrations`, `PATCH /integrations/{id}` | Free |342| **Create automation flow** | `POST /automations` | Free |343| **Manage automation flows** | `GET /automations`, `PATCH /automations/{slug}` | Free |344| **Add automation steps** | `POST /automations/{slug}/steps` | Free |345| **Trigger flow via webhook** | `POST /webhooks/inbound/{token}` | Free |346| **Open support ticket** | `POST /support/tickets` | Free |347| **Manage support tickets** | `GET /support/tickets`, `POST /support/tickets/{id}/messages` | Free |348349## Error Handling & Retry350351All errors return `{ "error": "error_code" }`. Key error codes:352353| Status | Code | Action |354|--------|------|--------|355| 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` | Fix the request, do not retry |356| 401 | `unauthenticated` | Check API key |357| 402 | `no_subscription`, `subscription_inactive`, `usage_limit_reached`, `no_addon`, `extra_usage_disabled`, `extra_usage_requires_v2`, `frozen`, `overage_limit_reached`, `insufficient_credits` | Subscribe, top up credits, enable extra usage, or wait for quota reset |358| 403 | `monitor_limit_reached`, `api_key_limit_reached`, `flow_limit_reached`, `step_limit_reached` | Delete a monitor/key/flow or add capacity |359| 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 |360| 403 | `account_needs_reauth` | Connected X account needs re-authentication |361| 409 | `monitor_already_exists`, `account_already_connected`, `conflict` | Resource already exists or concurrent edit conflict |362| 422 | `login_failed` | X credential verification failed. Check credentials |363| 429 | `x_api_rate_limited` | Rate limited. Retry with exponential backoff, respect `Retry-After` header |364| 500 | `internal_error` | Retry with backoff |365| 502 | `stream_registration_failed`, `x_api_unavailable`, `x_api_unauthorized`, `delivery_failed` | Retry with backoff |366367Retry only `429` and `5xx`. Never retry `4xx` (except 429). Max 3 retries with exponential backoff:368369```javascript370async function xquikFetch(path, options = {}) {371 const baseDelay = 1000;372373 for (let attempt = 0; attempt <= 3; attempt++) {374 const response = await fetch(`${BASE}${path}`, {375 ...options,376 headers: { ...headers, ...options.headers },377 });378379 if (response.ok) return response.json();380381 const retryable = response.status === 429 || response.status >= 500;382 if (!retryable || attempt === 3) {383 const error = await response.json();384 throw new Error(`Xquik API ${response.status}: ${error.error}`);385 }386387 const retryAfter = response.headers.get("Retry-After");388 const delay = retryAfter389 ? parseInt(retryAfter, 10) * 1000390 : baseDelay * Math.pow(2, attempt) + Math.random() * 1000;391392 await new Promise((resolve) => setTimeout(resolve, delay));393 }394}395```396397## Cursor Pagination398399Events, 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.400401```javascript402async function fetchAllPages(path, dataKey) {403 const results = [];404 let cursor;405406 while (true) {407 const params = new URLSearchParams({ limit: "100" });408 if (cursor) params.set("after", cursor);409410 const data = await xquikFetch(`${path}?${params}`);411 results.push(...data[dataKey]);412413 if (!data.hasMore) break;414 cursor = data.nextCursor;415 }416417 return results;418}419```420421Cursors are opaque strings. Never decode or construct them manually.422423## Extraction Tools (23 Types)424425Extractions run bulk data collection jobs. The complete workflow: estimate cost, create job, retrieve results, optionally export.426427### Tool Types and Required Parameters428429| Tool Type | Required Field | Description | Cost |430|-----------|---------------|-------------|------|431| `reply_extractor` | `targetTweetId` | Users who replied to a tweet | 1 credit/result |432| `repost_extractor` | `targetTweetId` | Users who retweeted a tweet | 2 credits/result |433| `quote_extractor` | `targetTweetId` | Users who quote-tweeted a tweet | 1 credit/result |434| `thread_extractor` | `targetTweetId` | All tweets in a thread | 1 credit/result |435| `article_extractor` | `targetTweetId` | Article content linked in a tweet | 7 credits/result |436| `favoriters` | `targetTweetId` | Users who favorited a tweet | 2 credits/result |437| `follower_explorer` | `targetUsername` | Followers of an account | 2 credits/result |438| `following_explorer` | `targetUsername` | Accounts followed by a user | 2 credits/result |439| `verified_follower_explorer` | `targetUsername` | Verified followers of an account | 2 credits/result |440| `mention_extractor` | `targetUsername` | Tweets mentioning an account | 1 credit/result |441| `post_extractor` | `targetUsername` | Posts from an account | 1 credit/result |442| `user_likes` | `targetUserId` | Tweets liked by a user | 1 credit/result |443| `user_media` | `targetUserId` | Media tweets from a user | 1 credit/result |444| `community_extractor` | `targetCommunityId` | Members of a community | 2 credits/result |445| `community_moderator_explorer` | `targetCommunityId` | Moderators of a community | 2 credits/result |446| `community_post_extractor` | `targetCommunityId` | Posts from a community | 1 credit/result |447| `community_search` | `targetCommunityId` + `searchQuery` | Search posts within a community | 1 credit/result |448| `list_member_extractor` | `targetListId` | Members of a list | 2 credits/result |449| `list_post_extractor` | `targetListId` | Posts from a list | 1 credit/result |450| `list_follower_explorer` | `targetListId` | Followers of a list | 2 credits/result |451| `space_explorer` | `targetSpaceId` | Participants of a Space | 2 credits/result |452| `people_search` | `searchQuery` | Search for users by keyword | 2 credits/result |453| `tweet_search_extractor` | `searchQuery` | Search and extract tweets by keyword or hashtag (bulk, up to 1,000) | 1 credit/result |454455### Complete Extraction Workflow456457```javascript458// Step 1: Estimate cost before running (pass resultsLimit if you only need a sample)459const estimate = await xquikFetch("/extractions/estimate", {460 method: "POST",461 body: JSON.stringify({462 toolType: "follower_explorer",463 targetUsername: "elonmusk",464 resultsLimit: 1000, // optional: limit to 1,000 results instead of all465 }),466});467// Response: { allowed: true, estimatedResults: 195000000, usagePercent: 12, projectedPercent: 98 }468469if (!estimate.allowed) {470 console.log("Extraction would exceed monthly quota");471 return;472}473474// Step 2: Create extraction job (pass same resultsLimit to match estimate)475const job = await xquikFetch("/extractions", {476 method: "POST",477 body: JSON.stringify({478 toolType: "follower_explorer",479 targetUsername: "elonmusk",480 resultsLimit: 1000,481 }),482});483// Response: { id: "77777", toolType: "follower_explorer", status: "completed", totalResults: 195000 }484485// Step 3: Poll until complete (large jobs may return status "running")486while (job.status === "pending" || job.status === "running") {487 await new Promise((r) => setTimeout(r, 2000));488 job = await xquikFetch(`/extractions/${job.id}`);489}490491// Step 4: Retrieve paginated results (up to 1,000 per page)492let cursor;493const allResults = [];494495while (true) {496 const path = `/extractions/${job.id}${cursor ? `?after=${cursor}` : ""}`;497 const page = await xquikFetch(path);498 allResults.push(...page.results);499 // Each result: { xUserId, xUsername, xDisplayName, xFollowersCount, xVerified, xProfileImageUrl }500501 if (!page.hasMore) break;502 cursor = page.nextCursor;503}504505// Step 5: Export as CSV/XLSX/Markdown (50,000 row limit)506const exportUrl = `${BASE}/extractions/${job.id}/export?format=csv`;507const csvResponse = await fetch(exportUrl, { headers });508const csvData = await csvResponse.text();509```510511## Giveaway Draws512513Run transparent, auditable giveaway draws from tweet replies with configurable filters.514515### Create Draw Request516517`POST /draws` with a `tweetUrl` (required) and optional filters:518519| Field | Type | Description |520|-------|------|-------------|521| `tweetUrl` | string | **Required.** Full tweet URL: `https://x.com/user/status/ID` |522| `winnerCount` | number | Winners to select (default 1) |523| `backupCount` | number | Backup winners to select |524| `uniqueAuthorsOnly` | boolean | Count only one entry per author |525| `mustRetweet` | boolean | Require participants to have retweeted |526| `mustFollowUsername` | string | Username participants must follow |527| `filterMinFollowers` | number | Minimum follower count |528| `filterAccountAgeDays` | number | Minimum account age in days |529| `filterLanguage` | string | Language code (e.g., `"en"`) |530| `requiredKeywords` | string[] | Words that must appear in the reply |531| `requiredHashtags` | string[] | Hashtags that must appear (e.g., `["#giveaway"]`) |532| `requiredMentions` | string[] | Usernames that must be mentioned (e.g., `["@xquik"]`) |533534### Complete Draw Workflow535536```javascript537// Step 1: Create draw with filters538const draw = await xquikFetch("/draws", {539 method: "POST",540 body: JSON.stringify({541 tweetUrl: "https://x.com/burakbayir/status/1893456789012345678",542 winnerCount: 3,543 backupCount: 2,544 uniqueAuthorsOnly: true,545 mustRetweet: true,546 mustFollowUsername: "burakbayir",547 filterMinFollowers: 50,548 filterAccountAgeDays: 30,549 filterLanguage: "en",550 requiredHashtags: ["#giveaway"],551 }),552});553554// Step 2: Get draw details with winners555const details = await xquikFetch(`/draws/${draw.id}`);556// details.winners: [557// { position: 1, authorUsername: "winner1", tweetId: "...", isBackup: false },558// ...559// ]560561// Step 3: Export results562const exportUrl = `${BASE}/draws/${draw.id}/export?format=csv`;563```564565## Webhook Event Handling566567Webhooks 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`.568569### Webhook Handler (Express)570571```javascript572import express from "express";573import { createHmac, timingSafeEqual, createHash } from "node:crypto";574575const WEBHOOK_SECRET = process.env.XQUIK_WEBHOOK_SECRET;576const processedHashes = new Set(); // Use Redis/DB in production577578function verifySignature(payload, signature, secret) {579 const expected = "sha256=" + createHmac("sha256", secret).update(payload).digest("hex");580 return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));581}582583const app = express();584585app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {586 const signature = req.headers["x-xquik-signature"];587 const payload = req.body.toString();588589 // 1. Verify HMAC signature (constant-time comparison)590 if (!signature || !verifySignature(payload, signature, WEBHOOK_SECRET)) {591 return res.status(401).send("Invalid signature");592 }593594 // 2. Deduplicate (retries can deliver the same event twice)595 const payloadHash = createHash("sha256").update(payload).digest("hex");596 if (processedHashes.has(payloadHash)) {597 return res.status(200).send("Already processed");598 }599 processedHashes.add(payloadHash);600601 // 3. Parse and route by event type602 const event = JSON.parse(payload);603 // event.eventType: "tweet.new" | "tweet.reply" | "tweet.quote" | "tweet.retweet" | "follower.gained" | "follower.lost"604605 // 4. Respond within 10 seconds (process async if slow)606 res.status(200).send("OK");607});608609app.listen(3000);610```611612For Flask (Python) webhook handler, see [references/python-examples.md](references/python-examples.md#webhook-handler-flask).613614Webhook security rules:615- Always verify signature before processing (constant-time comparison)616- Compute HMAC over raw body bytes, not re-serialized JSON617- Respond `200` within 10 seconds; queue slow processing for async618- Deduplicate by payload hash (retries can deliver same event twice)619- Store webhook secret in environment variables, never hardcode620- Retry policy: 5 attempts with exponential backoff on failure621622Check delivery status via `GET /webhooks/{id}/deliveries` to monitor successful and failed attempts.623624## Real-Time Monitoring Setup625626Complete end-to-end: create monitor, register webhook, handle events.627628```javascript629// 1. Create monitor (free)630const monitor = await xquikFetch("/monitors", {631 method: "POST",632 body: JSON.stringify({633 username: "elonmusk",634 eventTypes: ["tweet.new", "tweet.reply", "tweet.quote", "follower.gained"],635 }),636});637638// 2. Register webhook (free)639const webhook = await xquikFetch("/webhooks", {640 method: "POST",641 body: JSON.stringify({642 url: "https://your-server.com/webhook",643 eventTypes: ["tweet.new", "tweet.reply"],644 }),645});646// IMPORTANT: Save webhook.secret. It is shown only once!647648// 3. Poll events (alternative to webhooks, free)649const events = await xquikFetch("/events?monitorId=7&limit=50");650```651652Event types: `tweet.new`, `tweet.quote`, `tweet.reply`, `tweet.retweet`, `follower.gained`, `follower.lost`.653654## MCP Server (AI Agents)655656The MCP server at `https://xquik.com/mcp` provides 2 tools. StreamableHTTP transport. API key auth (`x-api-key` header) for CLI/IDE clients; OAuth 2.1 for web clients (Claude.ai, ChatGPT Developer Mode).657658### Tools659660| Tool | Description | Cost |661|------|-------------|------|662| `explore` | Search the API endpoint catalog (read-only, no network calls) | Free |663| `xquik` | Execute API calls against your account (120 endpoints, 12 categories) | Varies |664665Supported platforms: Claude.ai, Claude Desktop, Claude Code, ChatGPT (Custom GPT, Agents SDK, Developer Mode), Codex CLI, Cursor, VS Code, Windsurf, OpenCode.666667For setup configs per platform, read [references/mcp-setup.md](references/mcp-setup.md). For tool details with selection rules, common mistakes, and unsupported operations, read [references/mcp-tools.md](references/mcp-tools.md).668669### MCP vs REST API670671| | MCP Server | REST API |672|---|------------|----------|673| **Best for** | AI agents, IDE integrations | Custom apps, scripts, backend services |674| **Model** | 2 tools (explore + xquik) | 120 individual endpoints |675| **Categories** | 12: account, automations, bot, composition, credits, extraction, integrations, media, monitoring, support, twitter, x-accounts, x-write | Same |676| **Coverage** | Full — `xquik` tool calls any REST endpoint | Direct HTTP calls |677| **File export** | Not available | CSV, XLSX, Markdown |678| **Unique to REST** | - | API key management, file export (CSV/XLSX/MD), account locale update |679680### Workflow Patterns681682Common multi-step sequences (all via `xquik` tool calling REST endpoints):683684- **Set up real-time alerts:** `POST /monitors` → `POST /webhooks` → `POST /webhooks/{id}/test`685- **Run a giveaway:** `GET /account` (check budget) → `POST /draws`686- **Bulk extraction:** `POST /extractions/estimate` → `POST /extractions` → `GET /extractions/{id}`687- **Full tweet analysis:** `GET /x/tweets/{id}` (metrics) → `POST /extractions` with `thread_extractor`688- **Find and analyze user:** `GET /x/users/{username}` → `GET /x/users/{id}/tweets` → `GET /x/tweets/{id}`689- **Compose algorithm-optimized tweet:** `POST /compose` (step=compose) → AI asks follow-ups → (step=refine) → AI drafts → (step=score) → iterate690- **Analyze tweet style:** `POST /styles` (fetch & cache) → `GET /styles/{username}` (reference) → `POST /compose` with `styleUsername`691- **Compare styles:** `POST /styles` for both accounts → `GET /styles/compare`692- **Track tweet performance:** `POST /styles` (cache tweets) → `GET /styles/{username}/performance` (live metrics)693- **Save & manage drafts:** `POST /compose` → `POST /drafts` → `GET /drafts` → `DELETE /drafts/{id}`694- **Download & share media:** `POST /x/media/download` (returns permanent hosted URLs)695- **Get trending news:** `GET /radar` (7 sources, free) → `POST /compose` with trending topic696- **Subscribe or manage billing:** `POST /subscribe` (returns Stripe URL)697- **Post a tweet:** `POST /x/accounts` (connect) → `POST /x/tweets` with `account` + `text` (optionally `POST /x/media` first)698- **Engage with tweets:** `POST /x/tweets/{id}/like`, `POST /x/tweets/{id}/retweet`, `POST /x/users/{id}/follow`699- **Set up Telegram alerts:** `POST /integrations` (type=telegram, chatId, eventTypes) → `POST /integrations/{id}/test`700- **Create automation flow:** `POST /automations` (name, triggerType, triggerConfig) → `POST /automations/{slug}/steps` (add actions) → `PATCH /automations/{slug}` (activate)701- **Check & top up credits:** `GET /credits` → `POST /credits/topup`702- **Open support ticket:** `POST /support/tickets` (subject, body) → `GET /support/tickets/{id}` (check status) → `POST /support/tickets/{id}/messages` (reply)703704## Conventions705706- **IDs are strings.** Bigint values; treat as opaque strings, never parse as numbers707- **Timestamps are ISO 8601 UTC.** Example: `2026-02-24T10:30:00.000Z`708- **Errors return JSON.** Format: `{ "error": "error_code" }`709- **Cursors are opaque.** Pass `nextCursor` as the `after` query parameter, never decode710- Export formats: `csv`, `xlsx`, `md` via `GET /extractions/{id}/export?format=csv` or `GET /draws/{id}/export?format=csv&type=winners`711712## Reference Files713714For additional detail beyond this guide:715716- **`references/mcp-tools.md`**: MCP tool selection rules, workflow patterns, common mistakes, and unsupported operations717- **`references/api-endpoints.md`**: All REST API endpoints with methods, paths, parameters, and response shapes718- **`references/python-examples.md`**: Python equivalents of all JavaScript examples (retry, extraction, draw, webhook)719- **`references/webhooks.md`**: Extended webhook examples, local testing with ngrok, delivery status monitoring720- **`references/mcp-setup.md`**: MCP server configuration for 10 IDEs and AI agent platforms721- **`references/extractions.md`**: Extraction tool details, export columns722- **`references/types.md`**: TypeScript type definitions for all REST API and MCP output objects723
Full transparency — inspect the skill content before installing.