Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.xquik.com/llms.txt

Use this file to discover all available pages before exploring further.

The Xquik MCP server exposes 2 tools: explore (search the API spec) and xquik (execute API calls). Connect to the MCP server at https://xquik.com/mcp with your API key.

explore

Search the API endpoint catalog. Read-only, no network calls, and no credits required. The call still requires MCP authentication through an API key or OAuth Bearer token. Use this to discover available endpoints, check parameters, and find the right API path before executing calls. Input:

Sandbox code

Pass code as an async arrow function. It is required and can be up to 10,000 characters.

Spec access

The function runs against spec.endpoints so agents can filter endpoint paths, parameters, categories, costs, and response shapes before making a live call.
Sandbox API:
interface EndpointInfo {
  method: string;
  path: string;
  summary: string;
  category: string; // account, composition, credits, extraction, 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:
Find all free endpoints
async () => {
  return spec.endpoints.filter(e => e.free);
}
Find endpoints by category
async () => {
  return spec.endpoints.filter(e => e.category === 'composition');
}
Search by keyword
async () => {
  return spec.endpoints.filter(e => e.summary.toLowerCase().includes('tweet'));
}

xquik

Execute API calls against your Xquik account. The agent writes code using xquik.request() with auth injected automatically. Input:

Sandbox code

Pass code as an async arrow function. It is required and can be up to 10,000 characters.

Authenticated calls

The function can call xquik.request(path, { method, body, query }) with auth injected automatically.
Sandbox API:
declare const xquik: {
  request(path: string, options?: {
    method?: string;  // default: 'GET'
    body?: unknown;
    query?: Record<string, string>;
  }): Promise<unknown>;
};
declare const spec: { endpoints: EndpointInfo[] };
Response contract: xquik.request() uses the normalized v1 contract automatically. List and search responses use has_more and next_cursor, even when the REST API page shows the default has_next_page field. Pass next_cursor back as the cursor query parameter for most X data pages. Use after instead for /api/v1/draws, /api/v1/extractions, /api/v1/events, and /api/v1/radar. Workflow Examples:
Compose an algorithm-optimized tweet (3-step, free)
async () => {
  // Step 1: Get algorithm data and follow-up questions
  const compose = await xquik.request('/api/v1/compose', {
    method: 'POST',
    body: { step: 'compose', topic: 'AI agents' }
  });
  return compose;
  // Returns contentRules, followUpQuestions, scorerWeights
  // Step 2: After user answers: { step: 'refine', goal, tone, topic }
  // Step 3: After drafting: { step: 'score', draft }
  // The score step runs algorithm checks. The draft must pass
  // all checks before presenting to the user.
}
Save a writing style from screenshots (free)
async () => {
  // When a user shares tweet screenshots, extract the texts and save as a style.
  // This lets free users clone any writing voice without a subscription.
  return xquik.request('/api/v1/styles/elonmusk', {
    method: 'PUT',
    body: {
      label: 'Elon Musk style',
      tweets: [
        { text: 'The most entertaining outcome is the most likely' },
        { text: 'Mars, here we come!!' }
      ]
    }
  });
  // Then compose with: POST /api/v1/compose { step: 'compose', topic: '...', styleUsername: 'elonmusk' }
}
Browse trending news from radar (free)
async () => {
  return xquik.request('/api/v1/radar');
}
Radar + Style + Compose combined (free)
async () => {
  const [radar, styles] = await Promise.all([
    xquik.request('/api/v1/radar'),
    xquik.request('/api/v1/styles'),
  ]);
  return { radar, styles };
}
Analyze a user’s writing style
async () => {
  // Returns cached style if available (free)
  // Auto-refreshes from X if cache older than 7 days (subscription required)
  return xquik.request('/api/v1/styles', {
    method: 'POST',
    body: { username: 'elonmusk' }
  });
}
Search tweets with cursor pagination (subscription required)
async () => {
  const tweets = [];
  let cursor;

  for (let pageNumber = 0; pageNumber < 2; pageNumber += 1) {
    const page = await xquik.request('/api/v1/x/tweets/search', {
      query: {
        q: 'from:xquikcom giveaway',
        ...(cursor ? { cursor } : {})
      }
    });

    tweets.push(...page.tweets);
    if (!page.has_more) break;
    cursor = page.next_cursor;
  }

  return { tweets, next_cursor: cursor };
}
List follower pages (subscription required)
async () => {
  const users = [];
  let cursor;

  for (let pageNumber = 0; pageNumber < 2; pageNumber += 1) {
    const page = await xquik.request('/api/v1/x/users/xquikcom/followers', {
      query: {
        pageSize: '50',
        ...(cursor ? { cursor } : {})
      }
    });

    users.push(...page.users);
    if (!page.has_more) break;
    cursor = page.next_cursor;
  }

  return { users, next_cursor: cursor };
}
Post a tweet or reply with public media URLs (subscription required)
async () => {
  return xquik.request('/api/v1/x/tweets', {
    method: 'POST',
    body: {
      account: 'myxhandle',
      text: 'Launch media is ready',
      reply_to_tweet_id: '1893456789012345678',
      media: ['https://example.com/product-demo.mp4']
    }
  });
  // Returns { success: true, tweet_id: '...' } or a pending write_action_id
}
Upload media for a DM (subscription required)
async () => {
  const media = await xquik.request('/api/v1/x/media', {
    method: 'POST',
    body: {
      account: 'myxhandle',
      url: 'https://example.com/image.png'
    }
  });
  // Returns { success: true, media_id: '...', media_url: '...' }
  return xquik.request('/api/v1/x/dm/44196397', {
    method: 'POST',
    body: {
      account: 'myxhandle',
      text: 'Here is the asset',
      media_ids: [media.media_id]
    }
  });
}
Download media and get gallery link (subscription required)
async () => {
  return xquik.request('/api/v1/x/media/download', {
    method: 'POST',
    body: { tweetInput: '1234567890' }
  });
}
Bulk download: search + download combined
async () => {
  const search = await xquik.request('/api/v1/x/tweets/search', {
    query: { q: 'from:berktavsan has:videos' }
  });
  if (!search.tweets?.length) return { error: 'No video tweets found' };
  const tweetIds = search.tweets.map(t => t.id);
  return xquik.request('/api/v1/x/media/download', {
    method: 'POST',
    body: { tweetIds }
  });
}
Monitor a user + create webhook (monitor creation requires subscription, webhook is free)
async () => {
  const monitor = await xquik.request('/api/v1/monitors', {
    method: 'POST',
    body: { username: 'elonmusk', eventTypes: ['tweet.new', 'tweet.reply'] }
  });
  const webhook = await xquik.request('/api/v1/webhooks', {
    method: 'POST',
    body: { url: 'https://example.com/hook', eventTypes: ['tweet.new', 'tweet.reply'] }
  });
  const test = await xquik.request(`/api/v1/webhooks/${webhook.id}/test`, {
    method: 'POST'
  });
  return {
    monitor_id: monitor.id,
    event_types: monitor.event_types,
    next_billing_at: monitor.next_billing_at,
    webhook_id: webhook.id,
    webhook_url: webhook.url,
    save_secret_once: 'Store webhook.secret for X-Xquik-Signature verification; do not print it in logs.',
    test
  };
}
Run an extraction with a resumable handoff (subscription required)
async () => {
  const body = {
    toolType: 'tweet_search_extractor',
    searchQuery: 'launch announcement',
    resultsLimit: 500
  };

  const estimate = await xquik.request('/api/v1/extractions/estimate', {
    method: 'POST',
    body
  });

  if (estimate.allowed === false) {
    return {
      source: 'xquik_mcp',
      job: 'tweet_search_extraction',
      status: 'blocked',
      error: estimate.error,
      credits_required: estimate.credits_required,
      credits_available: estimate.credits_available
    };
  }

  const job = await xquik.request('/api/v1/extractions', {
    method: 'POST',
    body
  });

  return {
    source: 'xquik_mcp',
    job: 'tweet_search_extraction',
    extraction_id: job.id,
    tool_type: job.tool_type,
    status: job.status,
    query: body.searchQuery,
    results_limit: body.resultsLimit,
    estimated_results: estimate.estimated_results,
    credits_required: estimate.credits_required,
    poll: `/api/v1/extractions/${job.id}`,
    export_after_complete: `/api/v1/extractions/${job.id}/export?format=json`
  };
}

Agent handoff patterns

MCP returns JSON. Use extraction export endpoints when you need Xquik to generate CSV, JSON, XLSX, Markdown, or PDF files. For agent queues, CRMs, and warehouses, return a small object with the original job, the route used, the rows or IDs to store, and the next cursor or write action to poll.

Search tweets to JSON

Call GET /api/v1/x/tweets/search. Store tweets[].id, tweets[].text, tweets[].author, tweets[].created, has_more, next_cursor, and the original q. Cost: 1 credit per tweet returned.

Scrape tweet replies to files

Call POST /api/v1/extractions/estimate, then POST /api/v1/extractions with reply_extractor and targetTweetId. Poll GET /api/v1/extractions/{id}, export CSV/JSON/XLSX with GET /api/v1/extractions/{id}/export, and store reply rows plus has_more and next_cursor. Cost: 1 credit per reply extracted or returned.

Export followers to CRM

Call GET /api/v1/x/users/{id}/followers or POST /api/v1/extractions with follower_explorer. Store users[].id, users[].username, users[].name, users[].followers, has_more, and next_cursor. Cost: 1 credit per follower returned or extracted.

Post media tweets or replies

Call POST /api/v1/x/tweets with media: ["https://..."]. Store tweet_id or write_action_id, reply_to_tweet_id, account, and the original media URLs. Cost: 10 credits per write call.

Send DMs with media

Call POST /api/v1/x/media, then POST /api/v1/x/dm/{userId} with one media_ids value. Store media_id, media_url, message_id, user_id, account, and source URL or filename. Cost: 10 credits per media upload plus 10 credits per DM send.

Track tweet or reply writes

Call POST /api/v1/x/tweets, then GET /api/v1/x/write-actions/{id} when pending. Store tweet_id, reply_to_tweet_id, write_action_id, status, and charged. Cost: 10 credits per write call.

Monitor tweets to webhooks

Call POST /api/v1/monitors or POST /api/v1/monitors/keywords, then POST /api/v1/webhooks. Store monitor.id, event_types, next_billing_at, webhook.id, webhook URL, and the one-time webhook.secret; run POST /api/v1/webhooks/{id}/test before routing production events. Cost: 21 credits per active monitor-hour; webhook delivery is included.
Do not upload media before posting tweets or replies when the media is already public. POST /api/v1/x/tweets rejects media_ids with 400 unsupported_field; pass up to 4 public image URLs or exactly 1 public MP4 video URL up to 100 MB in media instead. Reserve uploaded media_id values for direct messages.
async () => {
  const page = await xquik.request('/api/v1/x/tweets/search', {
    query: { q: 'from:xquikcom giveaway', limit: '50' }
  });

  return {
    source: 'xquik_mcp',
    job: 'tweet_search',
    query: 'from:xquikcom giveaway',
    rows: page.tweets.map(tweet => ({
      tweet_id: tweet.id,
      text: tweet.text,
      author: tweet.author,
      created: tweet['created'],
      url: tweet.url
    })),
    has_more: page.has_more,
    next_cursor: page.next_cursor
  };
}
Subscribe (free, returns checkout or billing portal URL)
async () => {
  return xquik.request('/api/v1/subscribe', { method: 'POST' });
}

API endpoints

The server covers 120 operations across 10 categories:

Account, composition, and credits

24 operations across account, composition, and credits: account info, API keys, subscribe, X identity, compose, styles, drafts, radar, balance checks, top-ups, checkout status, and quick top-up.

Extractions and media

10 operations across extraction and media: giveaway draws, extraction jobs, estimates, exports, and media download.

Monitoring and webhooks

18 operations in monitoring: account monitors, keyword monitors, stored events, webhooks, deliveries, and test delivery.

Support

5 operations in support: create, list, read, reply to, and close support tickets.

X data reads

38 operations in twitter: tweet search, tweet and article lookup, user lookup, follow checks, trends, bookmarks, notifications, timeline, DM history, likes, media, followers, replies, communities, and lists.

X accounts and writes

25 operations across x-accounts and x-write: connect accounts, retry connection issues, post tweets, like, retweet, follow, remove followers, send DMs, upload media, update profiles, and manage community membership.
Use explore to browse the full catalog with parameters and response shapes.

Cost summary

Always free discovery

explore is free. Use it to find endpoints, parameters, and response shapes before making API calls.

Free account and stored records

Compose, cached styles, drafts, radar, subscribe, account, API keys, support, credits, X account management, webhooks, stored monitors, stored events, and existing extraction or draw reads are free.

Metered reads and jobs

Tweet search, user lookup, follow checks, media download, trends, extraction creation, and draw creation are metered.

Monitor billing

Active instant monitors cost 21 credits per active monitor-hour. Creating monitors requires a subscription and available credits.

Write actions

Tweet, reply, like, retweet, follow, DM, profile, community, and media upload writes are metered.

Subscription-only refreshes

Fresh style analysis after the 7-day cache window requires an active subscription.
Never combine free and paid endpoints in a single Promise.all. A 402 error on one call kills all results. Call free endpoints first, then paid ones separately.

Error handling

  • 402 / no_subscription / subscription_inactive: The sandbox attempts POST /api/v1/subscribe; when it returns a URL, the error includes it. Present that URL to the user.
  • 402 / no_credits / insufficient_credits: Free data already fetched is preserved. Call POST /api/v1/credits/topup or POST /api/v1/credits/quick-topup, then retry the failed metered call.
  • 429: Rate limited. Retry with exponential backoff.
  • API errors: Include status code and message in the response.

Last modified on May 16, 2026