diff --git a/docs/README.skills.md b/docs/README.skills.md
index c6cfc3229..f3cc9680b 100644
--- a/docs/README.skills.md
+++ b/docs/README.skills.md
@@ -283,3 +283,4 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-skills) for guidelines on how to
| [winui3-migration-guide](../skills/winui3-migration-guide/SKILL.md) | UWP-to-WinUI 3 migration reference. Maps legacy UWP APIs to correct Windows App SDK equivalents with before/after code snippets. Covers namespace changes, threading (CoreDispatcher to DispatcherQueue), windowing (CoreWindow to AppWindow), dialogs, pickers, sharing, printing, background tasks, and the most common Copilot code generation mistakes. | None |
| [workiq-copilot](../skills/workiq-copilot/SKILL.md) | Guides the Copilot CLI on how to use the WorkIQ CLI/MCP server to query Microsoft 365 Copilot data (emails, meetings, docs, Teams, people) for live context, summaries, and recommendations. | None |
| [write-coding-standards-from-file](../skills/write-coding-standards-from-file/SKILL.md) | Write a coding standards document for a project using the coding styles from the file(s) and/or folder(s) passed as arguments in the prompt. | None |
+| [x-twitter-scraper](../skills/x-twitter-scraper/SKILL.md) | Use when the user needs to interact with X (Twitter) — searching tweets, looking up users/followers, posting tweets/replies, liking, retweeting, following/unfollowing, sending DMs, downloading media, monitoring accounts in real time, or extracting bulk data. Provides 120 REST API endpoints, 2 MCP tools, and HMAC webhooks. Use even if the user says 'Twitter' instead of 'X', or asks about social media automation, tweet analytics, or follower analysis. | `references/api-endpoints.md`
`references/draws.md`
`references/extractions.md`
`references/mcp-setup.md`
`references/mcp-tools.md`
`references/pricing.md`
`references/python-examples.md`
`references/types.md`
`references/webhooks.md`
`references/workflows.md` |
diff --git a/skills/x-twitter-scraper/SKILL.md b/skills/x-twitter-scraper/SKILL.md
new file mode 100644
index 000000000..8678b9a0d
--- /dev/null
+++ b/skills/x-twitter-scraper/SKILL.md
@@ -0,0 +1,242 @@
+---
+name: x-twitter-scraper
+description: "Use when the user needs to interact with X (Twitter) — searching tweets, looking up users/followers, posting tweets/replies, liking, retweeting, following/unfollowing, sending DMs, downloading media, monitoring accounts in real time, or extracting bulk data. Provides 120 REST API endpoints, 2 MCP tools, and HMAC webhooks. Use even if the user says 'Twitter' instead of 'X', or asks about social media automation, tweet analytics, or follower analysis."
+compatibility: Requires internet access to call the Xquik REST API (https://xquik.com/api/v1)
+license: MIT
+metadata:
+ author: Xquik
+ version: "2.0.0"
+ openclaw:
+ requires:
+ env:
+ - XQUIK_API_KEY
+ primaryEnv: XQUIK_API_KEY
+ emoji: "𝕏"
+ homepage: https://docs.xquik.com
+---
+
+# Xquik API Integration
+
+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 Summary
+
+$20/month base plan. 1 credit = $0.00015. Read operations: 1-7 credits. Write operations: 2 credits. Extractions: 1-7 credits/result. Draws: 1 credit/participant. Monitors, webhooks, radar, compose, drafts, and support are free.
+
+Pay-per-use: credit top-ups via Stripe ($10 min) or MPP (USDC, 16 endpoints, no account needed).
+
+For full pricing breakdown, comparison vs official X API, and MPP endpoint list, see [references/pricing.md](references/pricing.md).
+
+## 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 (shown only once at creation).
+
+```javascript
+const headers = { "x-api-key": "xq_YOUR_KEY_HERE", "Content-Type": "application/json" };
+```
+
+## Error Handling
+
+All errors return `{ "error": "error_code" }`. Retry only `429` and `5xx` (max 3 retries, exponential backoff). Never retry other `4xx`.
+
+| Status | Codes | Action |
+|--------|-------|--------|
+| 400 | `invalid_input`, `invalid_id`, `invalid_params`, `missing_query` | Fix request |
+| 401 | `unauthenticated` | Check API key |
+| 402 | `no_subscription`, `insufficient_credits`, `usage_limit_reached` | Subscribe, top up, or enable extra usage |
+| 403 | `monitor_limit_reached`, `account_needs_reauth` | Delete resource or re-authenticate |
+| 404 | `not_found`, `user_not_found`, `tweet_not_found` | Resource doesn't exist |
+| 409 | `monitor_already_exists`, `conflict` | Already exists |
+| 422 | `login_failed` | Check X credentials |
+| 429 | `x_api_rate_limited` | Retry with backoff, respect `Retry-After` |
+| 5xx | `internal_error`, `x_api_unavailable` | Retry with backoff |
+
+If implementing retry logic or cursor pagination, read [references/workflows.md](references/workflows.md).
+
+## Extractions (23 Tools)
+
+Bulk data collection jobs. Always estimate first (`POST /extractions/estimate`), then create (`POST /extractions`), poll status, retrieve paginated results, optionally export (CSV/XLSX/MD, 50K row limit).
+
+If running an extraction, read [references/extractions.md](references/extractions.md) for tool types, required parameters, and filters.
+
+## Giveaway Draws
+
+Run auditable draws from tweet replies with filters (retweet required, follow check, min followers, account age, language, keywords, hashtags, mentions).
+
+`POST /draws` with `tweetUrl` (required) + optional filters. If creating a draw, read [references/draws.md](references/draws.md) for the full filter list and workflow.
+
+## Webhooks
+
+HMAC-SHA256 signed event delivery to your HTTPS endpoint. Event types: `tweet.new`, `tweet.quote`, `tweet.reply`, `tweet.retweet`, `follower.gained`, `follower.lost`. Retry policy: 5 attempts with exponential backoff.
+
+If building a webhook handler, read [references/webhooks.md](references/webhooks.md) for signature verification code (Node.js, Python, Go) and security checklist.
+
+## MCP Server (AI Agents)
+
+2 tools at `https://xquik.com/mcp` (StreamableHTTP). API key auth for CLI/IDE; OAuth 2.1 for web clients.
+
+| Tool | Description | Cost |
+|------|-------------|------|
+| `explore` | Search the API endpoint catalog (read-only) | Free |
+| `xquik` | Execute API calls (120 endpoints, 12 categories) | Varies |
+
+If configuring the MCP server in an IDE or agent platform, read [references/mcp-setup.md](references/mcp-setup.md). If calling MCP tools, read [references/mcp-tools.md](references/mcp-tools.md) for selection rules and common mistakes.
+
+## Gotchas
+
+- **Follow/DM endpoints need numeric user ID, not username.** Look up the user first via `GET /x/users/{username}`, then use the `id` field for follow/unfollow/DM calls.
+- **Extraction IDs are strings, not numbers.** Tweet IDs, user IDs, and extraction IDs are bigints that overflow JavaScript's `Number.MAX_SAFE_INTEGER`. Always treat them as strings.
+- **Always estimate before extracting.** `POST /extractions/estimate` checks whether the job would exceed your quota. Skipping this risks a 402 error mid-extraction.
+- **Webhook secrets are shown only once.** The `secret` field in the `POST /webhooks` response is never returned again. Store it immediately.
+- **402 means billing issue, not a bug.** `no_subscription`, `insufficient_credits`, `usage_limit_reached` — call `POST /subscribe` to get a Stripe checkout URL, or `POST /credits/topup` to add credits.
+- **`POST /compose` drafts tweets, `POST /x/tweets` sends them.** Don't confuse composition (AI-assisted writing) with posting (actually publishing to X).
+- **Cursors are opaque.** Never decode, parse, or construct `nextCursor` values — just pass them as the `after` query parameter.
+- **Rate limits are per method tier, not per endpoint.** Read (120/60s), Write (30/60s), Delete (15/60s). A burst of writes across different endpoints shares the same 30/60s window.
+
+## Conventions
+
+- **Timestamps are ISO 8601 UTC.** Example: `2026-02-24T10:30:00.000Z`
+- **Errors return JSON.** Format: `{ "error": "error_code" }`
+- **Export formats:** `csv`, `xlsx`, `md` via `/extractions/{id}/export` or `/draws/{id}/export`
+
+## Reference Files
+
+Load these on demand — only when the task requires it.
+
+| File | When to load |
+|------|-------------|
+| [references/api-endpoints.md](references/api-endpoints.md) | Need endpoint parameters, request/response shapes, or full API reference |
+| [references/pricing.md](references/pricing.md) | User asks about costs, pricing comparison, or MPP pay-per-use |
+| [references/workflows.md](references/workflows.md) | Implementing retry logic, cursor pagination, extraction workflow, or monitoring setup |
+| [references/draws.md](references/draws.md) | Creating a giveaway draw with filters |
+| [references/webhooks.md](references/webhooks.md) | Building a webhook handler or verifying signatures |
+| [references/extractions.md](references/extractions.md) | Running a bulk extraction (tool types, required params, filters) |
+| [references/mcp-setup.md](references/mcp-setup.md) | Configuring the MCP server in an IDE or agent platform |
+| [references/mcp-tools.md](references/mcp-tools.md) | Calling MCP tools (selection rules, workflow patterns, common mistakes) |
+| [references/python-examples.md](references/python-examples.md) | User is working in Python |
+| [references/types.md](references/types.md) | Need TypeScript type definitions for API objects |
diff --git a/skills/x-twitter-scraper/references/api-endpoints.md b/skills/x-twitter-scraper/references/api-endpoints.md
new file mode 100644
index 000000000..ca90a39ca
--- /dev/null
+++ b/skills/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/x-twitter-scraper/references/draws.md b/skills/x-twitter-scraper/references/draws.md new file mode 100644 index 000000000..1b1f1b536 --- /dev/null +++ b/skills/x-twitter-scraper/references/draws.md @@ -0,0 +1,57 @@ +# Xquik Giveaway Draws + +Run transparent, auditable giveaway draws from tweet replies with configurable filters. + +## Create Draw + +`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 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`; +``` + +## Cost + +1 credit ($0.00015) per participant entry. diff --git a/skills/x-twitter-scraper/references/extractions.md b/skills/x-twitter-scraper/references/extractions.md new file mode 100644 index 000000000..d92104075 --- /dev/null +++ b/skills/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/x-twitter-scraper/references/mcp-setup.md b/skills/x-twitter-scraper/references/mcp-setup.md new file mode 100644 index 000000000..8be00b9d0 --- /dev/null +++ b/skills/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/x-twitter-scraper/references/mcp-tools.md b/skills/x-twitter-scraper/references/mcp-tools.md new file mode 100644 index 000000000..646bd4620 --- /dev/null +++ b/skills/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