Pular para o conteúdo

API do Twitter em Node.js: guia 2026 com código fetch que funciona

O Node.js lê dados do Twitter (X) com o fetch nativo: envie uma requisição HTTPS com a chave de API em um header, interprete o JSON e devolva o cursor recebido para pedir a próxima página. A API oficial do X cobra US$ 0,005 por post lido.

Resposta curta: O Node.js não precisa de pacote algum para ler dados do X: o fetch já vem embutido. A API oficial do X, mesmo por meio do twitter-api-v2, exige uma conta de desenvolvedor e créditos pré-pagos: US$ 0,005 por post e US$ 0,015 com o autor, a partir de 22 de novembro de 2026. Para dados públicos, use a Sorsa API: um header ApiKey, 20 tweets por requisição a partir de US$ 0,07 por 1.000; 100 requisições grátis buscam 2.000.

Endpoint
POST /search-tweets
Tamanho da página
20 tweets por requisição
Preço
a partir de US$ 0,07 por 1.000 tweets

#Qual pacote npm funciona com a API oficial do X em 2026?

Dois pacotes chamam a API oficial do X a partir do Node.js. O twitter-api-v2, um cliente da comunidade, chegou à versão 1.31.0 em 25 de outubro de 2026 e é instalado cerca de 336 mil vezes por semana. O SDK TypeScript do próprio X, @xdevplatform/xdk, está na 0.6.6. Os dois usam a mesma plataforma paga, então nenhum funciona sem uma conta de desenvolvedor, um bearer token e créditos pré-pagos. As mesmas requisições em Python, com Tweepy e o xdk nesse papel, estão no guia da API do Twitter em Python.

Os créditos são a parte que os tutoriais antigos de Node pulam. O X não tem plano gratuito para novos desenvolvedores e cobra cada objeto que uma resposta devolve: US$ 0,005 por post e US$ 0,010 por usuário, até 3 milhões de posts lidos por mês (tabela de preços do X (abre em uma nova aba)). A partir de 22 de novembro de 2026, autores e mídias trazidos por expansões custam o mesmo que as leituras diretas (aviso do X de 23 de outubro (abre em uma nova aba)). Uma busca típica do twitter-api-v2 passa então a pagar por cada autor que anexa:

twitter-api-v2
import { TwitterApi } from "twitter-api-v2";

const client = new TwitterApi("YOUR_BEARER_TOKEN").readOnly;
const page = await client.v2.search("nodejs lang:en", {
  max_results: 100,
  expansions: ["author_id"],
});
// 100 posts: $0.50. From Nov 22, 2026, with authors: up to $1.50

Os paginadores dele trazem uma segunda armadilha: a documentação da própria biblioteca diz que um loop for await sobre um paginador continua buscando até os resultados ou o seu rate limit acabarem, e cada página é cobrada. Todas as tarifas, limites e datas estão em preços da API do X.

O que a rota oficial exige antes da primeira chamada

  • Uma conta de desenvolvedor em console.x.com, aberta com uma conta do X que aceite o Contrato de Desenvolvedor do X.
  • Um app, cujo bearer token o X mostra uma única vez.
  • Créditos comprados antecipadamente e gastos por post, usuário e objeto de mídia devolvido.
  • Tokens de usuário OAuth para qualquer coisa que poste, curta ou siga.

Ler posts e perfis públicos não exige nada disso. Uma API de dados do X com chave própria responde por HTTPS simples, e o fetch do Node é o cliente inteiro.

#Obtenha tweets em Node.js com uma só chave de API

Para ler dados do X a partir do Node.js, use a Sorsa API, uma API e scraper do Twitter (X): um header ApiKey no lugar do OAuth, 20 requisições por segundo em todos os planos, inclusive nas contas grátis, e resultados de busca a partir de US$ 0,07 por 1.000 tweets. Cada tweet chega com o perfil completo do autor, então o código não define expansões nem listas de campos. A Sorsa API nunca pede um login do X, então nenhuma conta sua do X corre risco.

Configure a chave e envie a primeira requisição

  1. Obtenha uma chave. Cadastre-se e copie a sua chave de API no painel. As primeiras 100 requisições são grátis, não pedem cartão e nunca expiram.

  2. Verifique o Node e guarde a chave. Rode node -v: as linhas LTS em novembro de 2026 são 22, 24 e 26, e as três têm fetch e --env-file embutidos. Coloque a chave em um arquivo .env que o git ignore e chame a API apenas a partir de código de servidor.

    Terminal
    node -v                                  # v22 or newer
    echo 'SORSA_API_KEY=your_key' > .env
    echo '.env' >> .gitignore
    
  3. Busque tweets. Salve isto como first.mjs e rode node --env-file=.env first.mjs. Ele envia uma consulta na sintaxe de busca do X para POST /search-tweets (abre em uma nova aba) com a chave no header ApiKey; a extensão .mjs ativa o await de nível superior.

    const API = "https://api.sorsa.io/v3";
    const KEY = process.env.SORSA_API_KEY ?? "YOUR_API_KEY";
    
    const res = await fetch(`${API}/search-tweets`, {
      method: "POST",
      headers: { ApiKey: KEY, "Content-Type": "application/json" },
      body: JSON.stringify({ query: "nodejs lang:en", order: "latest" }),
    });
    if (!res.ok) throw new Error(`${res.status}: ${(await res.json()).message}`);
    
    const { tweets, next_cursor } = await res.json();
    for (const t of tweets) {
      console.log(t.user.username, t.full_text.slice(0, 60));
    }
    console.log("next page:", next_cursor);
    
  4. Leia a página. Cada resposta traz até 20 tweets e um next_cursor para a próxima página. Os valores abaixo são dados de demonstração no formato documentado.

    Response200 OK
    {
      "tweets": [
        {
          "id": "1985432176540981234",
          "full_text": "Moved our nightly X report to one Node script.",
          "created_at": "2026-11-01T18:42:07Z",
          "lang": "en",
          "likes_count": 58,
          "retweet_count": 9,
          "reply_count": 4,
          "quote_count": 1,
          "view_count": 4920,
          "bookmark_count": 6,
          "is_reply": false,
          "user": {
            "id": "1592041833471827968",
            "username": "node_stream_digest",
            "display_name": "Node Stream Digest",
            "followers_count": 2310,
            "verified": false
          }
        }
      ],
      "next_cursor": "DAABCgABF7Y8kQ3wAAA"
    }
    

Cada campo abaixo vem direto da resposta; o tipo JavaScript é o que res.json() entrega, e os IDs continuam strings de propósito.

CampoTipo em JavaScriptO que contém
idstringO ID de 19 dígitos do tweet: nunca o converta para Number
full_textstringO texto completo do post
created_atstringISO 8601 em UTC: new Date(t.created_at) o interpreta
likes_count, retweet_count, reply_count, quote_countnumberContagens de engajamento
view_count, bookmark_countnumberVisualizações e itens salvos
userobjectO perfil do autor: 19 campos, como username e followers_count
quoted_status, retweeted_statusobject ou ausenteO tweet citado ou repostado, no mesmo formato
  • id

    Tipo em JavaScript
    string
    O que contém
    O ID de 19 dígitos do tweet: nunca o converta para Number
  • full_text

    Tipo em JavaScript
    string
    O que contém
    O texto completo do post
  • created_at

    Tipo em JavaScript
    string
    O que contém
    ISO 8601 em UTC: new Date(t.created_at) o interpreta
  • likes_count, retweet_count, reply_count, quote_count

    Tipo em JavaScript
    number
    O que contém
    Contagens de engajamento
  • view_count, bookmark_count

    Tipo em JavaScript
    number
    O que contém
    Visualizações e itens salvos
  • user

    Tipo em JavaScript
    object
    O que contém
    O perfil do autor: 19 campos, como username e followers_count
  • quoted_status, retweeted_status

    Tipo em JavaScript
    object ou ausente
    O que contém
    O tweet citado ou repostado, no mesmo formato

#Tente de novo em caso de 429 e erros de servidor com um só helper

Coloque toda chamada atrás de uma função que repete apenas o que vale repetir. Um 429 significa que mais de 20 requisições começaram dentro de um segundo na sua chave; um 5xx ou um timeout é uma falha passageira. O resto deste guia importa este helper sorsa.

sorsa.mjs
// sorsa.mjs: one place for the key, timeouts and retries
const API = "https://api.sorsa.io/v3";
const KEY = process.env.SORSA_API_KEY ?? "YOUR_API_KEY";
const RETRY = new Set([429, 500, 502, 503, 504]);
const sleep = (ms) => new Promise((ok) => setTimeout(ok, ms));

export async function sorsa(path, { body, query } = {}) {
  const url = new URL(API + path);
  if (query) url.search = query; // a URLSearchParams for GET endpoints

  for (let attempt = 0; ; attempt++) {
    let res;
    try {
      res = await fetch(url, {
        method: body ? "POST" : "GET",
        headers: { ApiKey: KEY, "Content-Type": "application/json" },
        body: body && JSON.stringify(body),
        signal: AbortSignal.timeout(30_000),
      });
    } catch (err) { // timeout or dropped connection
      if (attempt === 4) throw err;
      await sleep(1000 * 2 ** attempt);
      continue;
    }
    if (res.ok) return res.json();
    if (!RETRY.has(res.status) || attempt === 4) {
      const { message } = await res.json().catch(() => ({}));
      throw new Error(`${res.status} on ${path}: ${message}`);
    }
    await sleep(1000 * 2 ** attempt); // 1, 2, 4, 8 seconds
  }
}

Um 401 ou 403 nunca merece nova tentativa: a chave está errada ou ausente, ou suas requisições acabaram, e o campo message diz qual é o caso. Requisições que falham nunca são cobradas, então repetir não custa nada. Todo endpoint da Sorsa API só lê, então repetir um POST como /search-tweets não cria nada em duplicidade. A Sorsa API não envia headers de rate limit porque a janela reinicia a cada segundo: a primeira espera de um segundo já limpa um 429.

#Percorra os resultados com next_cursor

Um cursor é uma chave opaca para a próxima página: devolva o next_cursor exatamente como veio e pare quando uma resposta não trouxer nenhum. Um gerador assíncrono se encaixa bem nesse loop, porque o código que lê as páginas nunca chega a ver o cursor.

pages.mjs
import { sorsa } from "./sorsa.mjs";

export async function* pages(path, body, maxPages = 100) {
  for (let n = 0; n < maxPages; n++) {
    const page = await sorsa(path, { body });
    yield page;
    if (!page.next_cursor) return; // null, empty or absent: the end
    body = { ...body, next_cursor: page.next_cursor };
  }
}
search.mjs
import { pages } from "./pages.mjs";

const search = { query: "nodejs lang:en", order: "latest" };
for await (const page of pages("/search-tweets", search, 50)) {
  for (const t of page.tweets) console.log(t.id, t.created_at);
}
Seu código for await yield page 1 yield page 2 return query query + "c1" query + "c2" requisição página página 1 página 2 página 3 Sorsa API POST /search-tweets 20 tweets next_cursor: "c1" 17 tweets next_cursor: "c2" 12 tweets next_cursor: null
O gerador devolve cada cursor e para no null; uma página curta no meio não é o fim.

Busca e timelines devolvem até 20 tweets por página, e uma página pode trazer menos enquanto ainda restam resultados, então a contagem nunca indica o fim. Mantenha a consulta e o order inalterados ao longo de um mesmo cursor e trate maxPages como um orçamento: 50 páginas são 50 requisições, não importa quanto a consulta encontre. Os endpoints GET recebem o cursor na query string: GET /followers (abre em uma nova aba) devolve até 200 perfis por página, e query.set("next_cursor", page.next_cursor) avança a leitura. POST /user-tweets (abre em uma nova aba) funciona com o mesmo gerador; para baixar todos os tweets de um usuário com um cursor retomável, esse guia traz o script completo.

#Busque 100 tweets ou perfis por requisição

Quando você já tem IDs ou nomes de usuário, envie-os em lotes. POST /tweet-info-bulk (abre em uma nova aba) recebe até 100 IDs ou links de tweets em tweet_links, e GET /info-batch (abre em uma nova aba) recebe até 100 nomes de usuário; cada lote é uma requisição.

batch.mjs
import { sorsa } from "./sorsa.mjs";
// tweetIds and usernames: arrays of strings you already hold

const chunks = (list, size = 100) =>
  Array.from({ length: Math.ceil(list.length / size) },
    (_, i) => list.slice(i * size, (i + 1) * size));

const tweets = [];
for (const ids of chunks(tweetIds)) {
  const body = { tweet_links: ids }; // up to 100 IDs or links
  tweets.push(...(await sorsa("/tweet-info-bulk", { body })).tweets);
}

const users = [];
for (const names of chunks(usernames)) {
  const query = new URLSearchParams();
  for (const name of names) query.append("usernames", name);
  users.push(...(await sorsa("/info-batch", { query })).users);
}

O GET /info-batch lê usernames como um parâmetro repetido (usernames=a&usernames=b), então monte-o com append. Passar um array para new URLSearchParams({ usernames }) junta os nomes com vírgulas em um único valor. Uma requisição custa o mesmo seja qual for o retorno, então um lote de 100 tweets custa o mesmo que um tweet: a partir de US$ 0,014 por 1.000 tweets, mais de 250 vezes menos que os US$ 5,00 da API oficial do X. As respostas em lote não trazem cursor; o lote inteiro vem em tweets ou users.

#Rode 20 requisições por segundo com uma só chave

A Sorsa API permite 20 requisições por segundo por chave em todos os planos, e um único processo Node chega a isso com facilidade: o event loop mantém dezenas de requisições em andamento em uma só thread. O limite a respeitar é a taxa de início, então coloque um único pacer na frente de toda chamada e divida o trabalho em cadeias independentes. Uma cadeia de cursor não roda em paralelo, porque cada página precisa do cursor da anterior, então use uma cadeia por conta ou uma por dia de busca.

lanes.mjs
import { sorsa } from "./sorsa.mjs";

// Hands out start times 1/19 s apart, however many lanes are waiting.
function pacer(perSecond) {
  const gap = 1000 / perSecond;
  let next = 0;
  return () => {
    const now = Date.now();
    const start = Math.max(next, now);
    next = start + gap;
    return new Promise((ok) => setTimeout(ok, start - now));
  };
}

const slot = pacer(19); // one pacer per key, one start under the limit

async function lane(query, maxPages = 100) {
  const tweets = [];
  let body = { query };
  for (let n = 0; n < maxPages; n++) {
    await slot();
    const page = await sorsa("/search-tweets", { body });
    tweets.push(...page.tweets);
    if (!page.next_cursor) break;
    body = { ...body, next_cursor: page.next_cursor };
  }
  return tweets;
}

const day = (d) => new Date(Date.UTC(2026, 9, d)).toISOString().slice(0, 10);
const queries = Array.from({ length: 31 }, (_, i) =>
  `nodejs lang:en since:${day(i + 1)} until:${day(i + 2)}`); // Oct 1-31
const results = await Promise.all(queries.map((q) => lane(q)));
console.log(results.flat().length, "tweets");

O pacer não precisa de lock: o JavaScript executa um callback por vez, então duas cadeias nunca pegam o mesmo horário de início. Com 19 inícios por segundo, sobra uma folga de um início, porque a oscilação dos timers pode empurrar o 20º início para a janela seguinte. A retentativa do helper pega qualquer 429 que ainda escape. O erro comum é usar Promise.all sem um pacer: 60 chamadas disparadas de uma vez levam 40 direto para 429 e gastam segundos de espera.

Um processo Node.js pacer(19) 1 out 2 out 3 out 4 out uma chave de API 19 inícios por segundo abaixo do limite de 20 por chave Três instâncias serverless pacer(19) pacer(19) pacer(19) cada uma conta só seus inícios a mesma chave de API 57 inícios por segundo 37 levam 429 e esperam
Um pacer só conta os inícios do próprio processo, então dê um único pacer a cada chave ou divida a taxa entre as instâncias.

As plataformas serverless quebram essa regra sem avisar. Cada instância de função no Lambda, na Vercel ou no Cloud Run carrega a própria cópia de lanes.mjs. Três instâncias aquecidas rodam, portanto, três pacers em uma só chave e iniciam até 57 requisições por segundo entre elas. Dê a cada instância uma parte da taxa (pacer(6) para três) ou rode o job em massa em um único worker. O limite é contado por chave, então uma segunda chave para um segundo worker traz os próprios 20 por segundo. A 20 requisições por segundo, lotes de 100 movem até 2.000 tweets por segundo e páginas de busca, 400.

#Salve os tweets em JSONL ou CSV

Grave cada página assim que ela chega, em vez de manter todos os tweets na memória. O JSON Lines, um objeto JSON por linha, preserva todos os campos e aceita acréscimos com segurança; o CSV abre no Excel e no Google Sheets. Os dois streams abaixo esperam o drain quando o disco fica para trás.

save.mjs
import { createWriteStream } from "node:fs";
import { once } from "node:events";
import { pages } from "./pages.mjs";

const jsonl = createWriteStream("tweets.jsonl");
const csv = createWriteStream("tweets.csv");
const put = async (out, line) => {
  if (!out.write(line)) await once(out, "drain"); // respect backpressure
};
const cell = (v) => `"${String(v ?? "").replaceAll('"', '""')}"`;
const BOM = String.fromCharCode(0xfeff); // tells Excel the file is UTF-8

await put(csv, BOM + "id,created_at,username,likes_count,full_text\r\n");
const search = { query: "nodejs lang:en" };
for await (const page of pages("/search-tweets", search, 500)) {
  for (const t of page.tweets) {
    await put(jsonl, JSON.stringify(t) + "\n");
    const row = [t.id, t.created_at, t.user.username, t.likes_count,
      t.full_text];
    await put(csv, row.map(cell).join(",") + "\r\n");
  }
}
jsonl.end();
csv.end();

A marca de ordem de bytes (BOM) no início do CSV avisa ao Excel que o arquivo é UTF-8, então emojis e acentos aparecem corretamente. Toda célula vai entre aspas, então vírgulas, aspas e quebras de linha dentro de um tweet ficam na própria coluna. Quinhentas páginas formam até 10 mil tweets com 500 requisições; a memória fica estável por mais longa que seja a execução.

Bom saber

Os números do JavaScript guardam inteiros com exatidão só até 2^53, cerca de 9 quadrilhões, e os IDs de tweets têm 19 dígitos. Number("1985432176540981234") devolve 1985432176540981200, que é outro tweet. A Sorsa API envia todo ID e todo cursor como string, então mantenha-os como strings, compare-os como strings e use BigInt(id) quando precisar ordenar por ID.

#Adicione tipos TypeScript às respostas

Quem usa TypeScript ganha segurança de tipos com algumas interfaces nomeadas exatamente como os campos da API. As de baixo seguem a referência OpenAPI ao vivo e cobrem os campos que a maior parte do código lê; a referência lista o resto.

sorsa-types.ts
// sorsa-types.ts: field names as in the Sorsa API reference
export interface SorsaUser {
  id: string; // 64-bit ID: keep it a string
  username: string;
  display_name: string;
  description: string;
  followers_count: number;
  followings_count: number;
  tweets_count: number;
  verified: boolean;
  protected: boolean;
  created_at: string; // ISO 8601
}

export interface SorsaTweet {
  id: string;
  full_text: string;
  created_at: string;
  lang: string;
  likes_count: number;
  retweet_count: number;
  reply_count: number;
  quote_count: number;
  view_count: number;
  bookmark_count: number;
  is_reply: boolean;
  user: SorsaUser;
  quoted_status?: SorsaTweet;
  retweeted_status?: SorsaTweet;
}

export interface TweetsPage {
  tweets: SorsaTweet[];
  next_cursor?: string | null;
}

Os campos opcionais levam um ?: um tweet citado ou repostado só existe em citações e reposts, então o compilador obriga você a verificar antes de ler. Faça o cast na fronteira, const page = (await sorsa(path, { body })) as TweetsPage, e o resto do código passa a ser verificado. O Node 22.18 e posteriores executam arquivos .ts diretamente, removendo os tipos, então node --env-file=.env job.ts funciona sem etapa de build. Para todos os endpoints de uma vez, um gerador OpenAPI como o openapi-typescript pode transformar a referência completa em tipos.

#Dá para chamar uma API do Twitter a partir de JavaScript no navegador?

Não: uma chave de API no código do navegador pode ser lida por qualquer pessoa que abra as ferramentas de desenvolvedor, e a Sorsa API foi feita para chamadas pelo servidor, então os navegadores recebem erros de CORS. Crie uma pequena rota no seu servidor, guarde a chave lá e deixe o front-end chamar a sua rota.

// server.mjs: npm install express
import express from "express";
import { sorsa } from "./sorsa.mjs";

const app = express();
app.get("/api/tweets", async (req, res) => {
  const query = String(req.query.q ?? "").slice(0, 200);
  const page = await sorsa("/search-tweets", { body: { query } });
  res.json(page.tweets.map((t) => ({ id: t.id, text: t.full_text })));
});
app.listen(3000);

O navegador então executa fetch("/api/tweets?q=nodejs") no seu próprio domínio e nunca vê a chave. O mesmo padrão serve para qualquer runtime de servidor: um Cloudflare Worker ou uma função Lambda guarda a chave nas próprias variáveis de ambiente, e a página só fala com ele. Coloque a resposta da rota em cache por um minuto quando muitos visitantes pedirem a mesma coisa: a Sorsa API devolve dados novos a cada requisição, e uma resposta em cache atende todos eles por uma requisição só.

#Quanto custa um job em Node.js

A Sorsa API cobra por requisição, seja qual for o retorno do endpoint: resultados de busca custam a partir de US$ 0,07 por 1.000 tweets, lotes a partir de US$ 0,014, e requisições com falha nunca são cobradas. A API oficial do X cobra por objeto: US$ 5,00 por 1.000 posts e, a partir de 22 de novembro de 2026, até US$ 15,00 por 1.000 quando o código expande o autor de cada post, como faz a maioria das buscas do twitter-api-v2.

Tweets que seu código coleta por busca

API oficial do X

US$ 5.000

para 1 milhão tweets

US$ 0,005 por post

Sorsa API

a partir deUS$ 70

para 1 milhão tweets

a partir de US$ 0,07 por 1.000

Cada tweet traz o perfil do seu autor

O construtor abaixo escreve o loop em Node.js para um job e mostra as requisições, o menor tempo possível em uma chave e o custo na tarifa mais baixa da Sorsa API.

Tarefa
Quantos
job.mjs
// sorsa() e pages() vêm dos helpers acima
const body = { query: "nodejs lang:en" };
for await (const page of pages("/search-tweets", body, 5000)) {
  save(page.tweets); // até 20 tweets por página
}
Requisições
5,000
Mais rápido com uma chave
cerca de 4 min
Sorsa API
a partir de US$ 7

Tempo a 20 requisições por segundo; custo a US$ 1,40 por 1,000 requisições. Uma busca é uma única cadeia de cursor, então divida em janelas de datas para rodar faixas lado a lado.

O tempo é o mais rápido que uma chave permite; uma única cadeia de cursor anda no ritmo de suas idas e vindas, então divida trabalhos grandes em faixas.

A tarifa por 1.000 requisições vai de US$ 4,90 no Starter a US$ 1,40 no Hyperscale, e todo endpoint usa a mesma tarifa. Consultar o que resta não custa nada: GET /key-usage-info (abre em uma nova aba) nunca desconta das suas requisições.

usage.mjs
import { sorsa } from "./sorsa.mjs";
const usage = await sorsa("/key-usage-info");
console.log(usage.remaining_requests, "left until", usage.valid_until);

#Como migrar o código do twitter-api-v2 para a Sorsa API

A maioria das chamadas do twitter-api-v2 corresponde a um endpoint da Sorsa API, e o código perde as expansões e as listas de campos, porque cada tweet já traz o autor e as contagens. A tabela une os métodos comuns do client.v2 aos seus endpoints; para migrar da API do Twitter (X) em uma base de código inteira, esse guia mapeia todos os endpoints v2 e campos de resposta.

Método do twitter-api-v2EndpointO que muda
searchPOST /search-tweetsQualquer data desde 2006, não só os últimos 7 dias
searchAllPOST /search-tweetsO mesmo endpoint com since: e until: na consulta
userTimelinePOST /user-tweetsBasta o nome de usuário, e não há limite de 3.200 posts
userByUsernameGET /info (abre em uma nova aba)Todos os campos do perfil, sem user.fields
usersByUsernamesGET /info-batch100 nomes de usuário por requisição
tweetsPOST /tweet-info-bulk100 IDs ou links por requisição
followersGET /followers200 seguidores por página
tweetRetweetedByPOST /retweeters (abre em uma nova aba)O mesmo loop de cursor
tweetnenhumSomente leitura: poste pela API oficial do X
  • search

    Endpoint
    POST /search-tweets
    O que muda
    Qualquer data desde 2006, não só os últimos 7 dias
  • searchAll

    Endpoint
    POST /search-tweets
    O que muda
    O mesmo endpoint com since: e until: na consulta
  • userTimeline

    Endpoint
    POST /user-tweets
    O que muda
    Basta o nome de usuário, e não há limite de 3.200 posts
  • userByUsername

    O que muda
    Todos os campos do perfil, sem user.fields
  • usersByUsernames

    Endpoint
    GET /info-batch
    O que muda
    100 nomes de usuário por requisição
  • tweets

    Endpoint
    POST /tweet-info-bulk
    O que muda
    100 IDs ou links por requisição
  • followers

    Endpoint
    GET /followers
    O que muda
    200 seguidores por página
  • tweetRetweetedBy

    O que muda
    O mesmo loop de cursor
  • tweet

    Endpoint
    nenhum
    O que muda
    Somente leitura: poste pela API oficial do X

Os nomes dos campos mudam também: text vira full_text, public_metrics.like_count vira likes_count, author_id com includes.users vira o objeto user, e meta.next_token vira next_cursor. O fetchNext() do paginador vira o gerador pages acima, que para no seu orçamento de páginas em vez de parar num rate limit. O POST /user-tweets também passa dos 3.200 posts mais recentes, onde a timeline oficial para (documentação de timelines do X (abre em uma nova aba)), e a busca recente da API oficial do X cobre apenas os últimos 7 dias (documentação de busca do X (abre em uma nova aba)).

#Faça hoje a sua primeira requisição em Node.js

Ler dados do X em Node.js exige o fetch, uma chave e quatro pequenas peças: sorsa para as retentativas, pages para os cursores, chunks para os lotes de 100 e um pacer para 20 requisições por segundo. A Sorsa API é a API do Twitter para começar com isso: 20 tweets por requisição de busca a partir de US$ 0,07 por 1.000, o autor dentro de cada tweet, busca desde 2006 e 100 requisições grátis que não pedem cartão e nunca expiram. Para levar os mesmos dados a um workflow sem código, veja a API do Twitter no n8n.

#Perguntas frequentes

Qual API do Twitter devo usar com Node.js?

Para ler dados do X em Node.js, use a Sorsa API: uma chave em um header ApiKey com o fetch nativo, resultados de busca a partir de US$ 0,07 por 1.000 tweets e lotes a partir de US$ 0,014, 20 requisições por segundo em todos os planos, busca desde 2006 e nenhum login do X, então a sua conta nunca corre risco. Para postar também, combine-a com a API oficial do X.

Existe um SDK oficial da API do Twitter para Node.js?

Sim. O X publica um SDK TypeScript, @xdevplatform/xdk (versão 0.6.6), que funciona no Node.js e em bundlers. Ele é um cliente da API oficial do X, então cada chamada ainda exige uma conta de desenvolvedor, um bearer token e créditos pré-pagos a US$ 0,005 por post. A Sorsa API não precisa de SDK: o fetch simples cobre os 45 endpoints dela.

Qual é o melhor pacote npm para dados do Twitter (X)?

Para a Sorsa API, nenhum: o fetch nativo do Node e os helpers de 40 linhas deste guia cobrem todos os endpoints, sem dependência para atualizar. Para a API oficial do X, o twitter-api-v2 é o cliente mais instalado, com cerca de 336 mil downloads por semana e versão 1.31.0, e o @xdevplatform/xdk do próprio X ao lado; os dois cobram seus créditos do X por objeto.

Existe um pacote npm grátis para obter tweets sem chave de API?

Pacotes como o rettiwt-api leem o app web do X em vez de uma API. Sem login, eles alcançam só algumas consultas; para o resto, enviam os cookies da sua conta do X a cada requisição. Isso amarra o tráfego à sua conta, e eles quebram quando o X muda o app web. A Sorsa API nunca pede o seu login do X, e as 100 requisições grátis dela não pedem cartão. Os prós e contras estão em como fazer scraping do Twitter.

Posso postar tweets em Node.js com a Sorsa API?

Não. A Sorsa API é somente leitura por princípio: ela lê posts, perfis e seguidores públicos e nunca posta, curte, segue nem envia mensagens, então uma chave não pode agir em nenhuma conta do X. Para postar em Node.js, use a API oficial do X com um token de usuário OAuth, por exemplo o client.v2.tweet do twitter-api-v2, a US$ 0,015 por post, e deixe a Sorsa API para as leituras.

Posso obter dados do Twitter em Node.js sem conta de desenvolvedor?

Sim. A Sorsa API precisa só da própria chave: cadastre-se, defina o header ApiKey e chame qualquer endpoint dela a partir do Node.js, sem conta de desenvolvedor do X, sem análise de app e sem OAuth. As primeiras 100 requisições são grátis e buscam até 2.000 tweets por busca ou 10 mil em lotes de 100. As opções são comparadas em API do Twitter sem conta de desenvolvedor.

#Como verificamos

Escrevemos cada requisição a partir da referência OpenAPI ao vivo da Sorsa API (45 endpoints) e da documentação dela sobre autenticação, rate limits e paginação, lidas em 2 de novembro de 2026. Não tivemos uma chave da Sorsa API nesta rodada, então a resposta usa valores de demonstração. Rodamos os blocos Node.js no Node 26 contra um mock local que responde nos formatos documentados: a primeira requisição, o helper de retentativas (dois 503 e depois uma rajada de 60 chamadas), o gerador de cursores, os lotes de 100, a exportação em JSONL e CSV, a chamada de uso da chave e o pacer. Com 19 inícios por segundo, o pacer rodou 31 cadeias, 93 requisições, sem nenhum 429 em três execuções; com exatamente 20, uma de três execuções mandou 29 requisições para a retentativa. Os trechos de Express, Next.js e twitter-api-v2 não foram executados. No mesmo dia lemos os preços do X, o aviso de 22 de novembro sobre expansões e a página de ferramentas dele em docs.x.com, as entradas do twitter-api-v2 e do @xdevplatform/xdk no registro npm e a tabela de releases do Node.js.

Pela equipe da Sorsa API. Construímos e operamos a Sorsa API, a API e scraper somente leitura do Twitter (X), em produção desde 2022 (antiga TweetScout API), com mais de 6 bilhões de requisições atendidas. Sobre a Sorsa API

O que mudou
  • Reescrito do zero: preços atuais da API oficial do X com a cobrança das expansões a partir de 22 de novembro, twitter-api-v2 1.31.0 e o SDK TypeScript do X 0.6.6, e um caminho em Node.js com uma só chave de API: retentativas, um gerador assíncrono para cursores, lotes de 100, um pacer para 20 requisições por segundo com a armadilha do serverless, JSONL e CSV, tipos TypeScript, rotas Express e Next.js e um mapa de métodos do twitter-api-v2.
  • Primeira publicação.