API de Twitter en Python: tutorial 2026 con código funcional
Para obtener datos de Twitter/X en Python, llama a una API de datos de X con requests o con un cliente como Tweepy, envía tu clave en cada solicitud y sigue el cursor página por página; la API oficial de X cobra $0.005 por post.
Respuesta corta: Tweepy y el SDK de X, xdk, son librerías gratuitas para la API oficial de X, que exige una cuenta de desarrollador, un bearer token y créditos prepagados: $0.005 por post, y $0.015 con su autor a partir del 22 de noviembre de 2026. Para leer datos públicos, usa Sorsa API con requests: una sola clave, 20 tweets por solicitud desde $0.07 por 1,000; las 100 solicitudes gratis cubren 2,000.
- Endpoint
POST /search-tweets- Tamaño de página
- 20 tweets por solicitud
- Precio
- desde $0.07 por 1,000 tweets
#¿Tweepy sigue funcionando en 2026?
Sí. Tweepy 4.17.0 salió el 2 de julio de 2026, y X publica ahora su propio SDK de Python, xdk, en la versión 0.10.6. Los dos son clientes de la API oficial de X, así que cada llamada que hacen necesita una cuenta de desarrollador, el bearer token de una app y créditos prepagados.
Los créditos son lo que dejan fuera los tutoriales más viejos. Los desarrolladores nuevos no tienen nivel gratuito, y X cobra cada objeto que devuelve una respuesta: $0.005 por post y $0.010 por usuario, hasta 3 millones de lecturas de posts al mes (tabla de precios de X (se abre en una pestaña nueva)). A partir del 22 de noviembre de 2026, los autores y otros objetos que llegan mediante expansiones se cobran a las mismas tarifas (aviso de X del 23 de octubre (se abre en una pestaña nueva)), así que la búsqueda habitual con Tweepy paga por cada autor que adjunta:
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
La librería en sí es gratuita y de código abierto. Su página de inicio aún dice que la API que hay detrás tiene niveles gratuitos y de pago, algo que ya no vale para una cuenta nueva. Todas las tarifas, topes y fechas están en precios de la API de X.
Lo que exige la vía oficial antes de la primera llamada
- Una cuenta de desarrollador en console.x.com, abierta con una cuenta de X y con el Acuerdo para Desarrolladores de X aceptado.
- Una app, cuyo bearer token X muestra una sola vez (guía de acceso de X (se abre en una pestaña nueva)).
- Créditos comprados por adelantado y gastados por cada post y cada usuario que se devuelve.
- Tokens de usuario de OAuth para todo lo que publique, dé me gusta o siga cuentas.
Leer posts y perfiles públicos no necesita nada de esto. Una API de datos de Twitter/X con su propia clave responde por HTTPS simple, y requests es el único cliente que necesita un script.
#Obtén tweets en Python con una sola clave de API
Para leer datos de X desde Python, usa Sorsa API, una API y scraper de Twitter (X): un solo encabezado ApiKey en lugar de OAuth, 20 solicitudes por segundo en todos los planes, también en las cuentas gratuitas, y resultados de búsqueda desde $0.07 por 1,000 tweets. Devuelve posts, perfiles y seguidores públicos como JSON, con el perfil completo del autor dentro de cada tweet. Nunca pide tu inicio de sesión de X, así que tu cuenta de X no corre ningún riesgo.
Configura la clave y envía la primera solicitud
-
Obtén una clave. Regístrate y copia tu clave de API desde el panel. Las primeras 100 solicitudes son gratis, no piden tarjeta y nunca vencen.
-
Instala requests y guarda la clave. Mantén la clave en una variable de entorno en lugar de ponerla en el código, y llama a la API desde un script o un servidor, nunca desde JavaScript del navegador.
Terminal pip install requests export SORSA_API_KEY="your_key" # PowerShell: $env:SORSA_API_KEY="your_key" -
Busca tweets. Envía una consulta con la sintaxis de búsqueda de X a
POST /search-tweets(se abre en una pestaña nueva) con la clave en el encabezadoApiKey.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]) -
Lee la página. Cada respuesta trae hasta 20 tweets y un
next_cursorpara la página siguiente. Los valores de abajo son datos de demostración con la forma documentada.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 llega con sus conteos y su autor, así que un script no define expansiones ni listas de campos. Estos son los campos que lee la mayoría de los scripts:
| Campo | Qué contiene | Ejemplo |
|---|---|---|
id | El ID de 19 dígitos del tweet, enviado como string: déjalo como string | 1985432176540981234 |
full_text | El texto completo del post | Loaded a week of #python posts |
created_at | Cuándo se publicó, ISO 8601 en UTC | 2026-11-01T18:42:07Z |
likes_count, retweet_count, reply_count, quote_count | Conteos de interacción | 42 |
view_count, bookmark_count | Vistas y guardados | 3810 |
user | El perfil del autor: 19 campos como username y followers_count | py_data_notebook, 1840 |
quoted_status, retweeted_status | El tweet citado o republicado, con la misma forma | Un objeto de tweet |
id- Qué contiene
- El ID de 19 dígitos del tweet, enviado como string: déjalo como string
- Ejemplo
1985432176540981234
full_text- Qué contiene
- El texto completo del post
- Ejemplo
- Loaded a week of #python posts
created_at- Qué contiene
- Cuándo se publicó, ISO 8601 en UTC
- Ejemplo
2026-11-01T18:42:07Z
likes_count,retweet_count,reply_count,quote_count- Qué contiene
- Conteos de interacción
- Ejemplo
42
view_count,bookmark_count- Qué contiene
- Vistas y guardados
- Ejemplo
3810
user- Qué contiene
- El perfil del autor: 19 campos como
usernameyfollowers_count - Ejemplo
py_data_notebook,1840
quoted_status,retweeted_status- Qué contiene
- El tweet citado o republicado, con la misma forma
- Ejemplo
- Un objeto de tweet
#Reintenta los 429 y los errores del servidor en una sola función
Envía cada solicitud por una sola función que reintente solo lo que vale la pena reintentar. Un 429 significa que más de 20 solicitudes empezaron dentro de un mismo segundo con tu clave, y un 5xx es una falla temporal del lado del servidor. El resto de esta guía se apoya en esta función 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()
Un 401 o un 403 nunca merece un reintento: la clave es incorrecta o falta, o sus solicitudes se agotaron, y el campo message del JSON dice cuál es el caso. Las solicitudes fallidas nunca se cobran, así que un reintento no cuesta nada. Todos los endpoints de Sorsa API solo leen, así que reintentar un POST como /search-tweets no puede crear nada dos veces, y como el límite se reinicia cada segundo, la primera espera de un segundo despeja un 429.
#Recorre los resultados con next_cursor
Un cursor es una clave opaca hacia la página siguiente de resultados: devuelve next_cursor sin cambios y detente cuando una respuesta llegue sin él. La búsqueda y los timelines devuelven hasta 20 tweets por página, y una página puede traer menos mientras quedan más, así que la cantidad de elementos nunca te dice que llegaste al final.
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"])
Mantén sin cambios la consulta y order mientras sigues un mismo cursor, y trata max_pages como un presupuesto, para que una consulta amplia nunca gaste más de lo que planeaste. Los endpoints GET reciben el cursor como parámetro de consulta: GET /followers (se abre en una pestaña nueva), con hasta 200 perfiles por página, lee next_cursor desde params. Dos tareas tienen scripts completos propios: descargar todos los tweets de un usuario retoma un timeline entero desde un cursor guardado, y la guía para exportar seguidores de Twitter escribe listas de seguidores a CSV.
#Obtén 100 tweets o perfiles por solicitud
Cuando ya tienes IDs o nombres de usuario, envíalos en lotes. POST /tweet-info-bulk (se abre en una pestaña nueva) acepta hasta 100 IDs o enlaces de tweets, y GET /info-batch (se abre en una pestaña nueva) acepta hasta 100 nombres de usuario, cada uno en una sola solicitud.
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"]
Cada solicitud cuesta lo mismo sin importar lo que devuelva, así que un lote de 100 tweets cuesta lo que cuesta un tweet: desde $0.014 por 1,000 tweets, más de 250 veces menos que los $5.00 de la API oficial de X. Con requests, una lista en params sale como un parámetro repetido (usernames=a&usernames=b), la forma que lee GET /info-batch. Las respuestas por lote no traen cursor: una respuesta contiene el lote completo, en tweets o en users.
#Guarda los tweets en CSV o en un DataFrame de pandas
pandas.json_normalize convierte una lista de objetos de tweet en un DataFrame con una sola llamada, y el autor anidado pasa a ser columnas con puntos, 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)
Cincuenta páginas dan 1,000 tweets, lo que va bien para un notebook. La versión con pandas mantiene todas las filas en memoria hasta el final, así que para trabajos de unos cientos de miles de tweets o más, cambia a la versión con csv: escribe cada página según llega y mantiene la memoria estable sin importar cuánto dure la ejecución.
Para tener en cuenta
Los IDs de tweets tienen 19 dígitos y Excel conserva 15, así que un CSV abierto con doble clic convierte en ceros los últimos dígitos de cada ID. Lee el archivo de vuelta con
pd.read_csv("tweets.csv", dtype={"id": str}), y en Excel impórtalo con Datos, Desde texto/CSV, con la columna del ID definida como texto.
#Ejecuta hasta 20 solicitudes por segundo con asyncio
Sorsa API permite 20 solicitudes por segundo por clave en todos los planes, y un solo proceso de Python alcanza esa tasa con asyncio en cuanto el trabajo se divide en cadenas independientes. Una cadena de cursor no se puede dividir, porque cada página necesita el cursor de la anterior. Así que corre varias cadenas en paralelo, una por cuenta o una por día de una búsqueda, bajo un regulador compartido que mantiene la clave por debajo de su límite. Esta versión necesita 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)
Pace reparte los instantes de inicio de forma pareja, sin importar cuántas cadenas esperen. Con 19 por segundo deja un inicio de margen, así que la variación de la red no puede empujar un segundo por encima de los 20 de Sorsa API, y el reintento atrapa cualquier 429 que se cuele. Mantén un regulador por clave: dos procesos con un regulador cada uno pueden iniciar el doble del límite entre los dos. A 20 solicitudes por segundo, los lotes de 100 mueven hasta 2,000 tweets por segundo y las páginas de búsqueda, 400.
Para tener en cuenta
Un notebook de Jupyter ya ejecuta un bucle de eventos, así que
asyncio.run(main(queries))se detiene ahí con un RuntimeError. En una celda de notebook, escribe en su lugarresults = await main(queries).
El planificador convierte un trabajo en solicitudes, tiempo con una sola clave y costo con la tarifa más baja de Sorsa API.
- Solicitudes
- 5,000
- Lo más rápido con una sola clave
- unos 4 min
- Sorsa API
- desde $7
- 100 solicitudes gratis
- 2,000
20 tweets por solicitud
a 20 solicitudes por segundo
a $1.40 por cada 1,000 solicitudes
tweets, sin tarjeta
Una búsqueda es una sola cadena de cursores: divídela en ventanas de fechas y córrelas como carriles para llegar a esta tasa.
El tiempo mostrado es el más rápido que permite una clave; una sola cadena de cursores va más lenta, así que divide las tareas grandes en carriles.
#Cuánto cuesta un trabajo en Python
Sorsa API cobra por solicitud, sin importar lo que devuelva el endpoint: los resultados de búsqueda cuestan desde $0.07 por 1,000 tweets, los lotes desde $0.014, y las solicitudes fallidas nunca se cobran. La API oficial de X cobra en cambio por objeto: $5.00 por 1,000 posts, y a partir del 22 de noviembre de 2026 hasta $15.00 por 1,000 cuando el código expande el autor de cada post, como hacen la mayoría de los scripts con Tweepy.
API oficial de X
$5,000
para 1M tweets
$0.005 por post
Sorsa API
desde$70
para 1M tweets
desde $0.07 por 1,000
Autores incluidos en cada tweet
La tarifa por 1,000 solicitudes va de $4.90 en Starter a $1.40 en Hyperscale, y un plan la aplica por igual a todos los endpoints. Consultar cuánto te queda no cuesta nada: GET /key-usage-info (se abre en una pestaña nueva) nunca descuenta de tus solicitudes.
usage = call("GET", "/key-usage-info")
print(usage["remaining_requests"], "left until", usage["valid_until"])
#Cómo pasar el código de Tweepy a Sorsa API
La mayoría de las llamadas de Tweepy se corresponden con un endpoint de Sorsa API, y el código se acorta: sin expansiones y sin listas de campos, porque cada tweet ya trae su autor y sus conteos. La tabla empareja cada método de Client de Tweepy con su endpoint; para pasar un código base completo, la guía para migrar desde la API de Twitter/X mapea cada endpoint v2 y cada campo de respuesta.
| Método de Tweepy | Endpoint | Qué cambia |
|---|---|---|
search_recent_tweets | POST /search-tweets | Cualquier fecha desde 2006, no solo los últimos 7 días |
search_all_tweets | POST /search-tweets | El mismo endpoint con since: y until: en la consulta |
get_users_tweets | POST /user-tweets | Basta con un nombre de usuario, y no hay tope de 3,200 posts |
get_tweets | POST /tweet-info-bulk | 100 IDs o enlaces por solicitud |
get_users | GET /info-batch | 100 nombres de usuario por solicitud |
get_users_followers | GET /followers | 200 seguidores por página |
get_retweeters | POST /retweeters (se abre en una pestaña nueva) | El mismo bucle de cursor |
create_tweet | ninguno | Solo lectura: publica con la API oficial de X |
search_recent_tweets- Endpoint
POST /search-tweets- Qué cambia
- Cualquier fecha desde 2006, no solo los últimos 7 días
search_all_tweets- Endpoint
POST /search-tweets- Qué cambia
- El mismo endpoint con
since:yuntil:en la consulta
get_users_tweets- Endpoint
POST /user-tweets- Qué cambia
- Basta con un nombre de usuario, y no hay tope de 3,200 posts
get_tweets- Endpoint
POST /tweet-info-bulk- Qué cambia
- 100 IDs o enlaces por solicitud
get_users- Endpoint
GET /info-batch- Qué cambia
- 100 nombres de usuario por solicitud
get_users_followers- Endpoint
GET /followers- Qué cambia
- 200 seguidores por página
get_retweeters- Qué cambia
- El mismo bucle de cursor
create_tweet- Endpoint
- ninguno
- Qué cambia
- Solo lectura: publica con la API oficial de X
Los nombres de los campos también cambian: text pasa a ser full_text, public_metrics["like_count"] pasa a ser likes_count, author_id con includes["users"] pasa a ser el objeto user, y next_token pasa a ser next_cursor. El wait_on_rate_limit=True de Tweepy no tiene equivalente que configurar, porque las ventanas de 15 minutos de X desaparecen del código: el reintento de call y el regulador manejan un límite que se reinicia cada segundo. POST /user-tweets (se abre en una pestaña nueva) también llega más allá de los 3,200 posts más recientes, donde se detiene el timeline oficial (documentación de timelines de X (se abre en una pestaña nueva)).
#Haz hoy tu primera búsqueda
Llevar datos de X a Python necesita requests, una clave y cuatro funciones pequeñas: call para los reintentos, pages para los cursores, chunks para los lotes de 100 y Pace para 20 solicitudes por segundo. Empieza con Sorsa API, la API de Twitter creada para leer datos públicos de X a gran escala: 20 tweets por solicitud de búsqueda desde $0.07 por 1,000, el autor dentro de cada tweet y 100 solicitudes gratis que no piden tarjeta y nunca vencen. Las mismas llamadas en JavaScript están en la guía de la API de Twitter en Node.js, y los flujos sin código llegan a ellas mediante la API de Twitter en n8n.
#Preguntas frecuentes
¿Hay una API de Twitter gratuita para Python?
La API oficial de X no tiene nivel gratuito para desarrolladores nuevos: Tweepy y xdk son gratis, pero cada lectura gasta créditos prepagados a $0.005 por post. Sorsa API empieza cada cuenta con 100 solicitudes gratis que nunca vencen y no piden tarjeta. Desde Python recolectan 2,000 tweets por búsqueda, o hasta 10,000 tweets en lotes de 100.
¿Qué API de Twitter debería usar con Python?
Para leer datos de X desde Python, usa Sorsa API: una clave de API con requests en lugar de OAuth, resultados de búsqueda desde $0.07 por 1,000 tweets y lotes desde $0.014, 20 solicitudes por segundo en todos los planes, búsqueda desde 2006 y ningún inicio de sesión de X, así que tu cuenta nunca corre riesgo. Para publicar también, combínala con la API oficial de X.
¿Puedo obtener datos de Twitter en Python sin una cuenta de desarrollador?
Sí. Sorsa API solo necesita su propia clave: te registras, defines el encabezado ApiKey y llamas a cualquiera de sus 45 endpoints desde Python, sin cuenta de desarrollador de X, sin revisión de app y sin OAuth. Las librerías que leen X iniciando sesión con tu nombre de usuario o con las cookies del navegador ponen esa cuenta en riesgo. Los pros y contras se comparan en API de Twitter sin cuenta de desarrollador.
¿Puedo publicar tweets desde Python con Sorsa API?
No. Sorsa API es de solo lectura por diseño: lee posts, perfiles y seguidores públicos y nunca publica, da me gusta, sigue cuentas ni envía mensajes, así que una clave no puede actuar sobre ninguna cuenta de X. Para publicar desde Python, usa la API oficial de X con un token de usuario de OAuth, como el create_tweet de Tweepy a $0.015 por post, y deja Sorsa API para las lecturas.
¿Hay un SDK de Python o un paquete de pip para Sorsa API?
No hace falta ningún paquete: Sorsa API es REST simple con JSON, así que requests o httpx cubren cada endpoint, y las funciones de esta guía caben en unas 40 líneas. La referencia OpenAPI vigente en api.sorsa.io/v3/swagger.json lista los 45 endpoints, así que un generador como openapi-generator puede construir un cliente tipado si quieres uno.
#Cómo lo comprobamos
Escribimos cada solicitud de esta guía a partir de la referencia OpenAPI vigente de Sorsa API (45 endpoints) y de su documentación sobre límites de tasa, paginación y errores, leída el 2 de noviembre de 2026. No teníamos una clave de Sorsa API para esta revisión, así que la respuesta mostrada usa valores de demostración, y ejecutamos cada bloque de Python contra un mock local que responde con las formas documentadas: los reintentos, el bucle de cursor, los lotes de 100, las exportaciones con pandas y csv, y el regulador de asyncio: con 19 inicios por segundo corrió 31 cadenas sin ningún 429, mientras que con exactamente 20 la variación de los tiempos mandó una o dos solicitudes al reintento. El mismo día leímos en docs.x.com los precios, los pasos de acceso y los límites de búsqueda y timelines de la API oficial de X, el aviso de X sobre la facturación de expansiones y las páginas de PyPI de Tweepy y xdk.
Fuentes
- X API pay-per-usage pricing (docs.x.com) (se abre en una pestaña nueva)
- Getting Access (docs.x.com) (se abre en una pestaña nueva)
- Tools and libraries: the official Python SDK (docs.x.com) (se abre en una pestaña nueva)
- X API PPU: expansions update (X Developers, October 23, 2026) (se abre en una pestaña nueva)
- Search Posts (docs.x.com) (se abre en una pestaña nueva)
- tweepy on PyPI (se abre en una pestaña nueva)
- xdk on PyPI (se abre en una pestaña nueva)
- Sorsa API reference, OpenAPI
- Sorsa API rate limits (se abre en una pestaña nueva)
- Sorsa API pagination (se abre en una pestaña nueva)