x-developer
Version:
X (Twitter) data platform skill for AI coding agents. 100+ REST API endpoints, 2 MCP tools, 23 extraction types, HMAC webhooks.
193 lines (156 loc) • 6.93 kB
Markdown
# Xquik Workflow Examples
Code examples for common integration patterns.
## Authentication
```javascript
const API_KEY = "xq_YOUR_KEY_HERE";
const BASE = "https://xquik.com/api/v1";
const headers = { "x-api-key": API_KEY, "Content-Type": "application/json" };
```
## Retry with Exponential Backoff
Retry only `429` and `5xx`. Never retry `4xx` (except 429). Max 3 retries:
```javascript
async function xquikFetch(path, options = {}) {
const baseDelay = 1000;
for (let attempt = 0; attempt <= 3; attempt++) {
const response = await fetch(`${BASE}${path}`, {
...options,
headers: { ...headers, ...options.headers },
});
if (response.ok) return response.json();
const retryable = response.status === 429 || response.status >= 500;
if (!retryable || attempt === 3) {
const error = await response.json();
throw new Error(`Xquik API ${response.status}: ${error.error}`);
}
const retryAfter = response.headers.get("Retry-After");
const delay = retryAfter
? parseInt(retryAfter, 10) * 1000
: baseDelay * Math.pow(2, attempt) + Math.random() * 1000;
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
```
## Cursor Pagination
Events, draws, extractions, and extraction results use cursor-based pagination. When more results exist, the response includes `hasMore: true` and a `nextCursor` string. Pass `nextCursor` as the `after` query parameter.
```javascript
async function fetchAllPages(path, dataKey) {
const results = [];
let cursor;
while (true) {
const params = new URLSearchParams({ limit: "100" });
if (cursor) params.set("after", cursor);
const data = await xquikFetch(`${path}?${params}`);
results.push(...data[dataKey]);
if (!data.hasMore) break;
cursor = data.nextCursor;
}
return results;
}
```
Cursors are opaque strings. Never decode or construct them manually.
## Complete Extraction Workflow
```javascript
// Step 1: Estimate cost before running
const estimate = await xquikFetch("/extractions/estimate", {
method: "POST",
body: JSON.stringify({
toolType: "follower_explorer",
targetUsername: "elonmusk",
resultsLimit: 1000,
}),
});
if (!estimate.allowed) {
console.log(`Need ${estimate.creditsRequired} credits; available ${estimate.creditsAvailable}`);
return;
}
// Step 2: Create extraction job
let job = await xquikFetch("/extractions", {
method: "POST",
body: JSON.stringify({
toolType: "follower_explorer",
targetUsername: "elonmusk",
resultsLimit: 1000,
}),
});
// Step 3: Poll until complete
while (job.status === "pending" || job.status === "running") {
await new Promise((r) => setTimeout(r, 2000));
job = await xquikFetch(`/extractions/${job.id}`);
}
// Step 4: Retrieve paginated results (up to 1,000 per page)
let cursor;
const allResults = [];
while (true) {
const path = `/extractions/${job.id}${cursor ? `?after=${cursor}` : ""}`;
const page = await xquikFetch(path);
allResults.push(...page.results);
if (!page.hasMore) break;
cursor = page.nextCursor;
}
// Step 5: Export as CSV/JSON/MD/MD-document/PDF/TXT/XLSX (100,000 row limit; PDF 10,000)
const exportUrl = `${BASE}/extractions/${job.id}/export?format=csv`;
const csvResponse = await fetch(exportUrl, { headers });
const csvData = await csvResponse.text();
```
## Real-Time Monitoring Setup
Complete end-to-end: create monitor, register webhook, handle events. Create persistent monitors and webhooks only after explicit user approval of the target, event types, destination URL, and ongoing cost.
```javascript
// 1. Create monitor (persistent resource; active monitors are metered hourly)
const monitor = await xquikFetch("/monitors", {
method: "POST",
body: JSON.stringify({
username: "elonmusk",
eventTypes: ["tweet.new", "tweet.reply", "tweet.quote", "tweet.retweet"],
}),
});
// 2. Register webhook (persistent delivery destination)
const webhook = await xquikFetch("/webhooks", {
method: "POST",
body: JSON.stringify({
url: "https://your-server.com/webhook",
eventTypes: ["tweet.new", "tweet.reply"],
}),
});
// IMPORTANT: Save webhook.secret. It is shown only once!
// 3. Poll events (alternative to webhooks)
const events = await xquikFetch("/events?monitorId=7&limit=50");
```
Event types: `tweet.new`, `tweet.quote`, `tweet.reply`, `tweet.retweet`, `webhook.test`.
## Endpoint Guide
| Goal | Endpoint | Cost |
|------|----------|------|
| **Get a single tweet** by ID/URL | `GET /x/tweets/{id}` | 1 credit |
| **Get an X Article** by tweet ID | `GET /x/articles/{id}` | 5 credits |
| **Search tweets** by keyword/hashtag | `GET /x/tweets/search?q=...` | 1 credit/tweet |
| **Get a user profile** | `GET /x/users/{username}` | 1 credit |
| **Get user's recent tweets** | `GET /x/users/{id}/tweets` | 1 credit/tweet |
| **Get user's liked tweets** | `GET /x/users/{id}/likes` | 1 credit/result |
| **Get user's media tweets** | `GET /x/users/{id}/media` | 1 credit/result |
| **Get tweet favoriters** | `GET /x/tweets/{id}/favoriters` | 1 credit/result |
| **Get mutual followers** | `GET /x/users/{id}/followers-you-know` | 1 credit/result |
| **Check follow relationship** | `GET /x/followers/check?source=A&target=B` | 5 credits |
| **Get trending topics** | `GET /trends?woeid=1` | 3 credits |
| **Get radar (trending news)** | `GET /radar?source=hacker_news` | Free |
| **Get bookmarks** | `GET /x/bookmarks` | 1 credit/result |
| **Get bookmark folders** | `GET /x/bookmarks/folders` | 1 credit |
| **Get notifications** | `GET /x/notifications` | 1 credit/result |
| **Get home timeline** | `GET /x/timeline` | 1 credit/result |
| **Get DM history** | `GET /x/dm/{userId}/history` | 1 credit/result |
| **Monitor an X account** | `POST /monitors` | Active monitors are metered hourly |
| **Poll for events** | `GET /events` | Free |
| **Receive events via webhook** | `POST /webhooks` | Free; confirmation required for destination URL |
| **Run a giveaway draw** | `POST /draws` | 1 credit/entry |
| **Download tweet media** | `POST /x/media/download` | 1 credit/item |
| **Extract bulk data** | `POST /extractions` | 1-5 credits/result |
| **Check credits** | `GET /credits` | Free |
| **Top up credits** | `POST /credits/topup` | Exact amount confirmation required |
| **Compose a tweet** | `POST /compose` | Free |
| **Post a tweet** | `POST /x/tweets` | 10 credits |
| **Like / Unlike a tweet** | `POST` / `DELETE /x/tweets/{id}/like` | 10 credits |
| **Retweet** | `POST /x/tweets/{id}/retweet` | 10 credits |
| **Follow / Unfollow** | `POST` / `DELETE /x/users/{id}/follow` | 10 credits |
| **Send a DM** | `POST /x/dm/{userId}` | 10 credits |
| **Update profile** | `PATCH /x/profile` | 10 credits |
| **Upload media** | `POST /x/media` | 10 credits |
| **Community actions** | `POST /x/communities`, join/leave | 10 credits |
| **Support tickets** | `POST /support/tickets` | Free |