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:
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
-
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.
-
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êmfetche--env-fileembutidos. Coloque a chave em um arquivo.envque 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 -
Busque tweets. Salve isto como
first.mjse rodenode --env-file=.env first.mjs. Ele envia uma consulta na sintaxe de busca do X paraPOST /search-tweets(abre em uma nova aba) com a chave no headerApiKey; a extensão.mjsativa oawaitde 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); -
Leia a página. Cada resposta traz até 20 tweets e um
next_cursorpara 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.
| Campo | Tipo em JavaScript | O que contém |
|---|---|---|
id | string | O ID de 19 dígitos do tweet: nunca o converta para Number |
full_text | string | O texto completo do post |
created_at | string | ISO 8601 em UTC: new Date(t.created_at) o interpreta |
likes_count, retweet_count, reply_count, quote_count | number | Contagens de engajamento |
view_count, bookmark_count | number | Visualizações e itens salvos |
user | object | O perfil do autor: 19 campos, como username e followers_count |
quoted_status, retweeted_status | object ou ausente | O 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
usernameefollowers_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: 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.
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 };
}
}
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);
}
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.
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.
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.
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.
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 useBigInt(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: 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.
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.
// 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.
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-v2 | Endpoint | O que muda |
|---|---|---|
search | POST /search-tweets | Qualquer data desde 2006, não só os últimos 7 dias |
searchAll | POST /search-tweets | O mesmo endpoint com since: e until: na consulta |
userTimeline | POST /user-tweets | Basta o nome de usuário, e não há limite de 3.200 posts |
userByUsername | GET /info (abre em uma nova aba) | Todos os campos do perfil, sem user.fields |
usersByUsernames | GET /info-batch | 100 nomes de usuário por requisição |
tweets | POST /tweet-info-bulk | 100 IDs ou links por requisição |
followers | GET /followers | 200 seguidores por página |
tweetRetweetedBy | POST /retweeters (abre em uma nova aba) | O mesmo loop de cursor |
tweet | nenhum | Somente 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:euntil: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- Endpoint
GET /info(abre em uma nova aba)- 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.
Fontes
- X API pay-per-usage pricing (docs.x.com) (abre em uma nova aba)
- X API PPU: expansions update (X Developers, October 23, 2026) (abre em uma nova aba)
- Tools and libraries: the official TypeScript SDK (docs.x.com) (abre em uma nova aba)
- Timelines (docs.x.com) (abre em uma nova aba)
- Search Posts (docs.x.com) (abre em uma nova aba)
- twitter-api-v2 on npm (abre em uma nova aba)
- @xdevplatform/xdk on npm (abre em uma nova aba)
- Node.js releases (abre em uma nova aba)
- Sorsa API reference, OpenAPI
- Sorsa API rate limits (abre em uma nova aba)
- Sorsa API pagination (abre em uma nova aba)