Xquik TypeScript SDK: Twitter search, followers & X automation

August 26, 2026 ยท View on GitHub

OpenSSF Best Practices

NPM version npm bundle size

Use the Xquik TypeScript SDK for Twitter search, timelines, profiles & followers. Manage media, webhooks, MCP & X automation with generated types and agent Skills. It provides a Twitter API alternative through documented Xquik REST routes.

TypeScript SDK Guide | API Map | REST API | Webhooks | MCP Guide

Stainless generates this SDK.

Pi coding agent package

Install the bundled Xquik Skills directly from npm:

pi install npm:x-twitter-scraper

Pi loads 3 packaged Skills:

  • x-twitter-scraper for API and MCP integration.
  • xquik-social-research for public X research.
  • xquik-account-automation for approved connected-account actions.

Import the typed SDK from the same npm package.

Common Twitter & X tasks

TaskREST RouteUsage
Search tweets without the X APIGET /x/tweets/searchUse keyword or advanced operator queries.
Read an X profile timelineGET /x/users/{id}/tweetsPaginate bounded results.
Scrape Twitter followersGET /x/users/{id}/followersUse an extraction for complete datasets.
Scrape following accountsGET /x/users/{id}/followingUse an extraction for complete datasets.
Read a home timelineGET /x/timelineApprove this private read.
Export large X datasetsPOST /extractionsPoll status, then download results.
Download or upload media/x/media/*Use typed file helpers.
Monitor an accountPOST /monitorsDeliver events through HMAC webhooks.
Run a giveaway drawPOST /drawsConfirm the tweet and entry rules.
Post or replyPOST /x/tweetsConfirm the account and payload.

AI agent workflows with MCP

Use the typed REST SDK in application code. Add https://xquik.com/mcp to MCP clients. Follow the MCP guide for current authentication support.

Package & registry trust

Installation

Requires TypeScript 4.9 or later.

npm install x-twitter-scraper

Usage

See api.md for the complete API.

import XTwitterScraper from 'x-twitter-scraper';

const client = new XTwitterScraper({
  apiKey: process.env['X_TWITTER_SCRAPER_API_KEY'], // Optional; the client reads this variable.
});

const response = await client.x.tweets.search({ q: 'from:elonmusk', limit: 10 });

Request & response types

The package includes types for every request parameter and response field. Import them directly:

import XTwitterScraper from 'x-twitter-scraper';

const client = new XTwitterScraper({
  apiKey: process.env['X_TWITTER_SCRAPER_API_KEY'], // Optional; the client reads this variable.
});

const params: XTwitterScraper.X.TweetSearchParams = { q: 'from:elonmusk', limit: 10 };
const paginatedTweets: XTwitterScraper.PaginatedTweets = await client.x.tweets.search(params);

Editors show each method, parameter, and field description from its docstring.

Guest wallet authentication

Guest wallet keys work through the public Bearer flow. Pass the key through the bearerToken option, then poll status or create a confirmed top-up checkout.

const guestClient = new XTwitterScraper({
  bearerToken: process.env['XQUIK_GUEST_API_KEY'],
});

const wallet = await guestClient.guestWallets.retrieveStatus();

Keep guest keys out of source code, URLs, and logs.

File uploads

Pass file uploads in these forms:

  • File (or an object with the same structure)
  • a fetch Response (or an object with the same structure)
  • an fs.ReadStream
  • the return value of our toFile helper
import fs from 'fs';
import XTwitterScraper, { toFile } from 'x-twitter-scraper';

const client = new XTwitterScraper();

// Stream a local file with Node fs.
await client.x.media.upload({ account: '@elonmusk', file: fs.createReadStream('/path/to/file') });

// Pass a web File.
await client.x.media.upload({ account: '@elonmusk', file: new File(['my bytes'], 'file') });

// Pass a fetch Response.
await client.x.media.upload({ account: '@elonmusk', file: await fetch('https://somesite/file') });

// Convert bytes with toFile.
await client.x.media.upload({
  account: '@elonmusk',
  file: await toFile(Buffer.from('my bytes'), 'file'),
});
await client.x.media.upload({
  account: '@elonmusk',
  file: await toFile(new Uint8Array([0, 1, 2]), 'file'),
});

Handling errors

The SDK throws an APIError subclass for connection failures and non-2xx responses:

const paginatedTweets = await client.x.tweets
  .search({ q: 'from:elonmusk', limit: 10 })
  .catch(async (err) => {
    if (err instanceof XTwitterScraper.APIError) {
      console.log(err.status); // 400
      console.log(err.name); // BadRequestError
      console.log(err.headers); // {server: 'nginx', ...}
    } else {
      throw err;
    }
  });
Status CodeError Type
400BadRequestError
401AuthenticationError
403PermissionDeniedError
404NotFoundError
422UnprocessableEntityError
429RateLimitError
>=500InternalServerError
N/AAPIConnectionError

Retries

The SDK retries connection errors and HTTP 408, 409, 429, and 5xx responses. It uses exponential backoff and attempts 2 retries by default.

Set maxRetries to change or disable retries:

// Change the default for all requests.
const client = new XTwitterScraper({
  maxRetries: 0,
});

// Override one request.
await client.x.tweets.search({ q: 'from:elonmusk', limit: 10 }, {
  maxRetries: 5,
});

Timeouts

Requests time out after 1 minute. Set a custom timeout when needed:

// Change the default for all requests.
const client = new XTwitterScraper({
  timeout: 20 * 1000,
});

// Override one request.
await client.x.tweets.search({ q: 'from:elonmusk', limit: 10 }, {
  timeout: 5 * 1000,
});

On timeout, an APIConnectionTimeoutError is thrown.

Timed-out requests follow the default retry policy.

Raw response data

Call .asResponse() on any returned APIPromise to access the raw fetch() response. It returns after receiving successful headers without consuming the body. Then parse or stream the body.

Call .withResponse() to receive the raw response and parsed data together. This method consumes and parses the body before returning.

const client = new XTwitterScraper();

const response = await client.x.tweets.search({ q: 'from:elonmusk', limit: 10 }).asResponse();
console.log(response.headers.get('X-My-Header'));
console.log(response.statusText); // access the underlying Response object

const { data: paginatedTweets, response: raw } = await client.x.tweets
  .search({ q: 'from:elonmusk', limit: 10 })
  .withResponse();
console.log(raw.headers.get('X-My-Header'));
console.log(paginatedTweets.has_next_page);

Logging

Important

All log messages are intended for debugging only. The format and content of log messages may change between releases.

Log levels

The log level can be configured in two ways:

  1. Via the X_TWITTER_SCRAPER_LOG environment variable
  2. Using the logLevel client option (overrides the environment variable if set)
import XTwitterScraper from 'x-twitter-scraper';

const client = new XTwitterScraper({
  logLevel: 'debug', // Show all log messages
});

Available log levels, from most to least verbose:

  • 'debug' - Show debug messages, info, warnings, and errors
  • 'info' - Show info messages, warnings, and errors
  • 'warn' - Show warnings and errors (default)
  • 'error' - Show only errors
  • 'off' - Disable all logging

At the 'debug' level, all HTTP requests and responses are logged, including headers and bodies. Some authentication-related headers are redacted, but sensitive data in request and response bodies may still be visible.

Custom logger

The SDK logs through globalThis.console by default. Pass a custom logger instead. It supports pino, winston, bunyan, consola, signale, and @std/log.

The logLevel option still controls custom logger output.

import XTwitterScraper from 'x-twitter-scraper';
import pino from 'pino';

const logger = pino();

const client = new XTwitterScraper({
  logger: logger.child({ name: 'XTwitterScraper' }),
  logLevel: 'debug', // Send all messages to pino, allowing it to filter
});

Custom requests

The SDK types every documented endpoint, parameter, and response property. Use its lower-level methods for undocumented API features.

Undocumented endpoints

Use client.get, client.post, or another HTTP method for undocumented endpoints. Client options, including retries, apply to these requests.

await client.post('/some/path', {
  body: { some_prop: 'foo' },
  query: { some_query_arg: 'bar' },
});

Undocumented request params

Add // @ts-expect-error to an undocumented parameter. The SDK sends extra values without runtime type validation.

client.x.tweets.search({
  // ...
  // @ts-expect-error baz is not yet public
  baz: 'undocumented option',
});

The SDK sends extra GET parameters in the query. It sends all other extra parameters in the body.

Send explicit extra arguments through the query, body, and headers options.

Undocumented response properties

Add // @ts-expect-error to the response object or cast it to the required type. The SDK does not validate or remove extra API response properties.

Custom fetch client

The SDK uses the global fetch function by default.

Polyfill the global to use another fetch implementation:

import fetch from 'my-fetch';

globalThis.fetch = fetch;

Or pass it to the client:

import XTwitterScraper from 'x-twitter-scraper';
import fetch from 'my-fetch';

const client = new XTwitterScraper({ fetch });

Fetch options

Set fetchOptions on the client or request without replacing fetch. Request options take precedence.

import XTwitterScraper from 'x-twitter-scraper';

const client = new XTwitterScraper({
  fetchOptions: {
    // `RequestInit` options
  },
});

Proxies

Add runtime-specific proxy settings through fetchOptions:

Node [docs]

import XTwitterScraper from 'x-twitter-scraper';
import * as undici from 'undici';

const proxyAgent = new undici.ProxyAgent('http://localhost:8888');
const client = new XTwitterScraper({
  fetchOptions: {
    dispatcher: proxyAgent,
  },
});

Bun [docs]

import XTwitterScraper from 'x-twitter-scraper';

const client = new XTwitterScraper({
  fetchOptions: {
    proxy: 'http://localhost:8888',
  },
});

Deno [docs]

import XTwitterScraper from 'npm:x-twitter-scraper';

const httpClient = Deno.createHttpClient({ proxy: { url: 'http://localhost:8888' } });
const client = new XTwitterScraper({
  fetchOptions: {
    client: httpClient,
  },
});

Semantic versioning

This package follows SemVer with these exceptions:

  1. Static type changes that preserve runtime behavior.
  2. Changes to undocumented internals that remain technically public.
  3. Changes unlikely to affect normal use.

Open an issue with questions, bugs, or suggestions.

Runtime support

Supports these runtimes:

  • Current Chrome, Firefox, Safari, Edge, and other web browsers.
  • Maintained Node.js 20 LTS or later.
  • Deno v1.28.0 or higher.
  • Bun 1.0 or later.
  • Cloudflare Workers.
  • Vercel Edge Runtime.
  • Jest 28 or greater with the "node" environment ("jsdom" is not supported at this time).
  • Nitro v2.6 or greater.

React Native is not supported.

Request another runtime in a GitHub issue.

Contributing

See the contributing documentation.

Xquik is an independent third-party service. Not affiliated with X Corp. "Twitter" and "X" are trademarks of X Corp.