diff --git a/skills/.curated/x-twitter-scraper/LICENSE.txt b/skills/.curated/x-twitter-scraper/LICENSE.txt new file mode 100644 index 00000000..3a5f93c8 --- /dev/null +++ b/skills/.curated/x-twitter-scraper/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Xquik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/skills/.curated/x-twitter-scraper/SKILL.md b/skills/.curated/x-twitter-scraper/SKILL.md new file mode 100644 index 00000000..f149788b --- /dev/null +++ b/skills/.curated/x-twitter-scraper/SKILL.md @@ -0,0 +1,710 @@ +--- +name: x-twitter-scraper +description: "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." +--- + +# Xquik API Integration + +Xquik 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. + +**Reads start at $0.00015/call — 33x cheaper than the official X API.** + +Your 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. + +## Retrieval Sources + +| Source | How to retrieve | Use for | +|--------|----------------|---------| +| Xquik docs | [docs.xquik.com](https://docs.xquik.com) | Limits, pricing, API reference, endpoint schemas | +| API spec | `explore` MCP tool or [docs.xquik.com/api-reference/overview](https://docs.xquik.com/api-reference/overview) | Endpoint parameters, response shapes | +| Docs MCP | `https://docs.xquik.com/mcp` (no auth) | Search docs from AI tools | +| Billing guide | [docs.xquik.com/guides/billing](https://docs.xquik.com/guides/billing) | Credit costs, subscription tiers, MPP pricing | + +When this skill and the docs disagree, **trust the docs**. + +## Quick Reference + +| | | +|---|---| +| **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** | Read: 120/60s, Write: 30/60s, Delete: 15/60s (fixed window per method tier) | +| **Endpoints** | 120 across 12 categories | +| **MCP tools** | 2 (explore + xquik) | +| **Extraction tools** | 23 types | +| **Pricing** | $20/month base (reads from $0.00015). Pay-per-use available via MPP | +| **Docs** | [docs.xquik.com](https://docs.xquik.com) | +| **HTTPS only** | Plain HTTP gets `301` redirect | + +## Pricing + +Xquik is the most affordable X data API available. All metered operations deduct credits from a single shared pool. + +### Subscription + +| | | +|---|---| +| **Base plan** | $20/month | +| **Included monitors** | 1 | +| **Additional monitors** | $5/month each | +| **Credit value** | 1 credit = $0.00015 | + +### Per-Operation Costs + +#### Read operations — 1 credit ($0.00015) + +| Operation | Unit | +|-----------|------| +| Get tweet | per call | +| Search tweets | per tweet returned | +| User tweets | per tweet returned | +| User likes | per result | +| User media | per result | +| Bookmarks | per result | +| Bookmark folders | per call | +| Notifications | per result | +| Timeline | per result | +| DM history | per result | +| Download media | per media item | + +#### Read operations — 2 credits ($0.0003) + +| Operation | Unit | +|-----------|------| +| Get user | per call | +| Tweet favoriters | per result | +| Followers you know | per result | +| Verified followers | per result | + +#### Read operations — 3 credits ($0.00045) + +| Operation | Unit | +|-----------|------| +| Trends | per call | + +#### Read operations — 7 credits ($0.00105) + +| Operation | Unit | +|-----------|------| +| Follow check | per call | +| Get article | per call | + +#### Write operations — 2 credits ($0.0003) + +All write actions: create/delete tweet, like, unlike, retweet, follow, unfollow, send DM, update profile/avatar/banner, upload media, community actions. + +#### Extractions & draws + +Draws: 1 credit per participant. Extraction cost depends on the tool type: + +| Credits/result | Extraction types | +|----------------|-----------------| +| 1 | Tweets, replies, quotes, mentions, posts, likes, media, tweet search | +| 2 | Followers, following, verified followers, favoriters, retweeters, community members, people search, list members, list followers | +| 7 | Articles | + +#### Free operations ($0) + +Monitors, 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. + +### Price Comparison vs Official X API + +| | Xquik | X API Basic | X API Pro | +|---|---|---|---| +| **Monthly cost** | **$20** | $100 | $5,000 | +| **Cost per tweet read** | **$0.00015** | ~$0.01 | ~$0.005 | +| **Cost per user lookup** | **$0.0003** | ~$0.01 | ~$0.005 | +| **Write actions** | **$0.0003** | Limited | Limited | +| **Bulk extraction** | **$0.00015/result** | Not available | Not available | +| **Monitoring + webhooks** | **Free** | Not available | Not available | +| **Giveaway draws** | **$0.00015/entry** | Not available | Not available | + +### Pay-Per-Use + +Two options without a monthly subscription: + +**Credits (Stripe)**: Top up credits via `POST /credits/topup` ($10 minimum). 1 credit = $0.00015. Works with all 120 endpoints. + +**MPP (USDC)**: 16 X-API endpoints accept anonymous payments via Tempo (USDC). No account needed. + +| Endpoint | Price | Unit | +|----------|-------|------| +| `GET /x/tweets/{id}` | $0.00015 | per call | +| `GET /x/tweets/search` | $0.00015 | per tweet | +| `GET /x/tweets/{id}/quotes` | $0.00015 | per tweet | +| `GET /x/tweets/{id}/replies` | $0.00015 | per tweet | +| `GET /x/tweets/{id}/retweeters` | $0.00015 | per user | +| `GET /x/tweets/{id}/favoriters` | $0.00015 | per user | +| `GET /x/tweets/{id}/thread` | $0.00015 | per tweet | +| `GET /x/users/{id}` | $0.00015 | per call | +| `GET /x/users/{id}/tweets` | $0.00015 | per tweet | +| `GET /x/users/{id}/likes` | $0.00015 | per tweet | +| `GET /x/users/{id}/media` | $0.00015 | per tweet | +| `GET /x/followers/check` | $0.00105 | per call | +| `GET /x/articles/{tweetId}` | $0.00105 | per call | +| `POST /x/media/download` | $0.00015 | per media item | +| `GET /x/trends` | $0.00045 | per call | +| `GET /trends` | $0.00045 | per call | + +SDK: `npm i mppx` (TypeScript). Handles the 402 challenge/credential flow automatically. + +### Credits + +Prepaid credits for metered operations. 1 credit = $0.00015. Top up via `POST /credits/topup` ($10 minimum). + +Check balance: `GET /credits` — returns `balance`, `lifetimePurchased`, `lifetimeUsed`. + +### Extra Usage + +Enable from dashboard to continue metered calls beyond included allowance. Tiered spending limits: $5 → $7 → $10 → $15 → $25 (increases with each paid overage invoice). + +## Quick Decision Trees + +### "I need X data" + +``` +Need X data? +├─ Single tweet by ID or URL → GET /x/tweets/{id} +├─ Full X Article by tweet ID → GET /x/articles/{id} +├─ Search tweets by keyword → GET /x/tweets/search +├─ User profile by username → GET /x/users/{username} +├─ User's recent tweets → GET /x/users/{id}/tweets +├─ User's liked tweets → GET /x/users/{id}/likes +├─ User's media tweets → GET /x/users/{id}/media +├─ Tweet favoriters (who liked) → GET /x/tweets/{id}/favoriters +├─ Mutual followers → GET /x/users/{id}/followers-you-know +├─ Check follow relationship → GET /x/followers/check +├─ Download media (images/video) → POST /x/media/download +├─ Trending topics (X) → GET /trends +├─ Trending news (7 sources, free) → GET /radar +├─ Bookmarks → GET /x/bookmarks +├─ Notifications → GET /x/notifications +├─ Home timeline → GET /x/timeline +└─ DM conversation history → GET /x/dm/{userId}/history +``` + +### "I need bulk extraction" + +``` +Need bulk data? +├─ Replies to a tweet → reply_extractor +├─ Retweets of a tweet → repost_extractor +├─ Quotes of a tweet → quote_extractor +├─ Favoriters of a tweet → favoriters +├─ Full thread → thread_extractor +├─ Article content → article_extractor +├─ User's liked tweets (bulk) → user_likes +├─ User's media tweets (bulk) → user_media +├─ Account followers → follower_explorer +├─ Account following → following_explorer +├─ Verified followers → verified_follower_explorer +├─ Mentions of account → mention_extractor +├─ Posts from account → post_extractor +├─ Community members → community_extractor +├─ Community moderators → community_moderator_explorer +├─ Community posts → community_post_extractor +├─ Community search → community_search +├─ List members → list_member_extractor +├─ List posts → list_post_extractor +├─ List followers → list_follower_explorer +├─ Space participants → space_explorer +├─ People search → people_search +└─ Tweet search (bulk, up to 1K) → tweet_search_extractor +``` + +### "I need to write/post" + +``` +Need write actions? +├─ Post a tweet → POST /x/tweets +├─ Delete a tweet → DELETE /x/tweets/{id} +├─ Like a tweet → POST /x/tweets/{id}/like +├─ Unlike a tweet → DELETE /x/tweets/{id}/like +├─ Retweet → POST /x/tweets/{id}/retweet +├─ Follow a user → POST /x/users/{id}/follow +├─ Unfollow a user → DELETE /x/users/{id}/follow +├─ Send a DM → POST /x/dm/{userId} +├─ Update profile → PATCH /x/profile +├─ Update avatar → PATCH /x/profile/avatar +├─ Update banner → PATCH /x/profile/banner +├─ Upload media → POST /x/media +├─ Create community → POST /x/communities +├─ Join community → POST /x/communities/{id}/join +└─ Leave community → DELETE /x/communities/{id}/join +``` + +### "I need monitoring & alerts" + +``` +Need real-time monitoring? +├─ Monitor an account → POST /monitors +├─ Poll for events → GET /events +├─ Receive events via webhook → POST /webhooks +├─ Receive events via Telegram → POST /integrations +└─ Automate workflows → POST /automations +``` + +### "I need AI composition" + +``` +Need help writing tweets? +├─ Compose algorithm-optimized tweet → POST /compose (step=compose) +├─ Refine with goal + tone → POST /compose (step=refine) +├─ Score against algorithm → POST /compose (step=score) +├─ Analyze tweet style → POST /styles +├─ Compare two styles → GET /styles/compare +├─ Track engagement metrics → GET /styles/{username}/performance +└─ Save draft → POST /drafts +``` + +## Authentication + +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. + +```javascript +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](references/python-examples.md). + +## Choosing the Right Endpoint + +| Goal | Endpoint | Cost | +|------|----------|------| +| **Get a single tweet** by ID/URL | `GET /x/tweets/{id}` | 1 credit | +| **Get an X Article** by tweet ID | `GET /x/articles/{id}` | 7 credits | +| **Search tweets** by keyword/hashtag | `GET /x/tweets/search?q=...` | 1 credit/tweet | +| **Get a user profile** | `GET /x/users/{username}` | 2 credits | +| **Get user's recent tweets** | `GET /x/users/{id}/tweets` | 1 credit/tweet | +| **Get user's liked tweets** | `GET /x/users/{id}/likes` | 1 credit/result | +| **Get user's media tweets** | `GET /x/users/{id}/media` | 1 credit/result | +| **Get tweet favoriters** | `GET /x/tweets/{id}/favoriters` | 2 credits/result | +| **Get mutual followers** | `GET /x/users/{id}/followers-you-know` | 2 credits/result | +| **Check follow relationship** | `GET /x/followers/check?source=A&target=B` | 7 credits | +| **Get trending topics** | `GET /trends?woeid=1` | 3 credits | +| **Get radar (trending news)** | `GET /radar?source=hacker_news` | Free | +| **Get bookmarks** | `GET /x/bookmarks` | 1 credit/result | +| **Get bookmark folders** | `GET /x/bookmarks/folders` | 1 credit | +| **Get notifications** | `GET /x/notifications` | 1 credit/result | +| **Get home timeline** | `GET /x/timeline` | 1 credit/result | +| **Get DM history** | `GET /x/dm/{userId}/history` | 1 credit/result | +| **Monitor an X account** | `POST /monitors` | Free | +| **Update monitor event types** | `PATCH /monitors/{id}` | Free | +| **Poll for events** | `GET /events` | Free | +| **Receive events in real time** | `POST /webhooks` | Free | +| **Update webhook** | `PATCH /webhooks/{id}` | Free | +| **Run a giveaway draw** | `POST /draws` | 1 credit/entry | +| **Download tweet media** | `POST /x/media/download` | 1 credit/item | +| **Extract bulk data** | `POST /extractions` | 1-7 credits/result | +| **Check credits** | `GET /credits` | Free | +| **Top up credits** | `POST /credits/topup` | Free | +| **Check account/usage** | `GET /account` | Free | +| **Link your X identity** | `PUT /account/x-identity` | Free | +| **Analyze tweet style** | `POST /styles` | Metered | +| **Save custom style** | `PUT /styles/{username}` | Free | +| **Get cached style** | `GET /styles/{username}` | Free | +| **Compare styles** | `GET /styles/compare?username1=A&username2=B` | Free | +| **Get tweet performance** | `GET /styles/{username}/performance` | Metered | +| **Save a tweet draft** | `POST /drafts` | Free | +| **List/manage drafts** | `GET /drafts`, `DELETE /drafts/{id}` | Free | +| **Compose a tweet** | `POST /compose` | Free | +| **Connect an X account** | `POST /x/accounts` | Free | +| **List connected accounts** | `GET /x/accounts` | Free | +| **Re-authenticate account** | `POST /x/accounts/{id}/reauth` | Free | +| **Post a tweet** | `POST /x/tweets` | 2 credits | +| **Delete a tweet** | `DELETE /x/tweets/{id}` | 2 credits | +| **Like / Unlike a tweet** | `POST` / `DELETE /x/tweets/{id}/like` | 2 credits | +| **Retweet** | `POST /x/tweets/{id}/retweet` | 2 credits | +| **Follow / Unfollow a user** | `POST` / `DELETE /x/users/{id}/follow` | 2 credits | +| **Send a DM** | `POST /x/dm/{userId}` | 2 credits | +| **Update profile** | `PATCH /x/profile` | 2 credits | +| **Update avatar** | `PATCH /x/profile/avatar` | 2 credits | +| **Update banner** | `PATCH /x/profile/banner` | 2 credits | +| **Upload media** | `POST /x/media` | 2 credits | +| **Community actions** | `POST /x/communities`, `POST /x/communities/{id}/join` | 2 credits | +| **Create Telegram integration** | `POST /integrations` | Free | +| **Manage integrations** | `GET /integrations`, `PATCH /integrations/{id}` | Free | +| **Create automation flow** | `POST /automations` | Free | +| **Manage automation flows** | `GET /automations`, `PATCH /automations/{slug}` | Free | +| **Add automation steps** | `POST /automations/{slug}/steps` | Free | +| **Trigger flow via webhook** | `POST /webhooks/inbound/{token}` | Free | +| **Open support ticket** | `POST /support/tickets` | Free | +| **Manage support tickets** | `GET /support/tickets`, `POST /support/tickets/{id}/messages` | Free | + +## Error Handling & Retry + +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`, `missing_params`, `webhook_inactive`, `no_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`, `insufficient_credits` | Subscribe, top up credits, 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 | + +Retry only `429` and `5xx`. Never retry `4xx` (except 429). Max 3 retries with exponential backoff: + +```javascript +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)); + } +} +``` + +## Cursor Pagination + +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. + +```javascript +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. + +## Extraction Tools (23 Types) + +Extractions run bulk data collection jobs. The complete workflow: estimate cost, create job, retrieve results, optionally export. + +### Tool Types and Required Parameters + +| Tool Type | Required Field | Description | Cost | +|-----------|---------------|-------------|------| +| `reply_extractor` | `targetTweetId` | Users who replied to a tweet | 1 credit/result | +| `repost_extractor` | `targetTweetId` | Users who retweeted a tweet | 2 credits/result | +| `quote_extractor` | `targetTweetId` | Users who quote-tweeted a tweet | 1 credit/result | +| `thread_extractor` | `targetTweetId` | All tweets in a thread | 1 credit/result | +| `article_extractor` | `targetTweetId` | Article content linked in a tweet | 7 credits/result | +| `favoriters` | `targetTweetId` | Users who favorited a tweet | 2 credits/result | +| `follower_explorer` | `targetUsername` | Followers of an account | 2 credits/result | +| `following_explorer` | `targetUsername` | Accounts followed by a user | 2 credits/result | +| `verified_follower_explorer` | `targetUsername` | Verified followers of an account | 2 credits/result | +| `mention_extractor` | `targetUsername` | Tweets mentioning an account | 1 credit/result | +| `post_extractor` | `targetUsername` | Posts from an account | 1 credit/result | +| `user_likes` | `targetUserId` | Tweets liked by a user | 1 credit/result | +| `user_media` | `targetUserId` | Media tweets from a user | 1 credit/result | +| `community_extractor` | `targetCommunityId` | Members of a community | 2 credits/result | +| `community_moderator_explorer` | `targetCommunityId` | Moderators of a community | 2 credits/result | +| `community_post_extractor` | `targetCommunityId` | Posts from a community | 1 credit/result | +| `community_search` | `targetCommunityId` + `searchQuery` | Search posts within a community | 1 credit/result | +| `list_member_extractor` | `targetListId` | Members of a list | 2 credits/result | +| `list_post_extractor` | `targetListId` | Posts from a list | 1 credit/result | +| `list_follower_explorer` | `targetListId` | Followers of a list | 2 credits/result | +| `space_explorer` | `targetSpaceId` | Participants of a Space | 2 credits/result | +| `people_search` | `searchQuery` | Search for users by keyword | 2 credits/result | +| `tweet_search_extractor` | `searchQuery` | Search and extract tweets by keyword or hashtag (bulk, up to 1,000) | 1 credit/result | + +### Complete Extraction Workflow + +```javascript +// 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(); +``` + +## Giveaway Draws + +Run transparent, auditable giveaway draws from tweet replies with configurable filters. + +### Create Draw Request + +`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 | +| `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"]`) | + +### Complete Draw Workflow + +```javascript +// 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"], + }), +}); + +// Step 2: Get draw details with winners +const details = await xquikFetch(`/draws/${draw.id}`); +// details.winners: [ +// { position: 1, authorUsername: "winner1", tweetId: "...", isBackup: false }, +// ... +// ] + +// Step 3: Export results +const exportUrl = `${BASE}/draws/${draw.id}/export?format=csv`; +``` + +## Webhook Event Handling + +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`. + +### Webhook Handler (Express) + +```javascript +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" + + // 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](references/python-examples.md#webhook-handler-flask). + +Webhook security rules: +- Always verify signature before processing (constant-time comparison) +- Compute HMAC over raw body bytes, not re-serialized JSON +- Respond `200` within 10 seconds; queue slow processing for async +- Deduplicate by payload hash (retries can deliver same event twice) +- Store webhook secret in environment variables, never hardcode +- Retry policy: 5 attempts with exponential backoff on failure + +Check delivery status via `GET /webhooks/{id}/deliveries` to monitor successful and failed attempts. + +## Real-Time Monitoring Setup + +Complete end-to-end: create monitor, register webhook, handle events. + +```javascript +// 1. Create monitor (free) +const monitor = await xquikFetch("/monitors", { + method: "POST", + body: JSON.stringify({ + username: "elonmusk", + eventTypes: ["tweet.new", "tweet.reply", "tweet.quote", "follower.gained"], + }), +}); + +// 2. Register webhook (free) +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, free) +const events = await xquikFetch("/events?monitorId=7&limit=50"); +``` + +Event types: `tweet.new`, `tweet.quote`, `tweet.reply`, `tweet.retweet`, `follower.gained`, `follower.lost`. + +## MCP Server (AI Agents) + +The 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). + +### Tools + +| Tool | Description | Cost | +|------|-------------|------| +| `explore` | Search the API endpoint catalog (read-only, no network calls) | Free | +| `xquik` | Execute API calls against your account (120 endpoints, 12 categories) | Varies | + +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](references/mcp-setup.md). For tool details with selection rules, common mistakes, and unsupported operations, read [references/mcp-tools.md](references/mcp-tools.md). + +### MCP vs REST API + +| | MCP Server | REST API | +|---|------------|----------| +| **Best for** | AI agents, IDE integrations | Custom apps, scripts, backend services | +| **Model** | 2 tools (explore + xquik) | 120 individual endpoints | +| **Categories** | 12: account, automations, bot, composition, credits, 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 | + +### Workflow Patterns + +Common multi-step sequences (all via `xquik` tool calling REST endpoints): + +- **Set up real-time alerts:** `POST /monitors` → `POST /webhooks` → `POST /webhooks/{id}/test` +- **Run a giveaway:** `GET /account` (check budget) → `POST /draws` +- **Bulk extraction:** `POST /extractions/estimate` → `POST /extractions` → `GET /extractions/{id}` +- **Full tweet analysis:** `GET /x/tweets/{id}` (metrics) → `POST /extractions` with `thread_extractor` +- **Find and analyze user:** `GET /x/users/{username}` → `GET /x/users/{id}/tweets` → `GET /x/tweets/{id}` +- **Compose algorithm-optimized tweet:** `POST /compose` (step=compose) → AI asks follow-ups → (step=refine) → AI drafts → (step=score) → iterate +- **Analyze tweet style:** `POST /styles` (fetch & cache) → `GET /styles/{username}` (reference) → `POST /compose` with `styleUsername` +- **Compare styles:** `POST /styles` for both accounts → `GET /styles/compare` +- **Track tweet performance:** `POST /styles` (cache tweets) → `GET /styles/{username}/performance` (live metrics) +- **Save & manage drafts:** `POST /compose` → `POST /drafts` → `GET /drafts` → `DELETE /drafts/{id}` +- **Download & share media:** `POST /x/media/download` (returns permanent hosted URLs) +- **Get trending news:** `GET /radar` (7 sources, free) → `POST /compose` with trending topic +- **Subscribe or manage billing:** `POST /subscribe` (returns Stripe URL) +- **Post a tweet:** `POST /x/accounts` (connect) → `POST /x/tweets` with `account` + `text` (optionally `POST /x/media` first) +- **Engage with tweets:** `POST /x/tweets/{id}/like`, `POST /x/tweets/{id}/retweet`, `POST /x/users/{id}/follow` +- **Set up Telegram alerts:** `POST /integrations` (type=telegram, chatId, eventTypes) → `POST /integrations/{id}/test` +- **Create automation flow:** `POST /automations` (name, triggerType, triggerConfig) → `POST /automations/{slug}/steps` (add actions) → `PATCH /automations/{slug}` (activate) +- **Check & top up credits:** `GET /credits` → `POST /credits/topup` +- **Open support ticket:** `POST /support/tickets` (subject, body) → `GET /support/tickets/{id}` (check status) → `POST /support/tickets/{id}/messages` (reply) + +## Conventions + +- **IDs are strings.** Bigint values; treat as opaque strings, never parse as numbers +- **Timestamps are ISO 8601 UTC.** Example: `2026-02-24T10:30:00.000Z` +- **Errors return JSON.** Format: `{ "error": "error_code" }` +- **Cursors are opaque.** Pass `nextCursor` as the `after` query parameter, never decode +- Export formats: `csv`, `xlsx`, `md` via `GET /extractions/{id}/export?format=csv` or `GET /draws/{id}/export?format=csv&type=winners` + +## Reference Files + +For additional detail beyond this guide: + +- **`references/mcp-tools.md`**: MCP tool selection rules, workflow patterns, common mistakes, and unsupported operations +- **`references/api-endpoints.md`**: All REST API endpoints with methods, paths, parameters, and response shapes +- **`references/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 monitoring +- **`references/mcp-setup.md`**: MCP server configuration for 10 IDEs and AI agent platforms +- **`references/extractions.md`**: Extraction tool details, export columns +- **`references/types.md`**: TypeScript type definitions for all REST API and MCP output objects diff --git a/skills/.curated/x-twitter-scraper/references/api-endpoints.md b/skills/.curated/x-twitter-scraper/references/api-endpoints.md new file mode 100644 index 00000000..ca90a39c --- /dev/null +++ b/skills/.curated/x-twitter-scraper/references/api-endpoints.md @@ -0,0 +1,1595 @@ +# Xquik REST API Endpoints + +Base URL: `https://xquik.com/api/v1` + +All requests require the `x-api-key` header. All responses are JSON. HTTPS only. + +## Table of Contents + +- [Account](#account) +- [API Keys](#api-keys) +- [Monitors](#monitors) +- [Events](#events) +- [Webhooks](#webhooks) +- [Draws](#draws) +- [Extractions](#extractions) +- [X API (Direct Lookups)](#x-api-direct-lookups) +- [X Media (Download)](#x-media-download) +- [Trends](#trends) +- [Radar](#radar) +- [Compose](#compose) +- [Drafts](#drafts) +- [Tweet Style Cache](#tweet-style-cache) +- [Account Identity](#account-identity) +- [Subscribe](#subscribe) +- [X Accounts (Connected)](#x-accounts-connected) +- [X Write](#x-write) +- [Integrations](#integrations) +- [Automations](#automations) +- [Credits](#credits) +- [Support](#support) + +--- + +## Account + +### Get Account + +``` +GET /account +``` + +Returns subscription status, monitor allocation, and current period usage. + +**Response:** +```json +{ + "plan": "active", + "monitorsAllowed": 1, + "monitorsUsed": 0, + "currentPeriod": { + "start": "2026-02-01T00:00:00.000Z", + "end": "2026-03-01T00:00:00.000Z", + "usagePercent": 45 + } +} +``` + +### Update Account + +``` +PATCH /account +``` + +Update account locale. Session auth only (not API key). + +**Body:** `{ "locale": "en" | "tr" | "es" }` + +--- + +## API Keys + +Session auth only. These endpoints do not accept API key auth. + +### Create API Key + +``` +POST /api-keys +``` + +**Body:** `{ "name": "My Key" }` (optional) + +**Response:** Returns `fullKey` (shown only once), `prefix`, `name`, `id`, `createdAt`. + +### List API Keys + +``` +GET /api-keys +``` + +Returns all keys with `id`, `name`, `prefix`, `isActive`, `createdAt`, `lastUsedAt`. Full key is never exposed. + +### Revoke API Key + +``` +DELETE /api-keys/{id} +``` + +Permanent and irreversible. The key stops working immediately. + +--- + +## Monitors + +### Create Monitor + +``` +POST /monitors +``` + +**Body:** +```json +{ + "username": "elonmusk", + "eventTypes": ["tweet.new", "tweet.reply", "tweet.quote"] +} +``` + +**Response:** +```json +{ + "id": "7", + "username": "elonmusk", + "xUserId": "44196397", + "eventTypes": ["tweet.new", "tweet.reply", "tweet.quote"], + "createdAt": "2026-02-24T10:30:00.000Z" +} +``` + +Event types: `tweet.new`, `tweet.quote`, `tweet.reply`, `tweet.retweet`, `follower.gained`, `follower.lost`. + +Returns `409 monitor_already_exists` if the username is already monitored. + +### List Monitors + +``` +GET /monitors +``` + +Returns all monitors (up to 200, no pagination). Response includes `monitors` array and `total` count. + +### Get Monitor + +``` +GET /monitors/{id} +``` + +### Update Monitor + +``` +PATCH /monitors/{id} +``` + +**Body:** `{ "eventTypes": [...], "isActive": true|false }` (both optional) + +### Delete Monitor + +``` +DELETE /monitors/{id} +``` + +Stops tracking and deletes all associated data. + +--- + +## Events + +### List Events + +``` +GET /events +``` + +**Query parameters:** + +| Param | Type | Description | +|-------|------|-------------| +| `monitorId` | string | Filter by monitor ID | +| `eventType` | string | Filter by event type | +| `limit` | number | Results per page (1-100, default 50) | +| `after` | string | Cursor for next page | + +**Response:** +```json +{ + "events": [ + { + "id": "9010", + "type": "tweet.new", + "monitorId": "7", + "username": "elonmusk", + "occurredAt": "2026-02-24T16:45:00.000Z", + "data": { + "tweetId": "1893556789012345678", + "text": "Hello world", + "metrics": { "likes": 3200, "retweets": 890, "replies": 245 } + } + } + ], + "hasMore": true, + "nextCursor": "MjAyNi0wMi0yNFQxNjozMDowMC4wMDBa..." +} +``` + +### Get Event + +``` +GET /events/{id} +``` + +Returns a single event with full details. + +--- + +## Webhooks + +### Create Webhook + +``` +POST /webhooks +``` + +**Body:** +```json +{ + "url": "https://your-server.com/webhook", + "eventTypes": ["tweet.new", "tweet.reply"] +} +``` + +**Response** includes a `secret` field (shown only once). Store it for signature verification. + +### List Webhooks + +``` +GET /webhooks +``` + +Returns all webhooks (up to 200). Secret is never exposed in list responses. + +### Update Webhook + +``` +PATCH /webhooks/{id} +``` + +**Body:** `{ "url": "...", "eventTypes": [...], "isActive": true|false }` (all optional) + +### Delete Webhook + +``` +DELETE /webhooks/{id} +``` + +Permanently removes the webhook. All future deliveries are stopped. + +### Test Webhook + +``` +POST /webhooks/{id}/test +``` + +Sends a `webhook.test` event to the webhook endpoint, HMAC-signed with the webhook's secret. Returns success or failure status with HTTP response details. + +**Payload delivered to your endpoint:** +```json +{ + "eventType": "webhook.test", + "data": { + "message": "Test delivery from Xquik" + }, + "timestamp": "2026-02-27T12:00:00.000Z" +} +``` + +The delivery includes the `X-Xquik-Signature` header, identical to production deliveries. + +Returns `400 webhook_inactive` if the webhook is disabled. Reactivate via `PATCH /webhooks/{id}` before testing. + +### List Deliveries + +``` +GET /webhooks/{id}/deliveries +``` + +View delivery attempts and statuses for a webhook. Statuses: `pending`, `delivered`, `failed`, `exhausted`. + +--- + +## Draws + +### Create Draw + +``` +POST /draws +``` + +Run a giveaway draw from a tweet. Picks random winners from replies. + +**Body:** +```json +{ + "tweetUrl": "https://x.com/user/status/1893456789012345678", + "winnerCount": 3, + "backupCount": 2, + "uniqueAuthorsOnly": true, + "mustRetweet": true, + "mustFollowUsername": "burakbayir", + "filterMinFollowers": 100, + "filterAccountAgeDays": 30, + "filterLanguage": "en", + "requiredKeywords": ["giveaway"], + "requiredHashtags": ["#contest"], + "requiredMentions": ["@xquik"] +} +``` + +All filter fields are optional. Only `tweetUrl` is required. + +**Response:** +```json +{ + "id": "42", + "tweetId": "1893456789012345678", + "tweetUrl": "https://x.com/user/status/1893456789012345678", + "tweetText": "Like & RT to enter! Picking 3 winners tomorrow.", + "tweetAuthorUsername": "xquik", + "tweetLikeCount": 4200, + "tweetRetweetCount": 1800, + "tweetReplyCount": 1500, + "tweetQuoteCount": 120, + "status": "completed", + "totalEntries": 1500, + "validEntries": 890, + "createdAt": "2026-02-24T10:00:00.000Z", + "drawnAt": "2026-02-24T10:01:00.000Z" +} +``` + +### List Draws + +``` +GET /draws +``` + +Cursor-paginated. Returns compact draw objects. + +### Get Draw + +``` +GET /draws/{id} +``` + +Returns full draw details including winners. + +### Export Draw + +``` +GET /draws/{id}/export?format=csv&type=winners +``` + +Formats: `csv`, `xlsx`, `md`. Types: `winners` (default), `entries`. Entry exports capped at 50,000 rows. + +--- + +## Extractions + +### Create Extraction + +``` +POST /extractions +``` + +Run a bulk data extraction job. See `references/extractions.md` for all 23 tool types. + +**Body:** +```json +{ + "toolType": "reply_extractor", + "targetTweetId": "1893704267862470862", + "resultsLimit": 500 +} +``` + +`resultsLimit` (optional): Maximum results to extract. Stops early instead of fetching all data. Useful for controlling costs. + +**Tweet Search Filters** (`tweet_search_extractor` only): + +| Field | Type | Description | +|-------|------|-------------| +| `fromUser` | string | Author username | +| `toUser` | string | Directed to user | +| `mentioning` | string | Mentions user | +| `language` | string | Language code (e.g., `en`) | +| `sinceDate` | string | Start date (YYYY-MM-DD) | +| `untilDate` | string | End date (YYYY-MM-DD) | +| `mediaType` | string | `images`, `videos`, `gifs`, or `media` | +| `minFaves` | number | Minimum likes | +| `minRetweets` | number | Minimum retweets | +| `minReplies` | number | Minimum replies | +| `verifiedOnly` | boolean | Verified authors only | +| `replies` | string | `include`, `exclude`, or `only` | +| `retweets` | string | `include`, `exclude`, or `only` | +| `exactPhrase` | string | Exact match text | +| `excludeWords` | string | Comma-separated words to exclude | +| `advancedQuery` | string | Raw X search operators appended to query | + +These filters are converted to X search operators and combined with `searchQuery`. + +**Response:** +```json +{ + "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "toolType": "reply_extractor", + "status": "running" +} +``` + +### Estimate Extraction + +``` +POST /extractions/estimate +``` + +Preview the cost before running. Same body as create. + +**Response:** +```json +{ + "allowed": true, + "source": "replyCount", + "estimatedResults": 150, + "usagePercent": 45, + "projectedPercent": 48 +} +``` + +### List Extractions + +``` +GET /extractions +``` + +Cursor-paginated. Filter by `status` and `toolType`. + +### Get Extraction + +``` +GET /extractions/{id} +``` + +Returns job details with paginated results (up to 1,000 per page). + +### Export Extraction + +``` +GET /extractions/{id}/export?format=csv +``` + +Formats: `csv`, `xlsx`, `md`. 50,000 row limit. Exports include enrichment columns not in the API response. + +--- + +## X API (Direct Lookups) + +Metered operations that count toward the monthly quota. + +### Get Tweet + +``` +GET /x/tweets/{id} +``` + +Returns full tweet with engagement metrics (likes, retweets, replies, quotes, views, bookmarks), author info (username, followers, verified status, profile picture), and optional attached media (photos/videos with URLs). + +### Get Article + +``` +GET /x/articles/{id} +``` + +Retrieve the full content of an X Article (long-form post) by tweet ID. Returns title, body text with block-level formatting, cover image, inline images, and engagement metrics. Metered. + +**Response:** +```json +{ + "title": "Why AI Will Transform Everything", + "coverImage": "https://pbs.twimg.com/...", + "bodyHtml": "
The future of AI...
", + "likeCount": 5200, + "retweetCount": 890, + "replyCount": 245, + "viewCount": 150000, + "bookmarkCount": 1200, + "author": { + "id": "44196397", + "username": "elonmusk", + "name": "Elon Musk" + } +} +``` + +### Search Tweets + +``` +GET /x/tweets/search?q={query} +``` + +Search using X syntax: keywords, `#hashtags`, `from:user`, `to:user`, `"exact phrases"`, `OR`, `-exclude`. + +Returns tweet info with optional engagement metrics (likeCount, retweetCount, replyCount) and optional attached media. Some fields may be omitted if unavailable. + +### Get User + +``` +GET /x/users/{username} +``` + +Returns profile info. Fields `id`, `username`, `name` are always present. All other fields (`description`, `followers`, `following`, `verified`, `profilePicture`, `location`, `createdAt`, `statusesCount`) are optional and omitted when unavailable. + +### Check Follower + +``` +GET /x/followers/check?source={username}&target={username} +``` + +Returns `isFollowing` and `isFollowedBy` for both directions. + +### Get User Tweets + +``` +GET /x/users/{id}/tweets +``` + +Get a user's recent tweets by user ID. Metered (1 credit/tweet). + +### Get User Likes + +``` +GET /x/users/{id}/likes +``` + +Get tweets liked by a user. Metered (1 credit/result). + +### Get User Media + +``` +GET /x/users/{id}/media +``` + +Get a user's media tweets (tweets containing photos/videos). Metered (1 credit/result). + +### Get Tweet Favoriters + +``` +GET /x/tweets/{id}/favoriters +``` + +Get users who liked a tweet. Metered (1 credit/result). + +### Get Mutual Followers + +``` +GET /x/users/{id}/followers-you-know +``` + +Get mutual followers (followers you know). Metered (1 credit/result). + +### Get Bookmarks + +``` +GET /x/bookmarks +``` + +Get bookmarked tweets. Requires a connected X account. Metered (1 credit/result). + +### Get Bookmark Folders + +``` +GET /x/bookmarks/folders +``` + +Get bookmark folders. Requires a connected X account. Metered (1 credit). + +### Get Notifications + +``` +GET /x/notifications +``` + +Get notifications with type filter. Requires a connected X account. Metered (1 credit/result). + +### Get Home Timeline + +``` +GET /x/timeline +``` + +Get home timeline. Requires a connected X account. Metered (1 credit/result). + +--- + +## X Media (Download) + +### Download Media + +``` +POST /x/media/download +``` + +Download images, videos, and GIFs from tweets. Single or bulk (up to 50). Returns a shareable gallery URL. + +**Body:** Provide either `tweetInput` (single tweet) or `tweetIds` (bulk). Exactly 1 is required. + +| Field | Type | Description | +|-------|------|-------------| +| `tweetInput` | string | Tweet URL or numeric tweet ID for a single download. Accepts `x.com` and `twitter.com` URL formats | +| `tweetIds` | string[] | Array of tweet URLs or IDs for bulk download. Maximum 50 items. Returns a single combined gallery | + +**Response (single):** +```json +{ + "tweetId": "1893456789012345678", + "galleryUrl": "https://xquik.com/gallery/abc123", + "cacheHit": false +} +``` + +**Response (bulk):** +```json +{ + "galleryUrl": "https://xquik.com/gallery/def456", + "totalTweets": 3, + "totalMedia": 7 +} +``` + +First download is metered (counts toward monthly quota). Subsequent requests for the same tweet return cached URLs at no cost (`cacheHit: true`). All downloads are saved to the gallery at `https://xquik.com/gallery`. + +Returns `400 no_media` if the tweet has no downloadable media. Returns `400 too_many_tweets` if bulk array exceeds 50 items. + +--- + +## Trends + +### List Trends + +``` +GET /trends?woeid=1&count=30 +``` + +Metered. Subscription required. Cached, refreshes every 15 minutes. + +**WOEIDs:** 1 (Worldwide), 23424977 (US), 23424975 (UK), 23424969 (Turkey), 23424950 (Spain), 23424829 (Germany), 23424819 (France), 23424856 (Japan), 23424848 (India), 23424768 (Brazil), 23424775 (Canada), 23424900 (Mexico). + +**Response:** +```json +{ + "trends": [ + { "name": "#AI", "description": "...", "rank": 1, "query": "#AI" } + ], + "total": 30, + "woeid": 1 +} +``` + +--- + +## Radar + +### List Radar Items + +``` +GET /radar +``` + +Get trending topics and news from 7 sources: Google Trends, Hacker News, Polymarket, TrustMRR, Wikipedia, GitHub Trending, Reddit. Free. + +**Query parameters:** + +| Param | Type | Description | +|-------|------|-------------| +| `source` | string | Filter by source: `google_trends`, `hacker_news`, `polymarket`, `trustmrr`, `wikipedia`, `github`, `reddit` | +| `category` | string | Filter by category: `general`, `tech`, `dev`, `science`, `culture`, `politics`, `business`, `entertainment` | +| `limit` | number | Items per page (1-100, default 50) | +| `hours` | number | Look-back window in hours (1-72, default 6) | +| `region` | string | Region code: `US`, `GB`, `TR`, `ES`, `DE`, `FR`, `JP`, `IN`, `BR`, `CA`, `MX`, `global` (default) | + +**Response:** +```json +{ + "items": [ + { + "id": "12345", + "title": "Claude 4.6 Released", + "description": "Anthropic releases Claude 4.6...", + "url": "https://example.com/article", + "imageUrl": "https://example.com/image.png", + "source": "hacker_news", + "sourceId": "hn_12345", + "category": "tech", + "region": "global", + "language": "en", + "score": 450, + "metadata": { "points": 450, "numberComments": 132, "author": "pgdev" }, + "publishedAt": "2026-03-05T10:00:00.000Z", + "createdAt": "2026-03-05T10:05:00.000Z" + } + ], + "hasMore": true, + "nextCursor": "NDUwfDIwMjYtMDMtMDRUMDg6MzA6MDAuMDAwWnwxMjM0NQ==" +} +``` + +Fields: `id`, `title`, `description?`, `url?`, `imageUrl?`, `source`, `sourceId`, `category`, `region`, `language`, `score`, `metadata`, `publishedAt`, `createdAt`. Response includes `hasMore` and `nextCursor` for pagination. + +--- + +## Compose + +### Compose Tweet + +``` +POST /compose +``` + +Compose, refine, and score tweets using X algorithm data. Free, 3-step workflow. + +**Body:** + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `step` | string | Yes | `compose`, `refine`, or `score` | +| `topic` | string | No | Tweet topic (compose, refine) | +| `goal` | string | No | `engagement`, `followers`, `authority`, `conversation` | +| `styleUsername` | string | No | Cached style username for voice matching (compose) | +| `tone` | string | No | Desired tone (refine) | +| `additionalContext` | string | No | Extra context or URLs (refine) | +| `callToAction` | string | No | Desired CTA (refine) | +| `mediaType` | string | No | `photo`, `video`, `none` (refine) | +| `draft` | string | No | Tweet text to evaluate (score) | +| `hasLink` | boolean | No | Link attached (score) | +| `hasMedia` | boolean | No | Media attached (score) | + +**Response (step=compose):** Returns `contentRules`, `scorerWeights`, `followUpQuestions`, `algorithmInsights`, `engagementMultipliers`, `topPenalties`. + +**Response (step=refine):** Returns `compositionGuidance`, `examplePatterns`. + +**Response (step=score):** Returns `totalChecks`, `passedCount`, `topSuggestion`, `checklist[]` with `factor`, `passed`, `suggestion`. + +--- + +## Drafts + +### Create Draft + +`POST /drafts` + +Save a tweet draft for later. + +**Request body:** + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `text` | string | Yes | The draft tweet text | +| `topic` | string | No | Topic the tweet is about | +| `goal` | string | No | Optimization goal: `engagement`, `followers`, `authority`, `conversation` | + +**Response (201):** + +```json +{ + "id": "123", + "text": "draft text", + "topic": "product launch", + "goal": "engagement", + "createdAt": "2026-02-24T10:30:00.000Z", + "updatedAt": "2026-02-24T10:30:00.000Z" +} +``` + +--- + +### List Drafts + +`GET /drafts` + +List saved tweet drafts with cursor pagination. + +**Query parameters:** + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `limit` | number | No | 50 | Results per page (max 50) | +| `afterCursor` | string | No | - | Pagination cursor from previous response | + +**Response (200):** + +```json +{ + "drafts": [ + { + "id": "123", + "text": "draft text", + "topic": "product launch", + "goal": "engagement", + "createdAt": "2026-02-24T10:30:00.000Z", + "updatedAt": "2026-02-24T10:30:00.000Z" + } + ], + "afterCursor": "cursor_string", + "hasMore": true +} +``` + +--- + +### Get Draft + +`GET /drafts/{id}` + +Get a specific draft by ID. + +**Response (200):** Single draft object. + +**Errors:** `400 invalid_id`, `404 draft_not_found` + +--- + +### Delete Draft + +`DELETE /drafts/{id}` + +Delete a draft. Returns `204 No Content`. + +**Errors:** `400 invalid_id`, `404 draft_not_found` + +--- + +## Tweet Style Cache + +### Analyze & Cache Style + +`POST /styles` + +Fetch recent tweets from an X account and cache them for style analysis. **Consumes API usage credits.** + +**Request body:** + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `username` | string | Yes | X username to analyze (without @) | + +**Response (201):** + +```json +{ + "xUsername": "elonmusk", + "tweetCount": 20, + "isOwnAccount": false, + "fetchedAt": "2026-02-24T10:30:00.000Z", + "tweets": [ + { + "id": "1893456789012345678", + "text": "The future is now.", + "authorUsername": "elonmusk", + "createdAt": "2026-02-24T14:22:00.000Z" + } + ] +} +``` + +--- + +### List Cached Styles + +`GET /styles` + +List all cached tweet style profiles. Max 200 results, ordered by fetch date. + +**Response (200):** + +```json +{ + "styles": [ + { + "xUsername": "elonmusk", + "tweetCount": 20, + "isOwnAccount": false, + "fetchedAt": "2026-02-24T10:30:00.000Z" + } + ] +} +``` + +--- + +### Save Custom Style + +`PUT /styles/{username}` + +Save a custom style profile from tweet texts. Free, no usage cost. Replaces existing style if one exists with the same label. + +**Body:** + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `label` | string | Yes | Style label name (1-30 characters) | +| `tweets` | object[] | Yes | Array of tweet objects (1-100). Each must have a `text` field | + +**Response (200):** Style object with label, `tweetCount`, `isOwnAccount: false`, `fetchedAt`, and `tweets` array. + +**Errors:** `400 invalid_input` + +--- + +### Get Cached Style + +`GET /styles/{username}` + +Get a cached style profile with full tweet data. + +**Response (200):** Full style object with `tweets` array. + +**Errors:** `404 style_not_found` + +--- + +### Delete Cached Style + +`DELETE /styles/{username}` + +Delete a cached style. Returns `204 No Content`. + +**Errors:** `404 style_not_found` + +--- + +### Compare Styles + +`GET /styles/compare?username1=A&username2=B` + +Compare two cached tweet style profiles side by side. + +**Query parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `username1` | string | Yes | First X username | +| `username2` | string | Yes | Second X username | + +**Response (200):** + +```json +{ + "style1": { "xUsername": "user1", "tweetCount": 20, "isOwnAccount": true, "fetchedAt": "...", "tweets": [...] }, + "style2": { "xUsername": "user2", "tweetCount": 15, "isOwnAccount": false, "fetchedAt": "...", "tweets": [...] } +} +``` + +**Errors:** `400 missing_params`, `404 style_not_found` + +--- + +### Analyze Performance + +`GET /styles/{username}/performance` + +Get live engagement metrics for cached tweets. **Consumes API usage credits.** + +**Response (200):** + +```json +{ + "xUsername": "elonmusk", + "tweetCount": 20, + "tweets": [ + { + "id": "1893456789012345678", + "text": "The future is now.", + "likeCount": 42000, + "retweetCount": 8500, + "replyCount": 3200, + "quoteCount": 1100, + "viewCount": 5000000, + "bookmarkCount": 2400 + } + ] +} +``` + +**Errors:** `404 style_not_found` + +--- + +## Account Identity + +### Set X Identity + +`PUT /account/x-identity` + +Link your X username to your Xquik account. Required for own-account detection in style analysis. + +**Request body:** + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `username` | string | Yes | Your X username (without @) | + +**Response (200):** + +```json +{ + "success": true, + "xUsername": "elonmusk" +} +``` + +**Errors:** `400 invalid_input` + +--- + +## Subscribe + +### Get Subscription Link + +``` +POST /subscribe +``` + +Returns a Stripe Checkout URL for subscribing or managing the subscription. If already subscribed, returns the billing portal URL. + +**Response:** +```json +{ + "url": "https://checkout.stripe.com/c/pay/..." +} +``` + +--- + +## X Accounts (Connected) + +Manage connected X accounts for write actions. All endpoints are free (no usage cost). + +### List X Accounts + +``` +GET /x/accounts +``` + +Returns all connected X accounts. Response: `{ accounts: [{ id, username, displayName, isActive, createdAt }] }`. + +### Connect X Account + +``` +POST /x/accounts +``` + +**Body:** + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `username` | string | Yes | X username (`@` auto-stripped) | +| `email` | string | Yes | Email associated with the X account | +| `password` | string | Yes | Account password (encrypted at rest) | +| `totp_secret` | string | No | TOTP base32 secret for 2FA accounts | +| `proxy_country` | string | No | Preferred proxy region (e.g. `"US"`, `"TR"`) | + +**Response (201):** `{ id, username, isActive, createdAt }` + +**Errors:** `409 account_already_connected`, `422 login_failed` + +### Get X Account + +``` +GET /x/accounts/{id} +``` + +Returns `{ id, username, displayName, isActive, createdAt }`. + +### Disconnect X Account + +``` +DELETE /x/accounts/{id} +``` + +Permanently removes the account and deletes stored credentials. Returns `{ success: true }`. + +### Re-authenticate X Account + +``` +POST /x/accounts/{id}/reauth +``` + +Use when a session expires or X requires re-verification. + +**Body:** `{ "password": "...", "totp_secret": "..." }` (password required, totp_secret optional) + +**Response:** `{ success: true }` + +**Errors:** `422 reauth_failed` + +--- + +## X Write + +Write actions performed through connected X accounts. All endpoints are metered. Every request requires an `account` field (username or account ID) identifying which connected account to use. + +### Create Tweet + +``` +POST /x/tweets +``` + +**Body:** + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `account` | string | Yes | Connected X username or account ID | +| `text` | string | Yes | Tweet text (280 chars, or 25,000 if `is_note_tweet` is true) | +| `reply_to_tweet_id` | string | No | Tweet ID to reply to | +| `attachment_url` | string | No | URL to attach as a card | +| `community_id` | string | No | Community ID to post into | +| `is_note_tweet` | boolean | No | Long-form note tweet (up to 25,000 chars) | +| `media_ids` | string[] | No | Media IDs from `POST /x/media` (max 4 images or 1 video) | + +**Response:** `{ tweetId, success: true }` + +**Errors:** `502 x_write_failed` + +### Delete Tweet + +``` +DELETE /x/tweets/{id} +``` + +**Body:** `{ "account": "username" }` + +**Response:** `{ success: true }` + +### Like Tweet + +``` +POST /x/tweets/{id}/like +``` + +**Body:** `{ "account": "username" }` + +### Unlike Tweet + +``` +DELETE /x/tweets/{id}/like +``` + +**Body:** `{ "account": "username" }` + +### Retweet + +``` +POST /x/tweets/{id}/retweet +``` + +**Body:** `{ "account": "username" }` + +### Follow User + +``` +POST /x/users/{id}/follow +``` + +**Body:** `{ "account": "username" }` + +**Errors:** `502 x_write_failed` + +### Unfollow User + +``` +DELETE /x/users/{id}/follow +``` + +**Body:** `{ "account": "username" }` + +### Send DM + +``` +POST /x/dm/{userId} +``` + +**Body:** + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `account` | string | Yes | Connected X username or account ID | +| `text` | string | Yes | Message text | +| `media_ids` | string[] | No | Media IDs to attach | +| `reply_to_message_id` | string | No | Message ID to reply to | + +### Get DM History + +``` +GET /x/dm/{userId}/history +``` + +Get DM conversation history with a user. Requires a connected X account. Metered (1 credit/result). + +### Update Profile + +``` +PATCH /x/profile +``` + +**Body:** `{ "account": "username", "name": "...", "description": "...", "location": "...", "url": "..." }` (account required, others optional) + +### Update Avatar + +``` +PATCH /x/profile/avatar +``` + +Update profile avatar. Max 700 KB, GIF/JPEG/PNG. Metered (2 credits). + +**Body:** FormData with `account` (required) and `file` (required, max 700 KB). + +### Update Banner + +``` +PATCH /x/profile/banner +``` + +Update profile banner. Max 2 MB, GIF/JPEG/PNG. Metered (2 credits). + +**Body:** FormData with `account` (required) and `file` (required, max 2 MB). + +### Upload Media + +``` +POST /x/media +``` + +**Body:** FormData with `account` (required), `file` (required), and `is_long_video` (optional boolean). Alternatively, JSON body with `account` (required) and `url` (required, direct media URL) for URL-based upload. + +**Response:** Returns a media ID to pass in `media_ids` when creating a tweet. + +### Create Community + +``` +POST /x/communities +``` + +**Body:** `{ "account": "username", "name": "...", "description": "..." }` (all required) + +### Delete Community + +``` +DELETE /x/communities/{id} +``` + +**Body:** `{ "account": "username", "community_name": "..." }` (name required for confirmation) + +### Join Community + +``` +POST /x/communities/{id}/join +``` + +**Body:** `{ "account": "username" }` + +**Errors:** `409 already_member` + +### Leave Community + +``` +DELETE /x/communities/{id}/join +``` + +**Body:** `{ "account": "username" }` + +--- + +## Integrations + +Manage third-party integrations (currently Telegram) that receive monitor event notifications. All endpoints are free (no usage cost). + +### Create Integration + +``` +POST /integrations +``` + +**Body:** + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `type` | string | Yes | Integration type: `"telegram"` | +| `name` | string | Yes | Human-readable name | +| `config` | object | Yes | Type-specific config. Telegram: `{ chatId: "-1001234567890" }` | +| `eventTypes` | string[] | Yes | Event types: `tweet.new`, `tweet.quote`, `tweet.reply`, `tweet.retweet`, `draw.completed`, `extraction.completed`, `extraction.failed` | + +**Response (201):** `{ id, type, name, config, eventTypes, isActive, createdAt }` + +### List Integrations + +``` +GET /integrations +``` + +Returns all integrations. Response: `{ integrations: [...] }`. + +### Get Integration + +``` +GET /integrations/{id} +``` + +Returns a single integration with full details. + +### Update Integration + +``` +PATCH /integrations/{id} +``` + +**Body:** `{ "name": "...", "eventTypes": [...], "isActive": true|false, "silentPush": false, "scopeAllMonitors": true, "filters": {}, "messageTemplate": {} }` (all optional, at least 1 required) + +### Delete Integration + +``` +DELETE /integrations/{id} +``` + +Permanently removes the integration. Returns `204 No Content`. + +### Test Integration + +``` +POST /integrations/{id}/test +``` + +Sends a test notification. Returns success or failure status. + +**Errors:** `502 delivery_failed` + +### List Deliveries + +``` +GET /integrations/{id}/deliveries +``` + +View delivery attempts and statuses. Statuses: `pending`, `delivered`, `failed`, `exhausted`. + +**Query:** `limit` (default 50). + +--- + +## Automations + +Trigger-driven workflow automation. Create flows with triggers (monitor events, schedules, search, inbound webhooks) and action steps. + +### List Automations + +``` +GET /automations +``` + +Returns all flows. Response: `{ items: [{ id, name, slug, triggerType, triggerConfig, isActive, runCount, lastRunAt, minIntervalSeconds, pausedReason, templateSlug, xAccountId, createdAt, updatedAt }] }`. + +### Create Automation + +``` +POST /automations +``` + +**Body:** +```json +{ + "name": "New Follower Welcome", + "triggerType": "monitor_event", + "triggerConfig": { "eventType": "follower.gained" }, + "templateSlug": "welcome-dm" +} +``` + +Trigger types: `monitor_event`, `schedule`, `search`, `webhook_inbound`. + +**Response (201):** Flow object with `id`, `slug`, `isActive: false`. + +Flows are created inactive. Add steps, then activate via `PATCH /automations/{slug}`. + +Free: 2 flows. Subscribers: 10 flows. + +### Get Automation + +``` +GET /automations/{slug} +``` + +Returns flow with steps and 20 most recent runs. + +### Update Automation + +``` +PATCH /automations/{slug} +``` + +**Body:** `{ "expectedUpdatedAt": "...", "name": "...", "triggerType": "...", "triggerConfig": {...}, "isActive": true|false }`. `expectedUpdatedAt` required (optimistic concurrency). Returns `409 conflict` if stale. + +Activation requires an active subscription and at least 1 action step. + +### Delete Automation + +``` +DELETE /automations/{slug} +``` + +Deletes the flow and all its steps. Returns `{ success: true }`. + +### Add Step + +``` +POST /automations/{slug}/steps +``` + +**Body:** +```json +{ + "stepType": "action", + "actionType": "send_dm", + "branch": "main", + "config": { "message": "Welcome!" }, + "position": 0 +} +``` + +Step types: `action`, `condition`, `extraction`. Max 10 steps per flow. + +Action types: `create_tweet`, `follow`, `like`, `reply_tweet`, `retweet`, `send_dm`, `send_email`, `send_telegram`, `unfollow`. + +Extraction types: all 23 extraction tool types (kebab-case, e.g. `reply-extractor`). Requires `outputName` for variable reference in later steps. + +### Update Step + +``` +PATCH /automations/{slug}/steps +``` + +**Body:** `{ "stepId": "101", "config": {...}, "positionX": 250, "positionY": 100 }`. `stepId` required. + +### Delete Step + +``` +DELETE /automations/{slug}/steps +``` + +**Body:** `{ "stepId": "101" }`. + +### Update Step Positions + +``` +PATCH /automations/{slug}/steps/positions +``` + +Batch update canvas positions: `{ "positions": [{ "stepId": "101", "positionX": 250, "positionY": 100 }] }`. + +### Test Automation + +``` +POST /automations/{slug}/test +``` + +Not yet implemented. Returns `{ status: "not_implemented" }`. + +### Inbound Webhook Trigger + +``` +POST /webhooks/inbound/{token} +``` + +No auth header required. The URL token identifies the flow. Accepts any JSON body as trigger payload. Rate limited per flow (60/hour) and per user (300/hour). + +--- + +## Credits + +### Get Credit Balance + +``` +GET /credits +``` + +Get credit balance, lifetime purchased/used, and auto top-up status. Free. + +### Top Up Credits + +``` +POST /credits/topup +``` + +Get a Stripe checkout URL to purchase credits ($10 minimum). Free. + +--- + +## Support + +### Create Ticket + +``` +POST /support/tickets +``` + +**Body:** `{ "subject": "...", "body": "..." }` + +**Response (201):** `{ id, subject, status, createdAt }` + +### List Tickets + +``` +GET /support/tickets +``` + +Returns all tickets for the authenticated user. + +### Get Ticket + +``` +GET /support/tickets/{id} +``` + +Returns ticket with messages. + +### Update Ticket + +``` +PATCH /support/tickets/{id} +``` + +Update ticket status. + +### Reply to Ticket + +``` +POST /support/tickets/{id}/messages +``` + +**Body:** `{ "body": "..." }` + +Add a message to an existing ticket. + +--- + +## Error Codes + +| Status | Code | Meaning | +|--------|------|---------| +| 400 | `invalid_input` | Request body failed validation | +| 400 | `invalid_id` | Path parameter is not a valid ID | +| 400 | `invalid_json` | Invalid JSON in request body | +| 400 | `invalid_tweet_url` | Tweet URL format is invalid | +| 400 | `invalid_tweet_id` | Tweet ID is empty or invalid | +| 400 | `invalid_username` | X username is empty or invalid | +| 400 | `invalid_tool_type` | Extraction tool type not recognized | +| 400 | `invalid_format` | Export format not `csv`, `xlsx`, or `md` | +| 400 | `invalid_params` | Export query parameters are missing or invalid | +| 400 | `missing_query` | Required query parameter is missing | +| 400 | `missing_params` | Required query parameters are missing | +| 400 | `no_media` | Tweet has no downloadable media | +| 400 | `webhook_inactive` | Webhook is disabled (test-webhook only) | +| 401 | `unauthenticated` | Missing or invalid API key | +| 403 | `account_needs_reauth` | X account session expired, re-authenticate | +| 402 | `no_subscription` | No active subscription | +| 402 | `subscription_inactive` | Subscription is not active | +| 402 | `usage_limit_reached` | Monthly usage cap exceeded | +| 402 | `extra_usage_disabled` | Extra usage not enabled | +| 402 | `extra_usage_requires_v2` | Extra usage requires the new pricing plan | +| 402 | `frozen` | Extra usage paused, outstanding payment required | +| 402 | `overage_limit_reached` | Overage spending limit reached | +| 402 | `no_addon` | No monitor addon on subscription | +| 403 | `monitor_limit_reached` | Plan monitor limit exceeded | +| 403 | `api_key_limit_reached` | API key limit reached (100 max) | +| 403 | `flow_limit_reached` | Flow limit reached (free: 2, subscriber: 10) | +| 403 | `step_limit_reached` | Step limit reached (10 per flow) | +| 404 | `not_found` | Resource does not exist | +| 404 | `user_not_found` | X user not found | +| 404 | `tweet_not_found` | Tweet not found | +| 404 | `style_not_found` | No cached style found | +| 404 | `draft_not_found` | Draft not found | +| 409 | `monitor_already_exists` | Duplicate monitor for same username | +| 409 | `conflict` | Concurrent edit conflict (automation updates) | +| 422 | `login_failed` | X credential verification failed | +| 429 | - | Rate limited. Retry with backoff | +| 429 | `x_api_rate_limited` | X data source rate limited. Retry | +| 500 | `internal_error` | Server error | +| 502 | `stream_registration_failed` | Stream registration failed. Retry | +| 502 | `x_api_unavailable` | X data source temporarily unavailable | +| 502 | `x_api_unauthorized` | X data source authentication failed. Retry | +| 502 | `delivery_failed` | Integration test delivery failed | diff --git a/skills/.curated/x-twitter-scraper/references/extractions.md b/skills/.curated/x-twitter-scraper/references/extractions.md new file mode 100644 index 00000000..d9210407 --- /dev/null +++ b/skills/.curated/x-twitter-scraper/references/extractions.md @@ -0,0 +1,229 @@ +# Xquik Extraction Tools + +23 bulk data extraction tools. Each requires a specific target parameter. + +**Endpoint:** `POST /extractions` + +**Always estimate first:** `POST /extractions/estimate` with the same body to preview cost and check quota. + +## Tool Types + +### Tweet-Based (require `targetTweetId`) + +| Tool Type | Description | +|-----------|-------------| +| `reply_extractor` | Extract users who replied to a tweet | +| `repost_extractor` | Extract users who retweeted a tweet | +| `quote_extractor` | Extract users who quote-tweeted a tweet | +| `thread_extractor` | Extract all tweets in a thread | +| `article_extractor` | Extract article content linked in a tweet | +| `favoriters` | Extract users who favorited a tweet | + +**Example:** +```json +{ + "toolType": "reply_extractor", + "targetTweetId": "1893704267862470862" +} +``` + +### User-Based (require `targetUsername`) + +| Tool Type | Description | +|-----------|-------------| +| `follower_explorer` | Extract followers of an account | +| `following_explorer` | Extract accounts followed by a user | +| `verified_follower_explorer` | Extract verified followers of an account | +| `mention_extractor` | Extract tweets mentioning an account | +| `post_extractor` | Extract posts from an account | + +**Example:** +```json +{ + "toolType": "follower_explorer", + "targetUsername": "elonmusk" +} +``` + +The `@` prefix is automatically stripped if included. + +### User-Based by ID (require `targetUserId`) + +| Tool Type | Description | +|-----------|-------------| +| `user_likes` | Extract tweets liked by a user | +| `user_media` | Extract media tweets from a user | + +**Example:** +```json +{ + "toolType": "user_likes", + "targetUserId": "44196397" +} +``` + +### Community-Based (require `targetCommunityId`) + +| Tool Type | Description | +|-----------|-------------| +| `community_extractor` | Extract members of a community | +| `community_moderator_explorer` | Extract moderators of a community | +| `community_post_extractor` | Extract posts from a community | +| `community_search` | Search posts within a community (also requires `searchQuery`) | + +**Example:** +```json +{ + "toolType": "community_extractor", + "targetCommunityId": "1234567890" +} +``` + +### List-Based (require `targetListId`) + +| Tool Type | Description | +|-----------|-------------| +| `list_member_extractor` | Extract members of a list | +| `list_post_extractor` | Extract posts from a list | +| `list_follower_explorer` | Extract followers of a list | + +**Example:** +```json +{ + "toolType": "list_member_extractor", + "targetListId": "1234567890" +} +``` + +### Space-Based (require `targetSpaceId`) + +| Tool Type | Description | +|-----------|-------------| +| `space_explorer` | Extract participants of a Space | + +**Example:** +```json +{ + "toolType": "space_explorer", + "targetSpaceId": "1YqKDqDXAbwKV" +} +``` + +### Search-Based (require `searchQuery`) + +| Tool Type | Description | +|-----------|-------------| +| `people_search` | Search for users by keyword | +| `tweet_search_extractor` | Search and extract tweets by keyword or hashtag (bulk, up to 1,000) | + +**Example (people search):** +```json +{ + "toolType": "people_search", + "searchQuery": "machine learning engineer" +} +``` + +**Example (tweet search):** +```json +{ + "toolType": "tweet_search_extractor", + "searchQuery": "#AI", + "resultsLimit": 100 +} +``` + +### Tweet Search Filters + +The `tweet_search_extractor` tool type supports 16 additional filter fields that are converted to X search operators and combined with `searchQuery`: + +| Field | Type | Description | +|-------|------|-------------| +| `fromUser` | string | Author username | +| `toUser` | string | Directed to user | +| `mentioning` | string | Mentions user | +| `language` | string | Language code (e.g., `en`) | +| `sinceDate` | string | Start date (YYYY-MM-DD) | +| `untilDate` | string | End date (YYYY-MM-DD) | +| `mediaType` | string | `images`, `videos`, `gifs`, or `media` | +| `minFaves` | number | Minimum likes | +| `minRetweets` | number | Minimum retweets | +| `minReplies` | number | Minimum replies | +| `verifiedOnly` | boolean | Verified authors only | +| `replies` | string | `include`, `exclude`, or `only` | +| `retweets` | string | `include`, `exclude`, or `only` | +| `exactPhrase` | string | Exact match text | +| `excludeWords` | string | Comma-separated words to exclude | +| `advancedQuery` | string | Raw X search operators appended to query | + +**Example with filters:** +```json +{ + "toolType": "tweet_search_extractor", + "searchQuery": "AI", + "fromUser": "elonmusk", + "minFaves": 100, + "sinceDate": "2026-01-01", + "mediaType": "videos", + "resultsLimit": 500 +} +``` + +`resultsLimit` (optional): Maximum results to extract. Stops early instead of fetching all. Pass this on both `POST /extractions/estimate` and `POST /extractions` when you only need a specific count. + +## Response + +```json +{ + "id": "77777", + "toolType": "reply_extractor", + "status": "completed", + "totalResults": 150 +} +``` + +Statuses: `pending`, `running`, `completed`, `failed`. + +## Retrieving Results + +``` +GET /extractions/{id} +``` + +Returns paginated results (up to 1,000 per page). Each result includes: + +- `xUserId`, `xUsername`, `xDisplayName` +- `xFollowersCount`, `xVerified`, `xProfileImageUrl` +- `tweetId`, `tweetText`, `tweetCreatedAt` (for tweet-based extractions) + +## Exporting Results + +``` +GET /extractions/{id}/export?format=csv +``` + +Formats: `csv`, `xlsx`, `md`. 50,000 row limit. + +Exports include enrichment columns not present in the API response. + +## Estimating Cost + +``` +POST /extractions/estimate +``` + +Same body as create. Response: + +```json +{ + "allowed": true, + "source": "replyCount", + "estimatedResults": 150, + "usagePercent": 45, + "projectedPercent": 48 +} +``` + +If `allowed` is `false`, the extraction would exceed your monthly quota. + +For common mistakes and tool selection rules, see [mcp-tools.md](mcp-tools.md#common-mistakes). diff --git a/skills/.curated/x-twitter-scraper/references/mcp-setup.md b/skills/.curated/x-twitter-scraper/references/mcp-setup.md new file mode 100644 index 00000000..8be00b9d --- /dev/null +++ b/skills/.curated/x-twitter-scraper/references/mcp-setup.md @@ -0,0 +1,201 @@ +# Xquik MCP Server Setup + +Connect AI agents and IDEs to Xquik via the Model Context Protocol. The MCP server uses the same API key as the REST API. + +| Setting | Value | +|---------|-------| +| Protocol | HTTP (StreamableHTTP) | +| Endpoint | `https://xquik.com/mcp` | +| Auth header | `x-api-key` | + +## Claude.ai (Web) + +Claude.ai supports MCP connectors natively via OAuth. Add Xquik as a connector from **Settings > Feature Preview > Integrations > Add More > Xquik**. The OAuth 2.1 flow handles authentication automatically. No API key needed. + +## Claude Desktop + +Claude Desktop only supports stdio transport. Use `mcp-remote` as a bridge (requires [Node.js](https://nodejs.org)). + +Add to `claude_desktop_config.json`: + +```json +{ + "mcpServers": { + "xquik": { + "command": "npx", + "args": [ + "mcp-remote@latest", + "https://xquik.com/mcp", + "--header", + "x-api-key:xq_YOUR_KEY_HERE" + ] + } + } +} +``` + +## Claude Code + +Add to `.mcp.json`: + +```json +{ + "mcpServers": { + "xquik": { + "type": "http", + "url": "https://xquik.com/mcp", + "headers": { + "x-api-key": "xq_YOUR_KEY_HERE" + } + } + } +} +``` + +## ChatGPT + +3 ways to connect ChatGPT to Xquik: + +### Option 1: Custom GPT (Recommended) + +Create a Custom GPT and add Xquik as an Action using the OpenAPI schema at `https://docs.xquik.com/openapi.json`. Set the API key under Authentication > API Key > Header `x-api-key`. + +### Option 2: Agents SDK + +Use the [OpenAI Agents SDK](https://openai.github.io/openai-agents-python/mcp/) for programmatic access: + +```python +from agents.mcp import MCPServerStreamableHttp + +async with MCPServerStreamableHttp( + url="https://xquik.com/mcp", + headers={"x-api-key": "xq_YOUR_KEY_HERE"}, + params={}, +) as xquik: + # use xquik as a tool provider + pass +``` + +### Option 3: Developer Mode + +ChatGPT Developer Mode supports MCP connectors via OAuth. Add Xquik from **Settings > Developer Mode > MCP Tools > Add**. Enter `https://xquik.com/mcp` as the endpoint. OAuth handles authentication automatically. + +## Codex CLI + +Add to `~/.codex/config.toml`: + +```toml +[mcp_servers.xquik] +url = "https://xquik.com/mcp" +http_headers = { "x-api-key" = "xq_YOUR_KEY_HERE" } +``` + +## Cursor + +Add to `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (project): + +```json +{ + "mcpServers": { + "xquik": { + "url": "https://xquik.com/mcp", + "headers": { + "x-api-key": "xq_YOUR_KEY_HERE" + } + } + } +} +``` + +## VS Code + +Add to `.vscode/mcp.json` (project) or use **MCP: Open User Configuration** (global): + +```json +{ + "servers": { + "xquik": { + "type": "http", + "url": "https://xquik.com/mcp", + "headers": { + "x-api-key": "xq_YOUR_KEY_HERE" + } + } + } +} +``` + +## Windsurf + +Add to `~/.codeium/windsurf/mcp_config.json`: + +```json +{ + "mcpServers": { + "xquik": { + "serverUrl": "https://xquik.com/mcp", + "headers": { + "x-api-key": "xq_YOUR_KEY_HERE" + } + } + } +} +``` + +## OpenCode + +Add to `opencode.json`: + +```json +{ + "mcp": { + "xquik": { + "type": "remote", + "url": "https://xquik.com/mcp", + "headers": { + "x-api-key": "xq_YOUR_KEY_HERE" + } + } + } +} +``` + +## MCP Server Architecture + +The default MCP server (v2) at `https://xquik.com/mcp` uses a **code-execution sandbox model** with 2 tools: + +| Tool | Description | Cost | +|------|-------------|------| +| `explore` | Search the API endpoint catalog (read-only, no network calls) | Free | +| `xquik` | Execute API calls against your account | Varies by endpoint | + +The agent writes async JavaScript arrow functions that run in a sandboxed environment. Auth is injected automatically. The sandbox covers all 97 REST API endpoints across 12 categories: account, automations, bot, composition, extraction, integrations, media, monitoring, support, twitter, x-accounts, and x-write. + +## After Setup + +### Workflow Patterns + +| Workflow | Steps (via `xquik` tool) | +|----------|--------------------------| +| Set up real-time alerts | `POST /monitors` -> `POST /webhooks` -> `POST /webhooks/{id}/test` | +| Run a giveaway | `GET /account` -> `POST /draws` | +| Bulk extraction | `POST /extractions/estimate` -> `POST /extractions` -> `GET /extractions/{id}` | +| Compose optimized tweet | `POST /compose` (step=compose -> refine -> score) | +| Subscribe or manage billing | `POST /subscribe` | + +### Example Prompts + +Try these with your AI agent: + +- "Monitor @vercel for new tweets and quote tweets" +- "How many followers does @elonmusk have?" +- "Search for tweets mentioning xquik" +- "What does this tweet say? https://x.com/elonmusk/status/1893456789012345678" +- "Does @elonmusk follow @SpaceX back?" +- "Pick 3 winners from this tweet: https://x.com/burakbayir/status/1893456789012345678" +- "How much would it cost to extract all followers of @elonmusk?" +- "What's trending in the US right now?" +- "What's trending on Hacker News today?" +- "Help me write a tweet about launching my product" +- "Set up a webhook at https://my-server.com/events for new tweets" +- "What plan am I on and how much have I used?" diff --git a/skills/.curated/x-twitter-scraper/references/mcp-tools.md b/skills/.curated/x-twitter-scraper/references/mcp-tools.md new file mode 100644 index 00000000..646bd462 --- /dev/null +++ b/skills/.curated/x-twitter-scraper/references/mcp-tools.md @@ -0,0 +1,151 @@ +# Xquik MCP Tools Reference + +The MCP server at `https://xquik.com/mcp` uses a code-execution sandbox model with 2 tools. The agent writes async JavaScript arrow functions that run in a sandboxed environment with auth injected automatically. + +## Tools + +| Tool | Description | Cost | +|------|-------------|------| +| `explore` | Search the API endpoint catalog (read-only, no network calls) | Free | +| `xquik` | Execute API calls against your account | Varies by endpoint | + +### `explore` — Search the API Spec + +The sandbox provides an in-memory `spec.endpoints` array. Filter/search it to find endpoints before calling them. + +```typescript +interface EndpointInfo { + method: string; + path: string; + summary: string; + category: string; // account, automations, bot, composition, credits, extraction, integrations, media, monitoring, support, twitter, x-accounts, x-write + free: boolean; + parameters?: Array<{ name: string; in: 'query' | 'path' | 'body'; required: boolean; type: string; description: string }>; + responseShape?: string; +} + +declare const spec: { endpoints: EndpointInfo[] }; +``` + +Examples: + +```javascript +// Find all free endpoints +async () => spec.endpoints.filter(e => e.free); + +// Find endpoints by category +async () => spec.endpoints.filter(e => e.category === 'x-write'); + +// Search by keyword +async () => spec.endpoints.filter(e => e.summary.toLowerCase().includes('tweet')); +``` + +### `xquik` — Execute API Calls + +The sandbox provides `xquik.request()` with auth injected automatically. Never pass API keys. + +```typescript +declare const xquik: { + request(path: string, options?: { + method?: string; // default: 'GET' + body?: unknown; + query?: Record