x-developer 2.6.1 → 2.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/CHANGELOG.md +24 -0
  5. package/README.md +34 -332
  6. package/commands/post.md +9 -3
  7. package/commands/trending.md +4 -2
  8. package/openclaw.plugin.json +1 -1
  9. package/package.json +1 -1
  10. package/scripts/check-versions.mjs +5 -2
  11. package/scripts/release-guard/content-policy.mjs +5 -3
  12. package/scripts/release-guard/policy.mjs +1 -1
  13. package/server.json +1 -1
  14. package/skills/x-twitter-scraper/SKILL.md +18 -5
  15. package/skills/x-twitter-scraper/metadata.json +1 -1
  16. package/skills/x-twitter-scraper/references/api-endpoints-api-keys.md +6 -23
  17. package/skills/x-twitter-scraper/references/api-endpoints-drafts.md +20 -1
  18. package/skills/x-twitter-scraper/references/api-endpoints-draws.md +26 -0
  19. package/skills/x-twitter-scraper/references/api-endpoints-error-codes.md +30 -1
  20. package/skills/x-twitter-scraper/references/api-endpoints-events.md +3 -1
  21. package/skills/x-twitter-scraper/references/api-endpoints-extractions.md +41 -26
  22. package/skills/x-twitter-scraper/references/api-endpoints-monitors.md +43 -6
  23. package/skills/x-twitter-scraper/references/api-endpoints-radar.md +2 -1
  24. package/skills/x-twitter-scraper/references/api-endpoints-support.md +37 -31
  25. package/skills/x-twitter-scraper/references/api-endpoints-trends.md +9 -1
  26. package/skills/x-twitter-scraper/references/api-endpoints-tweet-style-cache.md +32 -1
  27. package/skills/x-twitter-scraper/references/api-endpoints-webhooks.md +55 -9
  28. package/skills/x-twitter-scraper/references/api-endpoints-x-accounts.md +11 -0
  29. package/skills/x-twitter-scraper/references/api-endpoints-x-api.md +75 -14
  30. package/skills/x-twitter-scraper/references/api-endpoints-x-media.md +10 -0
  31. package/skills/x-twitter-scraper/references/api-endpoints-x-write.md +85 -19
  32. package/skills/x-twitter-scraper/references/extractions.md +74 -6
  33. package/skills/x-twitter-scraper/references/mcp-setup.md +7 -3
  34. package/skills/x-twitter-scraper/references/mcp-tools.md +15 -5
  35. package/skills/x-twitter-scraper/references/monitor-twitter-webhooks.md +12 -10
  36. package/skills/x-twitter-scraper/references/python-examples.md +39 -12
  37. package/skills/x-twitter-scraper/references/scrape-export-twitter-data.md +8 -4
  38. package/skills/x-twitter-scraper/references/track-twitter-keywords-mentions.md +5 -4
  39. package/skills/x-twitter-scraper/references/twitter-data-pipeline.md +1 -1
  40. package/skills/x-twitter-scraper/references/types-error.md +24 -3
  41. package/skills/x-twitter-scraper/references/types-events.md +5 -25
  42. package/skills/x-twitter-scraper/references/types-extractions.md +49 -2
  43. package/skills/x-twitter-scraper/references/types-mcp-output-schemas.md +10 -19
  44. package/skills/x-twitter-scraper/references/types-monitors.md +30 -1
  45. package/skills/x-twitter-scraper/references/types-radar.md +7 -0
  46. package/skills/x-twitter-scraper/references/types-rest-api-vs-mcp-field-naming.md +8 -12
  47. package/skills/x-twitter-scraper/references/types-support.md +31 -10
  48. package/skills/x-twitter-scraper/references/types-trends.md +3 -0
  49. package/skills/x-twitter-scraper/references/types-webhooks.md +10 -2
  50. package/skills/x-twitter-scraper/references/types-x-articles.md +27 -12
  51. package/skills/x-twitter-scraper/references/types-x-write.md +46 -7
  52. package/skills/x-twitter-scraper/references/types.md +0 -17
  53. package/skills/x-twitter-scraper/references/webhooks.md +123 -35
  54. package/skills/x-twitter-scraper/references/workflows.md +31 -9
  55. package/skills/x-twitter-scraper/skill-card.md +1 -1
  56. package/skills/xquik-social-research/SKILL.md +6 -0
  57. package/stub-server.mjs +1 -1
  58. package/task-guides/check-mutuals.md +1 -1
  59. package/task-guides/clean-followers.md +1 -1
  60. package/task-guides/export-tweets-csv.md +14 -5
  61. package/task-guides/extract-followers.md +2 -2
  62. package/task-guides/find-bangers.md +4 -2
  63. package/task-guides/find-influencers.md +7 -4
  64. package/task-guides/find-viral-tweets.md +1 -1
  65. package/task-guides/follow-unfollow.md +9 -4
  66. package/task-guides/for-you-feed.md +1 -1
  67. package/task-guides/going-viral.md +1 -1
  68. package/task-guides/grow-followers.md +4 -2
  69. package/task-guides/monitor-accounts.md +8 -9
  70. package/task-guides/optimize-tweets.md +1 -1
  71. package/task-guides/post-tweets.md +10 -6
  72. package/task-guides/run-giveaway.md +1 -1
  73. package/task-guides/search-tweets.md +19 -8
  74. package/task-guides/send-dms.md +16 -7
  75. package/task-guides/top-replies.md +1 -1
  76. package/task-guides/track-competitors.md +2 -2
  77. package/task-guides/track-hashtags.md +7 -8
  78. package/task-guides/track-mentions.md +10 -9
  79. package/task-guides/trending-news.md +13 -8
  80. package/task-guides/tweet-analytics.md +2 -2
  81. package/task-guides/tweet-ideas.md +1 -1
  82. package/task-guides/tweet-replies.md +29 -12
  83. package/task-guides/tweet-style.md +1 -1
  84. package/task-guides/tweet-webhooks.md +16 -12
  85. package/task-guides/update-x-profile.md +8 -5
  86. package/task-guides/user-tweets.md +20 -9
  87. package/task-guides/who-liked.md +1 -1
  88. package/task-guides/who-quoted.md +1 -1
  89. package/task-guides/who-retweeted.md +1 -1
  90. package/task-guides/write-threads.md +1 -1
  91. package/task-guides/write-tweets.md +1 -1
  92. package/task-guides/x-articles.md +10 -5
  93. package/task-guides/x-bookmarks.md +1 -1
  94. package/task-guides/x-communities.md +1 -1
  95. package/task-guides/x-lists.md +1 -1
  96. package/task-guides/x-spaces.md +1 -1
  97. package/task-guides/x-trends.md +6 -6
  98. package/skills/x-twitter-scraper/references/types-mcp-add-webhook.md +0 -14
  99. package/skills/x-twitter-scraper/references/types-mcp-check-follow.md +0 -10
  100. package/skills/x-twitter-scraper/references/types-mcp-compose-tweet.md +0 -31
  101. package/skills/x-twitter-scraper/references/types-mcp-estimate-extraction.md +0 -15
  102. package/skills/x-twitter-scraper/references/types-mcp-get-account.md +0 -26
  103. package/skills/x-twitter-scraper/references/types-mcp-get-draw.md +0 -30
  104. package/skills/x-twitter-scraper/references/types-mcp-get-events.md +0 -19
  105. package/skills/x-twitter-scraper/references/types-mcp-get-trends.md +0 -16
  106. package/skills/x-twitter-scraper/references/types-mcp-get-user-info.md +0 -16
  107. package/skills/x-twitter-scraper/references/types-mcp-list-monitors.md +0 -15
  108. package/skills/x-twitter-scraper/references/types-mcp-lookup-tweet.md +0 -25
  109. package/skills/x-twitter-scraper/references/types-mcp-refine-tweet.md +0 -13
  110. package/skills/x-twitter-scraper/references/types-mcp-run-draw.md +0 -18
  111. package/skills/x-twitter-scraper/references/types-mcp-run-extraction.md +0 -12
  112. package/skills/x-twitter-scraper/references/types-mcp-score-tweet.md +0 -16
  113. package/skills/x-twitter-scraper/references/types-mcp-search-tweets.md +0 -17
  114. package/skills/x-twitter-scraper/references/types-mcp-test-webhook.md +0 -11
@@ -2,6 +2,23 @@
2
2
 
3
3
  23 bulk data extraction tools. Each requires a specific target parameter.
4
4
 
5
+ ## Privacy and Acceptable Use
6
+
7
+ Bulk extraction and export can collect large amounts of public identity,
8
+ activity, and relationship data. Before creating a job, confirm the lawful
9
+ purpose, target, `resultsLimit`, intended recipients, and retention period.
10
+ Follow X rules and applicable privacy law. Do not use these tools for
11
+ credential collection, private data, surveillance, discrimination, harassment,
12
+ doxxing, or unrelated secondary use. Delete exported data when the approved
13
+ purpose ends.
14
+
15
+ Every extraction requires an estimate and explicit approval for the exact
16
+ bounded job. Never infer approval from a general request or increase a bound
17
+ without renewed approval.
18
+
19
+ The API accepts an omitted `resultsLimit`. This Skill must always send an
20
+ explicit finite positive bound. Use the same bound for estimate and create.
21
+
5
22
  **Endpoint:** `POST /extractions`
6
23
 
7
24
  **Always estimate first:** `POST /extractions/estimate` with the same body to preview `creditsRequired`, `creditsAvailable`, and whether the job is allowed.
@@ -55,7 +72,7 @@
55
72
 
56
73
  The `@` prefix is automatically stripped if included.
57
74
 
58
- ### User-Based by ID (require `targetUserId`)
75
+ ### User Timeline Tools (require `targetUsername`)
59
76
 
60
77
  | Tool Type | Description |
61
78
  |-----------|-------------|
@@ -66,7 +83,7 @@ The `@` prefix is automatically stripped if included.
66
83
  ```json
67
84
  {
68
85
  "toolType": "user_likes",
69
- "targetUserId": "44196397"
86
+ "targetUsername": "elonmusk"
70
87
  }
71
88
  ```
72
89
 
@@ -122,7 +139,7 @@ The `@` prefix is automatically stripped if included.
122
139
  | Tool Type | Description |
123
140
  |-----------|-------------|
124
141
  | `people_search` | Search for users by keyword |
125
- | `tweet_search_extractor` | Search and extract tweets by keyword or hashtag (bulk, up to 1,000) |
142
+ | `tweet_search_extractor` | Search and extract tweets by keyword or hashtag |
126
143
 
127
144
  **Example (people search):**
128
145
  ```json
@@ -143,7 +160,8 @@ The `@` prefix is automatically stripped if included.
143
160
 
144
161
  ### Tweet Search Filters
145
162
 
146
- The `tweet_search_extractor` tool type supports 16 additional filter fields that are converted to X search operators and combined with `searchQuery`:
163
+ `tweet_search_extractor` accepts structured filters. It combines them with
164
+ `searchQuery` before collection.
147
165
 
148
166
  | Field | Type | Description |
149
167
  |-------|------|-------------|
@@ -153,15 +171,49 @@ The `tweet_search_extractor` tool type supports 16 additional filter fields that
153
171
  | `language` | string | Language code (e.g., `en`) |
154
172
  | `sinceDate` | string | Start date (YYYY-MM-DD) |
155
173
  | `untilDate` | string | End date (YYYY-MM-DD) |
156
- | `mediaType` | string | `images`, `videos`, `gifs`, or `media` |
174
+ | `mediaType` | string | `images`, `videos`, `gifs`, `media`, `links`, or `none` |
157
175
  | `minFaves` | number | Minimum likes |
158
176
  | `minRetweets` | number | Minimum retweets |
159
177
  | `minReplies` | number | Minimum replies |
178
+ | `minQuotes` | number | Minimum quote count |
179
+ | `minViews` | number | Minimum view count |
180
+ | `minBookmarks` | number | Minimum bookmark count |
181
+ | `maxLikes` | number | Maximum likes |
182
+ | `maxRetweets` | number | Maximum reposts |
183
+ | `maxReplies` | number | Maximum replies |
184
+ | `maxQuotes` | number | Maximum quotes |
185
+ | `blueVerifiedOnly` | boolean | Blue-verified authors only |
186
+ | `cardName` | string | Match the Tweet card name |
187
+ | `source` | string | Match the source application |
188
+ | `excludeSource` | string | Exclude a source application |
189
+ | `geocode` | string | Match latitude, longitude, and radius |
190
+ | `sinceId` | string | Tweets newer than this ID |
191
+ | `maxId` | string | Tweets older than this ID |
192
+ | `near` | string | Match a place name |
193
+ | `within` | string | Radius for the `near` filter |
194
+ | `withinTime` | string | Recent time window |
195
+ | `nativeRetweets` | boolean | Native reposts only |
196
+ | `safe` | boolean | Enable safe search |
197
+ | `news` | boolean | News results only |
160
198
  | `verifiedOnly` | boolean | Verified authors only |
161
199
  | `replies` | string | `include`, `exclude`, or `only` |
162
200
  | `retweets` | string | `include`, `exclude`, or `only` |
201
+ | `quotes` | string | `include`, `exclude`, or `only` |
163
202
  | `exactPhrase` | string | Exact match text |
164
203
  | `excludeWords` | string | Comma-separated words to exclude |
204
+ | `anyWords` | string | Terms where any one can match |
205
+ | `hashtags` | string | Hashtags separated by spaces, commas, or lines |
206
+ | `cashtags` | string | Cashtags separated by spaces, commas, or lines |
207
+ | `url` | string | URL substring or domain |
208
+ | `conversationId` | string | Conversation ID |
209
+ | `inReplyToTweetId` | string | Replies to one Tweet ID |
210
+ | `quotesOfTweetId` | string | Quotes of one Tweet ID |
211
+ | `retweetsOfTweetId` | string | Reposts of one Tweet ID |
212
+ | `listId` | string | Search within a list |
213
+ | `place` | string | Search within a place ID |
214
+ | `placeCountry` | string | Search within a country code |
215
+ | `pointRadius` | string | Geographic point and radius |
216
+ | `boundingBox` | string | Geographic bounding box |
165
217
  | `advancedQuery` | string | Raw X search operators appended to query |
166
218
 
167
219
  **Example with filters:**
@@ -179,6 +231,13 @@ The `tweet_search_extractor` tool type supports 16 additional filter fields that
179
231
 
180
232
  `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.
181
233
 
234
+ ### Profile Filters
235
+
236
+ Profile-producing extractions also accept `minFollowers`, `maxFollowers`,
237
+ `minFollowing`, `maxFollowing`, `minPosts`, `maxPosts`,
238
+ `minAccountAgeDays`, `verifiedType`, `hasWebsite`, `hasLocation`,
239
+ `bioContains`, `locationContains`, and `usernameContains`.
240
+
182
241
  ## Response
183
242
 
184
243
  ```json
@@ -214,6 +273,14 @@ Formats: `csv`, `json`, `md`, `md-document`, `pdf`, `txt`, `xlsx`. 100,000 row l
214
273
 
215
274
  Exports include enrichment columns not present in the API response.
216
275
 
276
+ The endpoint supports follower, following, post, engagement, profile, media,
277
+ language, search, and date filters. It does not project individual fields.
278
+
279
+ **Approval required:** Set the smallest approved `resultsLimit` when creating
280
+ the job. Before export, show the job, filters, format, row count, schema,
281
+ recipients, storage, and retention. Materialize or transmit the dataset only
282
+ after explicit approval. Delete it when the approved purpose ends.
283
+
217
284
  ## Estimating Usage
218
285
 
219
286
  ```
@@ -232,6 +299,7 @@ Same body as create. Response:
232
299
  }
233
300
  ```
234
301
 
235
- If `allowed` is `false`, the extraction requires more credits than are currently available.
302
+ If `allowed` is `false`, do not create the extraction. It requires more credits
303
+ than are currently available.
236
304
 
237
305
  For common mistakes and tool selection rules, see [mcp-tools.md](mcp-tools.md#common-mistakes).
@@ -10,7 +10,8 @@ API keys.
10
10
  | Protocol | Streamable HTTP |
11
11
  | Endpoint | `https://xquik.com/mcp` |
12
12
  | Authentication | OAuth 2.1 discovery; API key fallback |
13
- | Version | `2.6.1` |
13
+ | Hosted MCP version | `2.6.0` |
14
+ | Skill bundle version | `2.6.3` |
14
15
 
15
16
  Current clients negotiate MCP `2026-07-28` through `server/discover`.
16
17
  Use a current MCP SDK. It adds request `_meta` and protocol headers.
@@ -287,7 +288,8 @@ Active guest `paid_reads` keys expose 33 eligible GET routes.
287
288
 
288
289
  ## MCP Server Architecture
289
290
 
290
- The MCP server (v2.6.1) exposes 120 catalog routes through 2 structured API tools. Of these, 119 support JSON or text. Binary support downloads use REST.
291
+ Hosted MCP v2.6.0 exposes 120 catalog routes through 2 structured API tools.
292
+ Of these, 119 support JSON or text. Binary support downloads use REST.
291
293
 
292
294
  | Tool | Description | Usage |
293
295
  |------|-------------|------|
@@ -298,7 +300,7 @@ The MCP server (v2.6.1) exposes 120 catalog routes through 2 structured API tool
298
300
  operations with normalized snake_case responses. Authentication is injected, so
299
301
  tool code must never include credentials.
300
302
 
301
- MCP v2.6.1 catalogs 120 of 128 documented REST operations. These 8 credential,
303
+ Hosted MCP v2.6.0 catalogs 120 of 128 documented REST operations. These 8 credential,
302
304
  checkout, or guest-wallet operations remain direct REST or dashboard workflows:
303
305
 
304
306
  - API key creation, listing, and revocation
@@ -327,6 +329,8 @@ Handle failures from structured error fields:
327
329
 
328
330
  - `401`: reconnect OAuth or replace the revoked API key.
329
331
  - `402`: report payment options. Never create checkout without confirmation.
332
+ - `409 coverage_cursor_unavailable`: wait the exact `Retry-After` seconds, then retry the same cursor once.
333
+ - `410 coverage_cursor_gone`: no `Retry-After`; restart without a cursor and deduplicate by ID.
330
334
  - `429`: honor `Retry-After`.
331
335
  - `5xx`: retry read-only requests with bounded exponential backoff.
332
336
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  The MCP server at `https://xquik.com/mcp` provides 2 structured API tools. The agent sends API requests through the server, which handles authentication and execution for `xquik.com/api/v1`.
4
4
 
5
- MCP v2.6.1 supports `2026-07-28` through `server/discover`.
5
+ Hosted MCP v2.6.0 supports `2026-07-28` through `server/discover`.
6
6
  Current MCP SDKs add request metadata and headers automatically.
7
7
  Modern calls need no initialization session.
8
8
 
@@ -35,6 +35,7 @@ interface EndpointInfo {
35
35
  category: string; // account, composition, credits, extraction, media, monitoring, support, twitter, x-accounts, x-write
36
36
  free: boolean; // Included usage flag from endpoint metadata
37
37
  parameters?: Array<{ name: string; in: 'query' | 'path' | 'body'; required: boolean; type: string; description: string }>;
38
+ injectedHeaders?: string[];
38
39
  responseShape?: string;
39
40
  }
40
41
 
@@ -56,7 +57,11 @@ async () => spec.endpoints.filter(e => e.summary.toLowerCase().includes('tweet')
56
57
 
57
58
  ### `xquik` - Send API Requests
58
59
 
59
- The tool provides `xquik.request()` with auth injected automatically. Never pass API keys.
60
+ The tool provides `xquik.request()` with authentication and required idempotency headers injected automatically. Never pass API keys or headers. The sandbox reuses each generated key for bounded transient retries. After an unresolved write failure, verify state. Start a new attempt only when `safe_to_retry` is true and the user approves.
61
+
62
+ For `409 coverage_cursor_unavailable`, wait the exact `Retry-After` seconds and
63
+ retry the same cursor once. For `410 coverage_cursor_gone`, the response omits
64
+ `Retry-After`. Restart without a cursor and deduplicate by ID.
60
65
 
61
66
  ## Safety Gates
62
67
 
@@ -128,11 +133,16 @@ Use `explore` first to find endpoints, then `xquik` to call them.
128
133
  | Get bookmark folders | `GET /api/v1/x/bookmarks/folders` |
129
134
  | Get notifications | `GET /api/v1/x/notifications` (private; confirmation required) |
130
135
  | Get home timeline | `GET /api/v1/x/timeline` (private; confirmation required) |
131
- | Get DM history | `GET /api/v1/x/dm/{userId}/history` (private; confirmation required) |
136
+ | Get DM history | `GET /api/v1/x/dm/{userId}/history?account={username}` (private; exact-account approval required; block ambiguous selection) |
132
137
  | Check credit balance | `GET /api/v1/credits` |
133
138
 
134
139
  Use `POST /api/v1/extractions` ONLY for bulk data that simpler endpoints cannot provide (all followers, all replies to a tweet, community members, etc.). Always call `POST /api/v1/extractions/estimate` first.
135
140
 
141
+ Fresh cursorless Tweet Search with `queryType=Latest` is newest-first across
142
+ pages. Existing cursors retain their ordering. Thread reads accept 32 effective
143
+ result filters. They exclude `nativeRetweets`, `sinceTime`, and `untilTime`.
144
+ See [direct lookups](api-endpoints-x-api.md) for the exact names.
145
+
136
146
  ## Workflow Patterns
137
147
 
138
148
  | Workflow | Steps |
@@ -142,7 +152,7 @@ Use `POST /api/v1/extractions` ONLY for bulk data that simpler endpoints cannot
142
152
  | **Bulk extraction** | `POST /extractions/estimate` -> `POST /extractions` -> `GET /extractions/{id}` |
143
153
  | **Compose optimized tweet** | `POST /compose` (step=compose -> refine -> score) |
144
154
  | **Analyze tweet style** | `POST /styles` -> `GET /styles/{id}` -> `POST /compose` with `styleUsername` |
145
- | **Post a tweet** | `GET /x/accounts` -> `POST /x/tweets` with `account` + `text` |
155
+ | **Post a tweet** | `GET /x/accounts` -> confirm -> `POST /x/tweets` with `account` and `text` -> hosted MCP injects a unique `Idempotency-Key` -> poll `statusUrl` |
146
156
  | **Get trending news** | `GET /radar` (supported sources, via `xquik` tool) -> `POST /compose` with trending topic |
147
157
  | **Open support ticket** | `POST /support/tickets` -> `GET /support/tickets/{id}` |
148
158
  | **Collect maximum-coverage replies** | `GET /x/tweets/{id}/replies?mode=complete&limit=<1-25000>` -> filter direct rows by `inReplyToId` -> keep `nested_replies` separate -> inspect `diagnostic` |
@@ -164,7 +174,7 @@ Use `POST /api/v1/extractions` ONLY for bulk data that simpler endpoints cannot
164
174
 
165
175
  ## REST-only operations
166
176
 
167
- MCP v2.6.1 catalogs 120 of 128 REST operations.
177
+ Hosted MCP v2.6.0 catalogs 120 of 128 REST operations.
168
178
  Of these, 119 support JSON or text. Binary support downloads use REST.
169
179
  These 8 credential, checkout, or guest-wallet operations remain outside MCP:
170
180
 
@@ -11,9 +11,10 @@ them to an HTTPS endpoint through HMAC-signed webhooks.
11
11
 
12
12
  | Object | Fields to Preserve |
13
13
  | --- | --- |
14
- | Monitor | Monitor ID, target, query, filters, event types, status |
15
- | Event | Event ID, monitor ID, event type, source tweet ID, source time |
16
- | Delivery | Webhook ID, attempt, delivery time, status, processing state |
14
+ | Account monitor | Monitor ID, username, event types, active state |
15
+ | Keyword monitor | Monitor ID, query, event types, active state |
16
+ | Event | Event ID, monitor ID and type, event type, occurrence time, data |
17
+ | Delivery | Delivery ID, stream event ID, attempts, status, delivery time |
17
18
 
18
19
  ## Twitter Monitor Polling Versus Webhook Delivery
19
20
 
@@ -21,7 +22,7 @@ them to an HTTPS endpoint through HMAC-signed webhooks.
21
22
  | --- | --- | --- |
22
23
  | Simple scheduled batch | Strong fit | Optional |
23
24
  | Low detection delay | More frequent polling needed | Strong fit |
24
- | Replay after downtime | Use stored cursor and event IDs | Use delivery log and event IDs |
25
+ | Recover after downtime | Resume with the stored cursor | Inspect delivery status, then repoll events |
25
26
  | Public HTTPS endpoint | Not required | Required |
26
27
  | Signature verification | Not applicable | Required |
27
28
  | Backpressure control | Caller controls fetch rate | Receiver must queue work |
@@ -54,8 +55,9 @@ Webhook alerts are HTTPS POST requests sent when a monitor creates a matching
54
55
  event. Xquik signs each delivery with a per-webhook HMAC secret. The secret is
55
56
  shown only once and should enter a secret store immediately.
56
57
 
57
- Verify the signature against the raw request body before parsing business data.
58
- Reject invalid signatures, return success quickly, and queue slower work.
58
+ Verify `<timestamp>.<nonce>.<raw body>` with `X-Xquik-Timestamp`,
59
+ `X-Xquik-Nonce`, and `X-Xquik-Signature`. Reject timestamps outside 5 minutes
60
+ and reused nonces. Reject invalid signatures before parsing business data.
59
61
 
60
62
  ### What is a Twitter account monitor API?
61
63
 
@@ -94,7 +96,7 @@ from production analytics.
94
96
  1. Read the raw request body.
95
97
  2. Compute and compare the expected HMAC signature safely.
96
98
  3. Reject invalid or missing signatures.
97
- 4. Deduplicate by event or delivery ID.
99
+ 4. Deduplicate webhook attempts by `deliveryId`.
98
100
  5. Acknowledge valid delivery quickly.
99
101
  6. Process asynchronously with bounded retries.
100
102
  7. Record attempt, status, and failure reason.
@@ -107,9 +109,9 @@ plan changes, credit changes, or tool changes.
107
109
 
108
110
  1. Pause downstream actions when signature checks fail.
109
111
  2. Check monitor status and delivery history.
110
- 3. Restore the receiver before replaying events.
111
- 4. Replay by stable event ID where supported.
112
- 5. Deduplicate before applying business changes.
112
+ 3. Restore the receiver before resuming delivery.
113
+ 4. Repoll events from the last stored cursor when recovery needs them.
114
+ 5. Deduplicate event IDs and webhook `deliveryId` values.
113
115
  6. Compare source and stored timestamps.
114
116
  7. Document gaps and permanent failures.
115
117
 
@@ -12,6 +12,12 @@ Python equivalents of the JavaScript examples in SKILL.md.
12
12
 
13
13
  ## Authentication
14
14
 
15
+ > **External transmission:** These examples send credentials, parameters, and
16
+ > returned data to and from `xquik.com`. Keep the key in a secret store. Get
17
+ > explicit approval before private reads, writes, exports, persistent resources,
18
+ > webhooks, or metered jobs. Never forward private results without separate
19
+ > approval.
20
+
15
21
  ```python
16
22
  import json
17
23
  import urllib.error
@@ -33,6 +39,8 @@ import time, random
33
39
 
34
40
  def xquik_fetch(path, method="GET", json_body=None, max_retries=3):
35
41
  base_delay = 1.0
42
+ method = method.upper()
43
+ retry_safe = method in {"GET", "HEAD", "OPTIONS"}
36
44
 
37
45
  for attempt in range(max_retries + 1):
38
46
  retry_after = None
@@ -49,7 +57,7 @@ def xquik_fetch(path, method="GET", json_body=None, max_retries=3):
49
57
  payload = json.loads(error.read() or b"{}")
50
58
  retry_after = error.headers.get("Retry-After")
51
59
 
52
- retryable = status == 429 or status >= 500
60
+ retryable = retry_safe and (status == 429 or status >= 500)
53
61
  if not retryable or attempt == max_retries:
54
62
  raise Exception(f"Xquik API {status}: {payload.get('error', 'request failed')}")
55
63
 
@@ -60,10 +68,18 @@ def xquik_fetch(path, method="GET", json_body=None, max_retries=3):
60
68
  ## Extraction Workflow
61
69
 
62
70
  ```python
71
+ RESULTS_LIMIT = 1000
72
+
73
+ def require_explicit_approval(scope: str) -> None:
74
+ raise RuntimeError(
75
+ f"Approval required for {scope}. Implement the approval gate first."
76
+ )
77
+
63
78
  # Step 1: Estimate
64
79
  estimate = xquik_fetch("/extractions/estimate", method="POST", json_body={
65
80
  "toolType": "reply_extractor",
66
81
  "targetTweetId": "1893704267862470862",
82
+ "resultsLimit": RESULTS_LIMIT,
67
83
  })
68
84
 
69
85
  if not estimate["allowed"]:
@@ -71,9 +87,13 @@ if not estimate["allowed"]:
71
87
  exit()
72
88
 
73
89
  # Step 2: Create job
90
+ require_explicit_approval(
91
+ "the bounded extraction job, usage, recipients, and retention"
92
+ )
74
93
  job = xquik_fetch("/extractions", method="POST", json_body={
75
94
  "toolType": "reply_extractor",
76
95
  "targetTweetId": "1893704267862470862",
96
+ "resultsLimit": RESULTS_LIMIT,
77
97
  })
78
98
 
79
99
  # Step 3: Poll until complete (large jobs may return "running")
@@ -88,7 +108,7 @@ results = []
88
108
  while True:
89
109
  path = f"/extractions/{job['id']}"
90
110
  if cursor:
91
- path += f"?after={cursor}"
111
+ path += f"?cursor={cursor}"
92
112
  page = xquik_fetch(path)
93
113
  results.extend(page["results"])
94
114
 
@@ -128,6 +148,8 @@ for winner in details["winners"]:
128
148
  import hashlib
129
149
  import hmac
130
150
  import json
151
+ import re
152
+ import time
131
153
  from http.server import BaseHTTPRequestHandler, HTTPServer
132
154
 
133
155
  def load_secret(name: str) -> str:
@@ -136,10 +158,15 @@ def load_secret(name: str) -> str:
136
158
 
137
159
  # Per-webhook secret from POST /webhooks response, not a Xquik account credential
138
160
  WEBHOOK_SECRET = load_secret("XQUIK_WEBHOOK_SECRET")
139
- processed_hashes = set() # Use Redis/DB in production
140
-
141
- def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
142
- expected = "sha256=" + hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
161
+ processed_delivery_ids = set() # Use durable storage in production
162
+
163
+ def verify_signature(payload: bytes, signature: str, timestamp: str, nonce: str, secret: str) -> bool:
164
+ if not timestamp.isdigit() or not re.fullmatch(r"[0-9a-f]{32}", nonce):
165
+ return False
166
+ if abs(int(time.time() * 1000) - int(timestamp)) > 5 * 60 * 1000:
167
+ return False
168
+ signing_input = timestamp.encode() + b"." + nonce.encode() + b"." + payload
169
+ expected = "sha256=" + hmac.new(secret.encode(), signing_input, hashlib.sha256).hexdigest()
143
170
  return hmac.compare_digest(expected, signature)
144
171
 
145
172
  EVENT_HANDLERS = {
@@ -153,23 +180,23 @@ class WebhookHandler(BaseHTTPRequestHandler):
153
180
  def do_POST(self):
154
181
  length = int(self.headers.get("Content-Length", "0"))
155
182
  signature = self.headers.get("X-Xquik-Signature", "")
183
+ timestamp = self.headers.get("X-Xquik-Timestamp", "")
184
+ nonce = self.headers.get("X-Xquik-Nonce", "")
156
185
  payload = self.rfile.read(length)
157
186
 
158
- if not verify_signature(payload, signature, WEBHOOK_SECRET):
187
+ if not verify_signature(payload, signature, timestamp, nonce, WEBHOOK_SECRET):
159
188
  self.send_response(401)
160
189
  self.end_headers()
161
190
  self.wfile.write(b"Invalid signature")
162
191
  return
163
192
 
164
- payload_hash = hashlib.sha256(payload).hexdigest()
165
- if payload_hash in processed_hashes:
193
+ event = json.loads(payload)
194
+ if event["deliveryId"] in processed_delivery_ids:
166
195
  self.send_response(200)
167
196
  self.end_headers()
168
197
  self.wfile.write(b"Already processed")
169
198
  return
170
- processed_hashes.add(payload_hash)
171
-
172
- event = json.loads(payload)
199
+ processed_delivery_ids.add(event["deliveryId"])
173
200
  handler = EVENT_HANDLERS.get(event["eventType"])
174
201
  if handler:
175
202
  handler(event["username"], event["data"])
@@ -36,6 +36,8 @@ structured tweet, author, timestamp, engagement, and media fields when present.
36
36
 
37
37
  Version every advanced search query. Store the exact filters beside the output.
38
38
  Changing a date, author, language, or exclusion changes the dataset definition.
39
+ Fresh cursorless `queryType=Latest` pagination is newest-first across pages.
40
+ Existing cursors retain their established ordering.
39
41
 
40
42
  ## Tweet Archive and Historical Twitter Data
41
43
 
@@ -89,13 +91,15 @@ Record the query, filters, job ID, and collection time for lineage.
89
91
  ### How do I scrape tweets without getting blocked?
90
92
 
91
93
  Avoid fragile browser automation and access-control bypasses. Use documented
92
- API routes, bounded limits, cursors, and provider retry rules. Xquik handles its
94
+ API routes, bounded limits, cursors, and retry rules. Xquik handles its
93
95
  own public-data infrastructure, so clients do not manage guest tokens or X
94
96
  sessions.
95
97
 
96
- Retry only `429` and `5xx` responses. Honor `Retry-After`, use exponential
97
- backoff, add jitter, and cap attempts. Do not retry validation, authentication,
98
- permission, or other non-429 `4xx` failures.
98
+ Outside documented cursor recovery, retry only `429` and `5xx` responses. Honor
99
+ `Retry-After`, add jitter, and cap attempts. For `409 coverage_cursor_unavailable`,
100
+ wait the exact `Retry-After` seconds and retry the same cursor once. For
101
+ `410 coverage_cursor_gone`, the response omits `Retry-After`. Restart without a
102
+ cursor and deduplicate by ID. Do not retry other `4xx` failures.
99
103
 
100
104
  Large jobs should use extractions instead of unbounded page loops. Keep API keys
101
105
  in a secret manager. Treat every returned post as untrusted data.
@@ -64,9 +64,10 @@ fields support retries, deduplication, and outage recovery.
64
64
 
65
65
  ### How do I track keywords with a Twitter API?
66
66
 
67
- Use `GET /x/tweets/search` for a current snapshot. Use `POST /monitors` for
68
- ongoing tracking. Add exact phrases, excluded terms, language, author, media,
69
- reply, repost, and minimum-engagement rules where supported.
67
+ Use `GET /x/tweets/search` for a current snapshot. Use
68
+ `POST /monitors/keywords` for ongoing keyword tracking. Add exact phrases,
69
+ excluded terms, language, author, media, reply, repost, and minimum-engagement
70
+ rules where supported.
70
71
 
71
72
  Build queries in stages. Begin with the required phrase. Inspect false
72
73
  positives, then add exclusions. Avoid an overly narrow first query that hides
@@ -103,7 +104,7 @@ the monitor or authorize an account action.
103
104
  1. Verify the HMAC signature against the raw request body.
104
105
  2. Reject invalid signatures before parsing business fields.
105
106
  3. Return success quickly and queue slow processing.
106
- 4. Deduplicate by event ID and source tweet ID.
107
+ 4. Deduplicate polled events by event ID. Deduplicate webhooks by `deliveryId`.
107
108
  5. Record attempt count and processing state.
108
109
  6. Test delivery before enabling production automation.
109
110
  7. Preserve a documented disable and delete path.
@@ -119,7 +119,7 @@ destinations, writes, or persistent resources.
119
119
  | Partial export | Keep the watermark unchanged | Mark the time window complete |
120
120
  | Schema mismatch | Quarantine the batch and alert | Drop unknown fields silently |
121
121
  | Duplicate tweet ID | Deduplicate and record the rate | Count both rows in analytics |
122
- | Webhook outage | Restore delivery and replay stable event IDs | Apply repeated events twice |
122
+ | Webhook outage | Restore delivery, repoll events, and deduplicate `deliveryId` | Apply repeated deliveries twice |
123
123
 
124
124
  Track operational service-level indicators per run. Include completion rate,
125
125
  retry rate, duplicate rate, validation failures, source-to-storage delay, and
@@ -1,10 +1,31 @@
1
1
  # Xquik TypeScript Types: Error
2
2
 
3
3
  ```typescript
4
+ type ApiErrorCode = string;
5
+
6
+ type ApiErrorType =
7
+ | "api_error"
8
+ | "authentication_error"
9
+ | "billing_error"
10
+ | "dependency_error"
11
+ | "invalid_request_error"
12
+ | "permission_error"
13
+ | "rate_limit_error";
4
14
 
5
15
  interface ApiError {
6
- error: string;
7
- limit?: number;
16
+ error:
17
+ | ApiErrorCode
18
+ | { message: string; type: ApiErrorType; code: ApiErrorCode };
19
+ message?: string;
20
+ reason?: string;
21
+ retryAfter?: number;
22
+ retryAfterMs?: number;
8
23
  }
9
-
10
24
  ```
25
+
26
+ OpenAPI enumerates all 109 `ApiErrorCode` values, including
27
+ `user_not_found`. Generated SDKs expose that exhaustive enum. The handwritten
28
+ type remains a string for forward compatibility.
29
+
30
+ Default v1 errors use the string form. Send
31
+ `xquik-api-contract: 2026-04-29` to receive the structured form.
@@ -6,34 +6,14 @@ interface XquikEvent {
6
6
  id: string;
7
7
  type: EventType;
8
8
  monitorId: string;
9
- username: string;
9
+ monitorType: "account" | "keyword";
10
+ username?: string;
11
+ query?: string;
12
+ keywordMonitorId?: string;
10
13
  occurredAt: string;
11
- data: EventData;
12
- xEventId?: string;
14
+ data: Record<string, unknown>;
13
15
  }
14
16
 
15
- // Tweet events (tweet.new, tweet.reply, tweet.quote, tweet.retweet)
16
- interface TweetEventData {
17
- tweetId: string;
18
- text: string;
19
- metrics: {
20
- likes: number;
21
- retweets: number;
22
- replies: number;
23
- };
24
- // tweet.quote only
25
- quotedTweetId?: string;
26
- quotedUsername?: string;
27
- // tweet.reply only
28
- inReplyToTweetId?: string;
29
- inReplyToUsername?: string;
30
- // tweet.retweet only
31
- retweetedTweetId?: string;
32
- retweetedUsername?: string;
33
- }
34
-
35
- type EventData = TweetEventData;
36
-
37
17
  interface EventList {
38
18
  events: XquikEvent[];
39
19
  hasMore: boolean;
@@ -34,7 +34,6 @@ interface ExtractionJob {
34
34
  totalResults: number;
35
35
  targetTweetId?: string;
36
36
  targetUsername?: string;
37
- targetUserId?: string;
38
37
  targetCommunityId?: string;
39
38
  targetListId?: string;
40
39
  targetSpaceId?: string;
@@ -91,16 +90,64 @@ interface CreateExtractionRequest {
91
90
  language?: string;
92
91
  sinceDate?: string; // YYYY-MM-DD
93
92
  untilDate?: string; // YYYY-MM-DD
94
- mediaType?: 'images' | 'videos' | 'gifs' | 'media';
93
+ mediaType?: 'images' | 'videos' | 'gifs' | 'media' | 'links' | 'none';
95
94
  minFaves?: number;
96
95
  minRetweets?: number;
97
96
  minReplies?: number;
97
+ minQuotes?: number;
98
+ minViews?: number;
99
+ minBookmarks?: number;
100
+ maxLikes?: number;
101
+ maxRetweets?: number;
102
+ maxReplies?: number;
103
+ maxQuotes?: number;
104
+ blueVerifiedOnly?: boolean;
105
+ cardName?: string;
106
+ source?: string;
107
+ excludeSource?: string;
108
+ geocode?: string;
109
+ sinceId?: string;
110
+ maxId?: string;
111
+ near?: string;
112
+ within?: string;
113
+ withinTime?: string;
114
+ nativeRetweets?: boolean;
115
+ safe?: boolean;
116
+ news?: boolean;
98
117
  verifiedOnly?: boolean;
99
118
  replies?: 'include' | 'exclude' | 'only';
100
119
  retweets?: 'include' | 'exclude' | 'only';
120
+ quotes?: 'include' | 'exclude' | 'only';
101
121
  exactPhrase?: string;
102
122
  excludeWords?: string;
123
+ anyWords?: string;
124
+ hashtags?: string;
125
+ cashtags?: string;
126
+ url?: string;
127
+ conversationId?: string;
128
+ inReplyToTweetId?: string;
129
+ quotesOfTweetId?: string;
130
+ retweetsOfTweetId?: string;
131
+ listId?: string;
132
+ place?: string;
133
+ placeCountry?: string;
134
+ pointRadius?: string;
135
+ boundingBox?: string;
103
136
  advancedQuery?: string;
137
+ // Profile filters for profile-producing extractions
138
+ minFollowers?: number;
139
+ maxFollowers?: number;
140
+ minFollowing?: number;
141
+ maxFollowing?: number;
142
+ minPosts?: number;
143
+ maxPosts?: number;
144
+ minAccountAgeDays?: number;
145
+ verifiedType?: string;
146
+ hasWebsite?: boolean;
147
+ hasLocation?: boolean;
148
+ bioContains?: string;
149
+ locationContains?: string;
150
+ usernameContains?: string;
104
151
  }
105
152
 
106
153
  ```