API do Twitter em Python: tutorial 2026 com código que funciona
Para obter dados do Twitter (X) em Python, chame uma API de dados do X com requests ou um cliente como o Tweepy, envie a chave em cada requisição e siga o cursor página por página; a API oficial do X cobra US$ 0,005 por post.
Resposta curta: O Tweepy e o SDK do X, o xdk, são bibliotecas gratuitas para a API oficial do X, que exige uma conta de desenvolvedor, um bearer token 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 ler dados públicos, use a Sorsa API com requests: uma chave, 20 tweets por requisição a partir de US$ 0,07 por 1.000; 100 requisições grátis cobrem 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
#O Tweepy ainda funciona em 2026?
Sim. O Tweepy 4.17.0 foi lançado em 2 de julho de 2026, e o X agora publica o próprio SDK Python, o xdk, na versão 0.10.6. Os dois são clientes da API oficial do X, então cada chamada que fazem exige uma conta de desenvolvedor, o bearer token de um app e créditos pré-pagos.
Os créditos são o que os tutoriais mais antigos deixam de fora. Novos desenvolvedores não têm plano gratuito, e o X 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, os autores e outros objetos trazidos por expansões são cobrados pelas mesmas tarifas (aviso do X de 23 de outubro (abre em uma nova aba)), então a busca usual do Tweepy paga por cada autor que anexa:
import tweepy
client = tweepy.Client(bearer_token="YOUR_BEARER_TOKEN")
resp = client.search_recent_tweets(
"python lang:en",
max_results=100,
expansions=["author_id"],
)
# 100 posts: $0.50. From Nov 22, 2026, with authors: up to $1.50
A biblioteca em si é gratuita e de código aberto. A página inicial dela ainda diz que a API por trás tem planos gratuitos e pagos, o que já não vale para uma conta nova. 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 e com o Contrato de Desenvolvedor do X aceito.
- Um app, cujo bearer token o X mostra uma única vez (guia de acesso do X (abre em uma nova aba)).
- Créditos comprados antecipadamente e gastos por post e por usuário 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 Twitter (X) com chave própria responde por HTTPS simples, e o requests é o único cliente de que um script precisa.
#Obtenha tweets em Python com uma só chave de API
Para ler dados do X a partir do Python, 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. Ela devolve posts, perfis e seguidores públicos em JSON, com o perfil completo do autor dentro de cada tweet. Ela nunca pede o seu login do X, então a sua conta do X não 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.
-
Instale o requests e guarde a chave. Mantenha a chave em uma variável de ambiente em vez de no código, e chame a API a partir de um script ou servidor, nunca de JavaScript no navegador.
Terminal pip install requests export SORSA_API_KEY="your_key" # PowerShell: $env:SORSA_API_KEY="your_key" -
Busque tweets. Envie uma consulta na sintaxe de busca do X para
POST /search-tweets(abre em uma nova aba) com a chave no headerApiKey.import os import requests API = "https://api.sorsa.io/v3" HEADERS = {"ApiKey": os.environ.get("SORSA_API_KEY", "YOUR_API_KEY")} r = requests.post( f"{API}/search-tweets", headers=HEADERS, json={"query": "python lang:en", "order": "latest"}, timeout=30, ) r.raise_for_status() for tweet in r.json()["tweets"]: print(tweet["user"]["username"], tweet["full_text"][:60]) -
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": "Loaded a week of #python posts into pandas in four lines.", "created_at": "2026-11-01T18:42:07Z", "lang": "en", "likes_count": 42, "retweet_count": 7, "reply_count": 3, "quote_count": 1, "view_count": 3810, "bookmark_count": 5, "is_reply": false, "user": { "id": "1592041833471827968", "username": "py_data_notebook", "display_name": "Py Data Notebook", "followers_count": 1840, "verified": false } } ], "next_cursor": "DAABCgABF7Y8kQ3wAAA" }
Cada tweet chega com as suas contagens e o seu autor, então um script não define expansões nem listas de campos. Estes são os campos que a maioria dos scripts lê:
| Campo | O que contém | Exemplo |
|---|---|---|
id | O ID de 19 dígitos do tweet, enviado como string: mantenha-o assim | 1985432176540981234 |
full_text | O texto completo do post | Loaded a week of #python posts |
created_at | Quando foi postado, ISO 8601 em UTC | 2026-11-01T18:42:07Z |
likes_count, retweet_count, reply_count, quote_count | Contagens de engajamento | 42 |
view_count, bookmark_count | Visualizações e itens salvos | 3810 |
user | O perfil do autor: 19 campos, como username e followers_count | py_data_notebook, 1840 |
quoted_status, retweeted_status | O tweet citado ou repostado, no mesmo formato | Um objeto de tweet |
id- O que contém
- O ID de 19 dígitos do tweet, enviado como string: mantenha-o assim
- Exemplo
1985432176540981234
full_text- O que contém
- O texto completo do post
- Exemplo
- Loaded a week of #python posts
created_at- O que contém
- Quando foi postado, ISO 8601 em UTC
- Exemplo
2026-11-01T18:42:07Z
likes_count,retweet_count,reply_count,quote_count- O que contém
- Contagens de engajamento
- Exemplo
42
view_count,bookmark_count- O que contém
- Visualizações e itens salvos
- Exemplo
3810
user- O que contém
- O perfil do autor: 19 campos, como
usernameefollowers_count - Exemplo
py_data_notebook,1840
quoted_status,retweeted_status- O que contém
- O tweet citado ou repostado, no mesmo formato
- Exemplo
- Um objeto de tweet
#Tente de novo em caso de 429 e erros de servidor com um só helper
Envie toda requisição por uma única 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, e um 5xx é uma falha temporária do lado do servidor. O resto deste guia se apoia neste helper call.
import os
import time
import requests
API = "https://api.sorsa.io/v3"
RETRY = {429, 500, 502, 503, 504}
KEY = os.environ.get("SORSA_API_KEY", "YOUR_API_KEY")
session = requests.Session()
session.headers["ApiKey"] = KEY
def call(method, path, **kwargs):
"""Send one request; retry 429 and 5xx up to 4 times."""
for attempt in range(5):
r = session.request(method, API + path, timeout=30, **kwargs)
if r.status_code not in RETRY or attempt == 4:
break
time.sleep(2 ** attempt) # 1, 2, 4, 8 seconds
r.raise_for_status()
return r.json()
Um 401 ou 403 nunca merece nova tentativa: a chave está errada ou ausente, ou suas requisições acabaram, e o campo message do JSON 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, e como o limite 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 de resultados: devolva o next_cursor sem alterações e pare quando uma resposta vier sem ele. A busca e as timelines devolvem até 20 tweets por página, e uma página pode trazer menos enquanto ainda restam resultados, então a contagem de itens nunca indica que você chegou ao fim.
def pages(path, body, max_pages=100):
"""Yield the pages of a POST endpoint until the cursor runs out."""
for _ in range(max_pages):
page = call("POST", path, json=body)
yield page
cursor = page.get("next_cursor")
if not cursor:
break
body = {**body, "next_cursor": cursor}
for page in pages("/search-tweets", {"query": "python lang:en"}):
for tweet in page["tweets"]:
print(tweet["id"], tweet["created_at"])
Mantenha a consulta e o order inalterados enquanto você segue um cursor e trate max_pages como um orçamento, para que uma consulta ampla nunca gaste mais do que você planejou. Os endpoints GET recebem o cursor como parâmetro de consulta: o GET /followers (abre em uma nova aba), com até 200 perfis por página, lê o next_cursor de params. Duas tarefas têm scripts completos próprios: baixar todos os tweets de um usuário retoma uma timeline inteira a partir de um cursor salvo, e o guia para exportar seguidores do Twitter grava listas de seguidores em CSV.
#Busque 100 tweets ou perfis por requisição
Quando você já tem IDs ou nomes de usuário, envie-os em lotes. O POST /tweet-info-bulk (abre em uma nova aba) recebe até 100 IDs ou links de tweets, e o GET /info-batch (abre em uma nova aba) recebe até 100 nomes de usuário, cada um em uma requisição.
def chunks(items, size=100):
for i in range(0, len(items), size):
yield items[i:i + size]
tweets = []
for batch in chunks(tweet_ids):
tweets += call("POST", "/tweet-info-bulk",
json={"tweet_links": batch})["tweets"]
profiles = []
for batch in chunks(usernames):
profiles += call("GET", "/info-batch",
params={"usernames": batch})["users"]
Toda 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. Com o requests, uma lista em params sai como um parâmetro repetido (usernames=a&usernames=b), a forma que o GET /info-batch lê. As respostas em lote não têm cursor: uma resposta traz o lote inteiro, em tweets ou em users.
#Salve os tweets em CSV ou em um DataFrame do pandas
O pandas.json_normalize transforma uma lista de objetos de tweet em um DataFrame em uma só chamada, e o autor aninhado vira colunas com ponto, como user.username.
import pandas as pd
rows = []
for page in pages("/search-tweets", {"query": "python lang:en"},
max_pages=50):
rows += page["tweets"]
df = pd.json_normalize(rows)
df = df[["id", "created_at", "user.username", "full_text",
"likes_count", "retweet_count", "view_count"]]
df["created_at"] = pd.to_datetime(df["created_at"])
df.to_csv("tweets.csv", index=False)
Cinquenta páginas formam 1.000 tweets, o que serve para um notebook. A versão com pandas guarda todas as linhas na memória até o fim, então, para jobs de algumas centenas de milhares de tweets ou mais, passe para a versão com csv: ela grava cada página assim que chega e mantém a memória estável por mais longa que seja a execução.
Bom saber
Os IDs de tweets têm 19 dígitos e o Excel guarda 15, então um CSV aberto com duplo clique transforma os últimos dígitos de cada ID em zeros. Leia o arquivo de volta com
pd.read_csv("tweets.csv", dtype={"id": str})e, no Excel, importe-o por Dados, De Texto/CSV, com a coluna do ID definida como texto.
#Rode até 20 requisições por segundo com asyncio
A Sorsa API permite 20 requisições por segundo por chave em todos os planos, e um único processo Python alcança essa taxa com asyncio quando o trabalho se divide em cadeias independentes. Uma cadeia de cursor não pode ser dividida, porque cada página precisa do cursor da anterior. Então rode várias cadeias lado a lado, uma por conta ou uma por dia de busca, sob um pacer compartilhado que mantém a chave abaixo do limite. Esta versão precisa de pip install httpx.
import asyncio
import os
import time
from datetime import date, timedelta
import httpx
API = "https://api.sorsa.io/v3"
HEADERS = {"ApiKey": os.environ.get("SORSA_API_KEY", "YOUR_API_KEY")}
RETRY = {429, 500, 502, 503, 504}
class Pace:
"""Start at most `rate` requests a second, evenly spaced."""
def __init__(self, rate=20):
self.gap, self.next = 1 / rate, 0.0
self.lock = asyncio.Lock()
async def wait(self):
async with self.lock:
now = time.monotonic()
start = max(self.next, now)
self.next = start + self.gap
await asyncio.sleep(start - now)
async def fetch(client, pace, body):
for attempt in range(5):
await pace.wait()
r = await client.post("/search-tweets", json=body)
if r.status_code not in RETRY or attempt == 4:
break
await asyncio.sleep(2 ** attempt)
r.raise_for_status()
return r.json()
async def lane(client, pace, query, max_pages=100):
"""One cursor chain: its pages run one after another."""
body, tweets = {"query": query}, []
for _ in range(max_pages):
page = await fetch(client, pace, body)
tweets += page["tweets"]
if not page.get("next_cursor"):
break
body = {**body, "next_cursor": page["next_cursor"]}
return tweets
async def main(queries):
pace = Pace(19) # one pacer per key, one start under the limit
async with httpx.AsyncClient(base_url=API, headers=HEADERS,
timeout=30) as client:
return await asyncio.gather(
*(lane(client, pace, q) for q in queries))
day = date(2026, 10, 1)
days = [day + timedelta(n) for n in range(32)] # Oct 1 to Nov 1
queries = [f"python lang:en since:{a} until:{b}"
for a, b in zip(days, days[1:])]
results = asyncio.run(main(queries)) # Jupyter: await main(queries)
O Pace distribui os horários de início de forma uniforme, não importa quantas cadeias esperem. Com 19 por segundo, sobra uma folga de um início, então a oscilação da rede não pode empurrar um segundo além dos 20 da Sorsa API, e a retentativa pega qualquer 429 que escape. Mantenha um pacer por chave: dois processos com um pacer cada podem iniciar o dobro do limite entre eles. A 20 requisições por segundo, lotes de 100 movem até 2.000 tweets por segundo e páginas de busca, 400.
Bom saber
Um notebook Jupyter já roda um event loop, então
asyncio.run(main(queries))falha com um RuntimeError ali. Em uma célula de notebook, escrevaresults = await main(queries).
O planejador transforma um job em requisições, tempo em uma chave e custo na tarifa mais baixa da Sorsa API.
- Requisições
- 5,000
- Mais rápido com uma chave
- cerca de 4 min
- Sorsa API
- a partir de US$ 7
- 100 requisições grátis
- 2,000
20 tweets por requisição
a 20 requisições por segundo
a US$ 1,40 por 1,000 requisições
tweets, sem cartão
Uma busca é uma única cadeia de cursor: divida em janelas de datas e rode como faixas para chegar a esta taxa.
O tempo mostrado é o mais rápido que uma chave permite; uma única cadeia de cursor é mais lenta, então divida trabalhos grandes em faixas.
#Quanto custa um job em Python
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 dos scripts com Tweepy.
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
Autores incluídos em cada tweet
A tarifa por 1.000 requisições vai de US$ 4,90 no Starter a US$ 1,40 no Hyperscale, e um plano a aplica igualmente a todos os endpoints. Consultar o que resta não custa nada: GET /key-usage-info (abre em uma nova aba) nunca desconta das suas requisições.
usage = call("GET", "/key-usage-info")
print(usage["remaining_requests"], "left until", usage["valid_until"])
#Como migrar o código do Tweepy para a Sorsa API
A maioria das chamadas do Tweepy corresponde a um endpoint da Sorsa API, e o código fica mais curto: sem expansões e sem listas de campos, porque cada tweet já traz o autor e as contagens. A tabela une cada método do Client do Tweepy ao seu endpoint; para migrar uma base de código inteira, o guia para migrar da API do Twitter (X) mapeia todos os endpoints da v2 e campos de resposta.
| Método do Tweepy | Endpoint | O que muda |
|---|---|---|
search_recent_tweets | POST /search-tweets | Qualquer data desde 2006, não só os últimos 7 dias |
search_all_tweets | POST /search-tweets | O mesmo endpoint com since: e until: na consulta |
get_users_tweets | POST /user-tweets | Basta o nome de usuário, e não há limite de 3.200 posts |
get_tweets | POST /tweet-info-bulk | 100 IDs ou links por requisição |
get_users | GET /info-batch | 100 nomes de usuário por requisição |
get_users_followers | GET /followers | 200 seguidores por página |
get_retweeters | POST /retweeters (abre em uma nova aba) | O mesmo loop de cursor |
create_tweet | nenhum | Somente leitura: poste pela API oficial do X |
search_recent_tweets- Endpoint
POST /search-tweets- O que muda
- Qualquer data desde 2006, não só os últimos 7 dias
search_all_tweets- Endpoint
POST /search-tweets- O que muda
- O mesmo endpoint com
since:euntil:na consulta
get_users_tweets- Endpoint
POST /user-tweets- O que muda
- Basta o nome de usuário, e não há limite de 3.200 posts
get_tweets- Endpoint
POST /tweet-info-bulk- O que muda
- 100 IDs ou links por requisição
get_users- Endpoint
GET /info-batch- O que muda
- 100 nomes de usuário por requisição
get_users_followers- Endpoint
GET /followers- O que muda
- 200 seguidores por página
get_retweeters- O que muda
- O mesmo loop de cursor
create_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 next_token vira next_cursor. O wait_on_rate_limit=True do Tweepy não tem equivalente para configurar, porque as janelas de 15 minutos do X saíram do código: a retentativa do call e o pacer cuidam de um limite que reinicia a cada segundo. O POST /user-tweets (abre em uma nova aba) 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)).
#Faça hoje a sua primeira busca
Levar dados do X para o Python exige o requests, uma chave e quatro pequenos helpers: call para as retentativas, pages para os cursores, chunks para os lotes de 100 e Pace para 20 requisições por segundo. Comece pela Sorsa API, a API do Twitter feita para ler dados públicos do X em escala: 20 tweets por requisição de busca a partir de US$ 0,07 por 1.000, o autor dentro de cada tweet e 100 requisições grátis que não pedem cartão e nunca expiram. As mesmas chamadas em JavaScript estão no guia da API do Twitter em Node.js, e os workflows no-code chegam a elas pela API do Twitter no n8n.
#Perguntas frequentes
Existe uma API do Twitter grátis para Python?
A API oficial do X não tem plano gratuito para novos desenvolvedores: o Tweepy e o xdk são grátis, mas cada leitura gasta créditos pré-pagos a US$ 0,005 por post. A Sorsa API dá a toda conta 100 requisições grátis que nunca expiram e não pedem cartão. No Python, elas coletam 2.000 tweets por busca, ou até 10 mil tweets em lotes de 100.
Qual API do Twitter devo usar com Python?
Para ler dados do X em Python, use a Sorsa API: uma chave de API com requests no lugar do OAuth, 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.
Posso obter dados do Twitter em Python sem conta de desenvolvedor?
Sim. A Sorsa API precisa só da própria chave: cadastre-se, defina o header ApiKey e chame qualquer um dos 45 endpoints dela a partir do Python, sem conta de desenvolvedor do X, sem análise de app e sem OAuth. Bibliotecas que leem o X fazendo login com o seu nome de usuário ou os cookies do navegador colocam essa conta em risco. Os prós e contras são comparados em API do Twitter sem conta de desenvolvedor.
Posso postar tweets em Python 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 Python, use a API oficial do X com um token de usuário OAuth, como o create_tweet do Tweepy, a US$ 0,015 por post, e deixe a Sorsa API para as leituras.
Existe um SDK Python ou pacote pip para a Sorsa API?
Nenhum pacote é necessário: a Sorsa API é REST simples com JSON, então o requests ou o httpx cobrem todos os endpoints, e os helpers deste guia cabem em cerca de 40 linhas. A referência OpenAPI ao vivo em api.sorsa.io/v3/swagger.json lista os 45 endpoints, então um gerador como o openapi-generator pode criar um cliente tipado, se você quiser um.
#Como verificamos
Escrevemos cada requisição deste guia a partir da referência OpenAPI ao vivo da Sorsa API (45 endpoints) e da documentação dela sobre rate limits, paginação e erros, lidas em 2 de novembro de 2026. Não tivemos uma chave da Sorsa API nesta rodada, então a resposta exibida usa valores de demonstração, e rodamos cada bloco Python contra um mock local que responde nos formatos documentados: as retentativas, o loop de cursor, os lotes de 100, as exportações em pandas e csv e o pacer do asyncio: com 19 inícios por segundo ele rodou 31 cadeias sem nenhum 429, enquanto com exatamente 20 a oscilação de tempo mandou uma ou duas requisições para a retentativa. No mesmo dia lemos os preços, os passos de acesso e os limites de busca e timeline da API oficial do X em docs.x.com, o aviso do X sobre a cobrança de expansões e as páginas do Tweepy e do xdk no PyPI.
Fontes
- X API pay-per-usage pricing (docs.x.com) (abre em uma nova aba)
- Getting Access (docs.x.com) (abre em uma nova aba)
- Tools and libraries: the official Python SDK (docs.x.com) (abre em uma nova aba)
- X API PPU: expansions update (X Developers, October 23, 2026) (abre em uma nova aba)
- Search Posts (docs.x.com) (abre em uma nova aba)
- tweepy on PyPI (abre em uma nova aba)
- xdk on PyPI (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)