Cómo migrar desde la API de Twitter/X
Migrar desde la API oficial de X requiere cinco cambios de código: asignar cada endpoint v2 a su nueva ruta, cambiar OAuth por una clave de API, quitar campos y expansiones, reescribir el análisis del JSON y cambiar el token de paginación.
Respuesta corta: Lo que conviene migrar son las lecturas: la API oficial de X cobra $0.005 por post y $0.010 por usuario, y desde el 22 de noviembre de 2026 cada autor devuelto con un post suma $0.010. Para migrarlas, usa Sorsa API: un solo encabezado ApiKey, 20 tweets por solicitud de búsqueda desde $0.07 por 1,000 y 100 solicitudes gratis para probar el mapa.
- Endpoint
POST /search-tweets- Tamaño de página
- 20 tweets por solicitud
- Precio
- desde $0.07 por 1,000
#Qué cambió en la API oficial de X desde 2023
El código escrito para la API oficial de X en su mayor parte sigue funcionando; lo que cambió desde 2023 es qué puede llamar, qué recibe de vuelta y cuánto cuesta cada respuesta. La lista con fechas de abajo sale del changelog (se abre en una pestaña nueva) de X y de los avisos de su foro de desarrolladores, con la parte de una integración que alcanza cada cambio.
| Fecha | Cambio en la API oficial de X | Qué le hace a tu código |
|---|---|---|
| 30 mar 2023 | Los niveles de pago sustituyen al acceso gratuito; Free pasa a ser solo de escritura | Las lecturas en una app gratuita devuelven errores |
| 26 jun 2023 | Se eliminan los endpoints de follows de Basic y Pro | Las llamadas de seguidores y seguidos se detienen en esos planes |
| 28 sep 2023 | search/tweets y users/search de la v1.1 quedan obsoletos | El código de búsqueda de la v1.1 necesita un endpoint nuevo |
| 19 ene 2026 | min_likes, min_replies, min_reposts se eliminan de la búsqueda | Las consultas que los usaban tuvieron que quitarlos hasta el 4 de mayo |
| 6 feb 2026 | El pago por uso pasa a ser el modelo; créditos comprados por adelantado | Las llamadas se bloquean cuando el saldo llega a cero |
| 23 feb 2026 | Las respuestas programáticas solo se permiten cuando el autor te invoca | Los bots de respuestas son rechazados |
| 20 abr 2026 | Seguir, dar me gusta y citar posts se quitan del autoservicio | Esas llamadas de escritura dejan de funcionar |
| 4 may 2026 | La búsqueda pasa a un índice nuevo; los retweets salen de los resultados por palabra clave | La misma consulta devuelve menos posts |
| 1 jun y 1 sep 2026 | Los planes heredados Basic, y luego Pro, pasan al pago por uso | Un tope mensual se convierte en una factura por recurso |
| 22 nov 2026 | Se cobran las expansiones: $0.010 por autor, $0.005 por elemento multimedia | expansions=author_id puede triplicar el costo de un post |
30 mar 2023
- Cambio en la API oficial de X
- Los niveles de pago sustituyen al acceso gratuito; Free pasa a ser solo de escritura
- Qué le hace a tu código
- Las lecturas en una app gratuita devuelven errores
26 jun 2023
- Cambio en la API oficial de X
- Se eliminan los endpoints de follows de Basic y Pro
- Qué le hace a tu código
- Las llamadas de seguidores y seguidos se detienen en esos planes
28 sep 2023
- Cambio en la API oficial de X
search/tweetsyusers/searchde la v1.1 quedan obsoletos- Qué le hace a tu código
- El código de búsqueda de la v1.1 necesita un endpoint nuevo
19 ene 2026
- Cambio en la API oficial de X
min_likes,min_replies,min_repostsse eliminan de la búsqueda- Qué le hace a tu código
- Las consultas que los usaban tuvieron que quitarlos hasta el 4 de mayo
6 feb 2026
- Cambio en la API oficial de X
- El pago por uso pasa a ser el modelo; créditos comprados por adelantado
- Qué le hace a tu código
- Las llamadas se bloquean cuando el saldo llega a cero
23 feb 2026
- Cambio en la API oficial de X
- Las respuestas programáticas solo se permiten cuando el autor te invoca
- Qué le hace a tu código
- Los bots de respuestas son rechazados
20 abr 2026
- Cambio en la API oficial de X
- Seguir, dar me gusta y citar posts se quitan del autoservicio
- Qué le hace a tu código
- Esas llamadas de escritura dejan de funcionar
4 may 2026
- Cambio en la API oficial de X
- La búsqueda pasa a un índice nuevo; los retweets salen de los resultados por palabra clave
- Qué le hace a tu código
- La misma consulta devuelve menos posts
1 jun y 1 sep 2026
- Cambio en la API oficial de X
- Los planes heredados Basic, y luego Pro, pasan al pago por uso
- Qué le hace a tu código
- Un tope mensual se convierte en una factura por recurso
22 nov 2026
- Cambio en la API oficial de X
- Se cobran las expansiones: $0.010 por autor, $0.005 por elemento multimedia
- Qué le hace a tu código
expansions=author_idpuede triplicar el costo de un post
Dos de estos cambios alteraron los resultados sin cambiar una sola línea de código. Desde que el nuevo índice de búsqueda (se abre en una pestaña nueva) entró en funcionamiento el 4 de mayo, una búsqueda por palabra clave que funcionaba bien en abril devuelve resultados sin retweets. Y desde el 22 de noviembre, la misma solicitud con expansions=author_id que costaba $5.00 por 1,000 posts cuesta hasta $15.00, porque el aviso de expansiones (se abre en una pestaña nueva) de X cobra cada autor devuelto como una lectura de usuario.
Los planes también cambiaron bajo el código en ejecución. X terminó las suscripciones de Basic heredado (se abre en una pestaña nueva) en sus fechas de transición posteriores al 1 de junio de 2026, y las de Pro heredado (se abre en una pestaña nueva) después del 1 de septiembre; a una cuenta mensual de Pro se le cargó un crédito de $5,000 en la transición, con la recarga automática activada. Ahora cada lectura descuenta créditos a $0.005 por post y $0.010 por usuario o seguidor, hasta 3M de lecturas de posts al mes, y la tarifa completa está en nuestro desglose de los precios de la API de X.
#Qué pasa a Sorsa API y qué se queda en X
Toda lectura de datos públicos puede migrarse: búsqueda, timelines, consultas de posts y usuarios, seguidores, listas, citas, quienes hicieron retweet, respuestas, Spaces, comunidades y tendencias. Sorsa API, una API y scraper de Twitter (X) en producción desde 2022, con más de 6 mil millones de solicitudes procesadas, devuelve todo eso con una sola clave, sin OAuth y, como explica la guía de la API de Twitter sin cuenta de desarrollador, sin cuenta de desarrollador de X. Es de solo lectura por diseño, así que las llamadas que escriben se quedan donde están.
Deja estas en la API oficial de X:
- Publicar, responder, borrar y subir contenido multimedia.
- Mensajes directos, y me gusta, follows, marcadores y bloqueos, ya sean acciones o listas de la actividad de tu propia cuenta.
- Quién dio me gusta a un post, y cualquier cosa de cuentas protegidas.
- Filtered stream, firehose, webhooks y la API X Activity: Sorsa API se consulta por sondeo, no recibe envíos (el patrón de sondeo está más abajo).
- Conteos de posts, Community Notes y la API de Ads.
Tu cuenta de X no participa en las lecturas, porque Sorsa API nunca pide tu usuario ni tus cookies. Después de la separación, la API oficial de X solo cobra las escrituras, por solicitud: $0.015 por post creado y $0.20 por un post con un enlace.
Las lecturas son donde se va el dinero. En lecturas de búsqueda y de timelines, 1M de tweets cuestan $5,000 en la API oficial de X antes de cobrar los autores, y desde $70 en Sorsa API, que devuelve el autor de cada tweet dentro de él sin costo adicional. Las consultas por ID rinden más: POST /tweet-info-bulk devuelve 100 tweets por solicitud, desde $0.014 por 1,000.
API oficial de X
$5,000
para 1M tweets
$0.005 por post, sin autores
Sorsa API
desde$70
para 1M tweets
desde $0.07 por 1,000, autor incluido
#Migra a Sorsa API en seis pasos
Seis pasos llevan una integración v2 desde el inventario hasta el cambio definitivo. Los primeros cinco son ediciones de tu código; el último dura unos días, mientras ambas API responden las mismas lecturas.
-
Lista cada llamada que hace tu código. Busca en el código
api.x.com/2,api.twitter.com/2y los nombres de método de tu cliente, comosearch_recent_tweetsde Tweepy oposts.search_recentde xdk. Exporta el uso del último mes por recurso en la Developer Console de X y marca cada llamada como lectura o escritura. -
Consigue una clave y envía una solicitud. Regístrate, copia la clave del panel y llama al endpoint que reemplaza tu lectura más usada.
Terminal curl -X POST "https://api.sorsa.io/v3/search-tweets" \ -H "ApiKey: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "from:mira_moves_reads", "order": "latest"}'Respuesta200 OK { "tweets": [ { "id": "1852331946812457001", "full_text": "Read path moved. Writes stay where they were.", "created_at": "2026-10-30T14:05:12Z", "lang": "en", "likes_count": 41, "retweet_count": 6, "reply_count": 3, "quote_count": 1, "view_count": 2380, "conversation_id_str": "1852331946812457001", "is_reply": false, "user": { "id": "1592041833471827968", "username": "mira_moves_reads", "display_name": "Mira Moves Reads", "followers_count": 912, "verified": false } } ], "next_cursor": "DAADDAABCgABGbHk7m4WYAEKAAIZsd" } -
Asigna cada lectura a su endpoint. La tabla y el conversor de la sección siguiente emparejan cada lectura v2 con su ruta y sus parámetros en Sorsa API.
-
Cambia OAuth, campos y expansiones por un solo encabezado. En las llamadas de lectura, elimina el bearer token o la firma OAuth 1.0a y envía
ApiKey: YOUR_API_KEYen su lugar. Quitatweet.fields,user.fieldsyexpansions: todos los campos y el autor llegan por defecto. -
Reescribe el análisis y la paginación. Lee
tweetsousersdonde leíasdataeincludes, renombra los campos que difieren y envíanext_cursorde vuelta hasta que llegue vacío. -
Ejecuta en paralelo y luego cambia. Envía una copia de tus lecturas a Sorsa API durante unos días, compara los resultados, mueve el tráfico por etapas y conserva la clave de X para las escrituras.
#Asigna cada endpoint de la API v2 de X a Sorsa API
Cada lectura pública que una integración probablemente llame tiene un endpoint de Sorsa API. La tabla empareja cada ruta v2, tal como está escrita en la especificación OpenAPI (se abre en una pestaña nueva) de X, con su reemplazo y con cuánto devuelve una solicitud; Sorsa API cobra por solicitud, no por elemento, así que la última columna fija el precio.
| Llamada de la API v2 de X | Endpoint de Sorsa API | Una solicitud devuelve |
|---|---|---|
GET /2/tweets/search/recent | POST /search-tweets (se abre en una pestaña nueva) | unos 20 tweets |
GET /2/tweets/search/all | POST /search-tweets con since: y until: | unos 20 tweets |
GET /2/tweets/:id | POST /tweet-info (se abre en una pestaña nueva) | 1 tweet |
GET /2/tweets (por IDs) | POST /tweet-info-bulk (se abre en una pestaña nueva) | hasta 100 tweets |
GET /2/users/:id/tweets | POST /user-tweets (se abre en una pestaña nueva) | unos 20 tweets |
GET /2/users/:id/mentions | POST /mentions (se abre en una pestaña nueva) | unos 20 tweets |
GET /2/tweets/:id/quote_tweets | POST /quotes (se abre en una pestaña nueva) | unos 20 tweets |
GET /2/tweets/:id/retweeted_by | POST /retweeters (se abre en una pestaña nueva) | una página de perfiles |
Búsqueda con conversation_id: | POST /comments (se abre en una pestaña nueva) | unas 20 respuestas |
GET /2/users/by/username/:username | GET /info (se abre en una pestaña nueva) | 1 perfil |
GET /2/users y GET /2/users/by | GET /info-batch (se abre en una pestaña nueva) | hasta 100 perfiles |
GET /2/users/:id/followers | GET /followers (se abre en una pestaña nueva) | hasta 200 perfiles |
GET /2/users/:id/following | GET /follows (se abre en una pestaña nueva) | hasta 200 perfiles |
GET /2/users/search | POST /search-users (se abre en una pestaña nueva) | una página de perfiles |
GET /2/lists/:id/members | GET /list-members (se abre en una pestaña nueva) | una página de perfiles |
GET /2/lists/:id/followers | GET /list-followers (se abre en una pestaña nueva) | una página de perfiles |
GET /2/lists/:id/tweets | GET /list-tweets (se abre en una pestaña nueva) | unos 20 tweets |
GET /2/spaces/:id | GET /spaces (se abre en una pestaña nueva) | 1 Space |
GET /2/communities/:id | POST /community-info (se abre en una pestaña nueva) | 1 comunidad |
GET /2/communities/search | POST /search-communities (se abre en una pestaña nueva) | una página de comunidades |
GET /2/trends/by/woeid/:woeid | GET /trends (se abre en una pestaña nueva) | las tendencias de 1 ubicación |
GET /2/usage/tweets | GET /key-usage-info (se abre en una pestaña nueva) | tu saldo, sin costo |
GET /2/tweets/search/recent- Endpoint de Sorsa API
POST /search-tweets(se abre en una pestaña nueva)- Una solicitud devuelve
- unos 20 tweets
GET /2/tweets/search/all- Endpoint de Sorsa API
POST /search-tweetsconsince:yuntil:- Una solicitud devuelve
- unos 20 tweets
GET /2/tweets/:id- Endpoint de Sorsa API
POST /tweet-info(se abre en una pestaña nueva)- Una solicitud devuelve
- 1 tweet
GET /2/tweets(por IDs)- Endpoint de Sorsa API
POST /tweet-info-bulk(se abre en una pestaña nueva)- Una solicitud devuelve
- hasta 100 tweets
GET /2/users/:id/tweets- Endpoint de Sorsa API
POST /user-tweets(se abre en una pestaña nueva)- Una solicitud devuelve
- unos 20 tweets
GET /2/users/:id/mentions- Endpoint de Sorsa API
POST /mentions(se abre en una pestaña nueva)- Una solicitud devuelve
- unos 20 tweets
GET /2/tweets/:id/quote_tweets- Endpoint de Sorsa API
POST /quotes(se abre en una pestaña nueva)- Una solicitud devuelve
- unos 20 tweets
GET /2/tweets/:id/retweeted_by- Endpoint de Sorsa API
POST /retweeters(se abre en una pestaña nueva)- Una solicitud devuelve
- una página de perfiles
Búsqueda con
conversation_id:- Endpoint de Sorsa API
POST /comments(se abre en una pestaña nueva)- Una solicitud devuelve
- unas 20 respuestas
GET /2/users/by/username/:username- Endpoint de Sorsa API
GET /info(se abre en una pestaña nueva)- Una solicitud devuelve
- 1 perfil
GET /2/usersyGET /2/users/by- Endpoint de Sorsa API
GET /info-batch(se abre en una pestaña nueva)- Una solicitud devuelve
- hasta 100 perfiles
GET /2/users/:id/followers- Endpoint de Sorsa API
GET /followers(se abre en una pestaña nueva)- Una solicitud devuelve
- hasta 200 perfiles
GET /2/users/:id/following- Endpoint de Sorsa API
GET /follows(se abre en una pestaña nueva)- Una solicitud devuelve
- hasta 200 perfiles
GET /2/users/search- Endpoint de Sorsa API
POST /search-users(se abre en una pestaña nueva)- Una solicitud devuelve
- una página de perfiles
GET /2/lists/:id/members- Endpoint de Sorsa API
GET /list-members(se abre en una pestaña nueva)- Una solicitud devuelve
- una página de perfiles
GET /2/lists/:id/followers- Endpoint de Sorsa API
GET /list-followers(se abre en una pestaña nueva)- Una solicitud devuelve
- una página de perfiles
GET /2/lists/:id/tweets- Endpoint de Sorsa API
GET /list-tweets(se abre en una pestaña nueva)- Una solicitud devuelve
- unos 20 tweets
GET /2/spaces/:id- Endpoint de Sorsa API
GET /spaces(se abre en una pestaña nueva)- Una solicitud devuelve
- 1 Space
GET /2/communities/:id- Endpoint de Sorsa API
POST /community-info(se abre en una pestaña nueva)- Una solicitud devuelve
- 1 comunidad
GET /2/communities/search- Endpoint de Sorsa API
POST /search-communities(se abre en una pestaña nueva)- Una solicitud devuelve
- una página de comunidades
GET /2/trends/by/woeid/:woeid- Endpoint de Sorsa API
GET /trends(se abre en una pestaña nueva)- Una solicitud devuelve
- las tendencias de 1 ubicación
GET /2/usage/tweets- Endpoint de Sorsa API
GET /key-usage-info(se abre en una pestaña nueva)- Una solicitud devuelve
- tu saldo, sin costo
Cuatro hábitos cambian con las rutas. Los endpoints de tweets usan POST con un cuerpo JSON, mientras que las consultas de usuarios, listas y utilidades siguen siendo GET. Un nombre de usuario sirve donde la v2 pedía un ID numérico, así que desaparece la consulta extra antes de cada llamada de timeline. tweet_link acepta la URL completa de un post o un ID simple. Y POST /user-tweets sigue paginando más allá de los 3,200 posts más recientes, donde se detiene el timeline de usuario de X.
Algunas lecturas de la v2 no tienen equivalente, porque Sorsa API devuelve solo lo que cualquiera puede ver en un perfil público: usuarios que dieron me gusta, los me gusta y marcadores de un usuario, conteos de posts, el timeline de inicio y todos los streams. Sorsa API también responde preguntas que la v2 no puede: POST /check-follow (se abre en una pestaña nueva) indica en una solicitud si una cuenta sigue a otra, y GET /about (se abre en una pestaña nueva) devuelve el país de una cuenta y el historial de sus nombres de usuario.
El conversor de abajo convierte una llamada v2 a la vez en su solicitud de Sorsa API, con los campos que cambian y el precio de 1,000 resultados de cada lado.
curl -G "https://api.x.com/2/tweets/search/recent" \
-H "Authorization: Bearer $X_BEARER_TOKEN" \
--data-urlencode "query=from:mira_moves_reads" \
-d "max_results=100" \
-d "expansions=author_id" \
-d "tweet.fields=created_at,public_metrics"
curl -X POST "https://api.sorsa.io/v3/search-tweets" \
-H "ApiKey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "from:mira_moves_reads", "order": "latest"}'
- Endpoint de Sorsa API
POST /search-tweets- Una solicitud devuelve
- unos 20 tweets
- API oficial de X, 1,000 resultados
- $5.00, más hasta $10.00 por los autores
- Sorsa API, 1,000 resultados
- desde $0.07
Paginación: next_token pasa a ser next_cursor en el cuerpo JSON. La misma consulta busca hasta 2006, no solo 7 días, y cada tweet trae a su autor.
Elige la llamada v2 que hace tu código para ver la misma solicitud en Sorsa API y cuánto cuestan 1,000 resultados en cada una.
Si tu código usa filtered stream o webhooks
Sorsa API no tiene stream ni webhook, así que una regla de filtered stream se convierte en una consulta de búsqueda que consultas por sondeo. Envía la regla como consulta de POST /search-tweets con "order": "latest", agrega since_id: con el ID más reciente que tengas guardado y sigue next_cursor para que un minuto movido con más de 20 posts nuevos no pierda ninguno.
import os
import time
import requests
URL = "https://api.sorsa.io/v3/search-tweets"
HEADERS = {"ApiKey": os.environ.get("SORSA_API_KEY", "YOUR_API_KEY")}
RULE = '"open data" lang:en'
def new_tweets(since_id):
body = {"query": f"{RULE} since_id:{since_id}", "order": "latest"}
found = []
while True:
r = requests.post(URL, headers=HEADERS, json=body, timeout=30)
if r.status_code == 429:
time.sleep(1)
continue
r.raise_for_status()
page = r.json()
found += page.get("tweets", [])
if not page.get("next_cursor"):
return found
body["next_cursor"] = page["next_cursor"]
newest = "1852331946812457001"
while True:
tweets = new_tweets(newest)
for t in sorted(tweets, key=lambda t: int(t["id"])):
print(t["id"], t["user"]["username"], t["full_text"][:60])
if tweets:
newest = max((t["id"] for t in tweets), key=int)
time.sleep(15)
Una clave funciona a 20 solicitudes por segundo en todos los planes, es decir, 1,200 sondeos de una página por minuto compartidos entre tus reglas. La programación, las alertas y la deduplicación para tareas de monitoreo están en la guía de monitoreo de Twitter en tiempo real.
#Reescribe el análisis de la respuesta
Sorsa API envía cada tweet como un solo objeto con su autor adentro, así que desaparece la unión entre data e includes y el trabajo que queda es renombrar campos.
Los nombres de campo de abajo salen del diccionario de datos de X y de la referencia en vivo de Sorsa API, leídos el 2 de noviembre de 2026. La mayoría de los cambios son aplanar: una métrica sale de public_metrics y conserva su nombre, y unos pocos son renombres.
| Campo de la API v2 de X | Campo de Sorsa API |
|---|---|
data[] | tweets[], o users[] para listas de cuentas |
text y note_tweet.text | full_text |
author_id con includes.users[] | user, el perfil completo |
public_metrics.like_count | likes_count |
public_metrics.impression_count | view_count |
public_metrics.retweet_count, reply_count, quote_count, bookmark_count | los mismos nombres, un nivel más arriba |
conversation_id | conversation_id_str |
referenced_tweets con includes.tweets[] | quoted_status, retweeted_status, in_reply_to_tweet_id |
attachments.media_keys con includes.media[] | entities[] con type, link, preview |
name en un usuario | display_name |
public_metrics.following_count, tweet_count en un usuario | followings_count, tweets_count |
meta.next_token y pagination_token | next_cursor, en ambas direcciones |
errors[] con title y detail | {"message": "..."} |
data[]- Campo de Sorsa API
tweets[], ousers[]para listas de cuentas
textynote_tweet.text- Campo de Sorsa API
full_text
author_idconincludes.users[]- Campo de Sorsa API
user, el perfil completo
public_metrics.like_count- Campo de Sorsa API
likes_count
public_metrics.impression_count- Campo de Sorsa API
view_count
public_metrics.retweet_count,reply_count,quote_count,bookmark_count- Campo de Sorsa API
- los mismos nombres, un nivel más arriba
conversation_id- Campo de Sorsa API
conversation_id_str
referenced_tweetsconincludes.tweets[]- Campo de Sorsa API
quoted_status,retweeted_status,in_reply_to_tweet_id
attachments.media_keysconincludes.media[]- Campo de Sorsa API
entities[]contype,link,preview
nameen un usuario- Campo de Sorsa API
display_name
public_metrics.following_count,tweet_counten un usuario- Campo de Sorsa API
followings_count,tweets_count
meta.next_tokenypagination_token- Campo de Sorsa API
next_cursor, en ambas direcciones
errors[]contitleydetail- Campo de Sorsa API
{"message": "..."}
Los IDs siguen siendo cadenas y las fechas siguen en ISO 8601, así que los datos guardados conservan su forma. La paginación se reduce a un solo nombre: envía next_cursor como parámetro de consulta en los endpoints GET o en el cuerpo JSON en los endpoints POST, y detente cuando llegue vacío. El control de límites también se simplifica. Las ventanas de 15 minutos y los encabezados x-rate-limit-* que describe nuestra guía de límites de solicitudes de la API de X dan paso a un solo límite de 20 solicitudes por segundo por clave: un 429 significa esperar un segundo y reintentar, y un 403 significa que falta la clave o que se agotaron las solicitudes del mes.
Para tener en cuenta
El tamaño de las páginas no es fijo. Una página de búsqueda contiene hasta 20 tweets y puede contener menos mientras quedan más páginas, así que haz el ciclo con
next_cursor, nunca con la cantidad de elementos, o una página corta terminará el trabajo antes de tiempo.
#Código de Tweepy y xdk, antes y después
Tweepy y xdk son librerías gratuitas; la factura viene de la API oficial de X que hay detrás, así que la migración cambia el cliente, no el lenguaje. Aquí está la misma búsqueda con autores, paginada hasta el final, en Tweepy y luego en Python simple sobre Sorsa API:
import tweepy
client = tweepy.Client(bearer_token="X_BEARER_TOKEN")
for page in tweepy.Paginator(
client.search_recent_tweets,
query="from:mira_moves_reads",
tweet_fields=["created_at", "public_metrics"],
expansions=["author_id"],
user_fields=["username", "name"],
max_results=100):
users = {u.id: u for u in page.includes.get("users", [])}
for t in page.data or []:
author = users[t.author_id]
print(t.id, author.username, t.public_metrics["like_count"])
Las listas de campos, la unión con includes y el paginador salen del código; entra un solo encabezado. El código de xdk cambia igual: Client(bearer_token=...) y client.posts.search_recent(...) paginan los mismos data e includes, así que dan paso al ciclo de arriba. La consulta también llega más lejos tras la migración, porque POST /search-tweets busca hasta 2006 en todos los planes, mientras que la búsqueda reciente de la v2 cubre los últimos 7 días.
El código construido sobre el paquete twitter-api-v2 de Node.js se migra igual, a fetch y un solo encabezado:
const URL = "https://api.sorsa.io/v3/search-tweets";
const headers = {
ApiKey: process.env.SORSA_API_KEY ?? "YOUR_API_KEY",
"Content-Type": "application/json",
};
const body = { query: "from:mira_moves_reads", order: "latest" };
do {
const res = await fetch(URL, {
method: "POST",
headers,
body: JSON.stringify(body),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const page = await res.json();
for (const t of page.tweets ?? []) {
console.log(t.id, t.user.username, t.likes_count);
}
body.next_cursor = page.next_cursor;
} while (body.next_cursor);
Para el código de Tweepy que solo lee, pasa las lecturas a Sorsa API: la búsqueda devuelve 20 tweets por solicitud desde $0.07 por 1,000, los perfiles llegan 100 por solicitud desde $0.014 por 1,000 y no hace falta OAuth. Los reintentos, el procesamiento por lotes y un regulador de ritmo para 20 solicitudes por segundo están en la guía de la API de Twitter en Python.
#Traduce tus consultas de búsqueda
POST /search-tweets acepta la sintaxis de la búsqueda avanzada de X, el lenguaje del cuadro de búsqueda de x.com, así que las palabras clave, las frases entre comillas, from:, to:, lang:, conversation_id: y los hashtags pasan sin cambios. Lo que cambia son los operadores is: y has: de la v2 y sus parámetros de tiempo, que pasan a la consulta:
| API v2 de X | Consulta de Sorsa API |
|---|---|
parámetros start_time y end_time | since:2026-10-01 y until:2026-11-01 |
parámetros since_id y until_id | since_id:ID y max_id:ID (inclusivo) |
sort_order=recency o relevancy | "order": "latest" o "popular" en el cuerpo |
-is:retweet | nada: los retweets quedan fuera por defecto |
is:reply y -is:reply | filter:replies y -filter:replies |
is:quote | filter:quote |
has:media, has:images, has:links | filter:media, filter:images, filter:links |
has:video_link | filter:native_video |
min_likes:, min_reposts:, min_replies: | min_faves:, min_retweets:, min_replies: |
point_radius: | geocode: |
parámetros
start_timeyend_time- Consulta de Sorsa API
since:2026-10-01yuntil:2026-11-01
parámetros
since_idyuntil_id- Consulta de Sorsa API
since_id:IDymax_id:ID(inclusivo)
sort_order=recencyorelevancy- Consulta de Sorsa API
"order": "latest"o"popular"en el cuerpo
-is:retweet- Consulta de Sorsa API
- nada: los retweets quedan fuera por defecto
is:replyy-is:reply- Consulta de Sorsa API
filter:repliesy-filter:replies
is:quote- Consulta de Sorsa API
filter:quote
has:media,has:images,has:links- Consulta de Sorsa API
filter:media,filter:images,filter:links
has:video_link- Consulta de Sorsa API
filter:native_video
min_likes:,min_reposts:,min_replies:- Consulta de Sorsa API
min_faves:,min_retweets:,min_replies:
point_radius:- Consulta de Sorsa API
geocode:
Tres detalles hacen tropezar a las consultas traducidas. Los retweets nativos quedan fuera salvo que agregues include:nativeretweets. geocode: toma primero la latitud, como en geocode:37.77,-122.41,5km, mientras que point_radius: de la v2 tomaba primero la longitud. Y max_id: incluye el propio ID, mientras que until_id de la v2 lo excluye. La lista completa de operadores, con recetas, está en nuestra guía de operadores de búsqueda de Twitter.
#Prueba con una ejecución en paralelo y luego cambia
Una ejecución en paralelo envía una copia de cada lectura a Sorsa API mientras la API oficial de X sigue atendiendo a tu app, de modo que los resultados se comparan antes de que algo dependa de ellos.
El script ejecuta una consulta sobre el mismo día cerrado en ambas API y compara los IDs de los posts. Ejecútalo para cada consulta y timeline de los que dependas; una ventana cerrada evita que los posts nuevos distorsionen el conteo.
import os
import requests
X_URL = "https://api.x.com/2/tweets/search/recent"
X_HEADERS = {"Authorization": "Bearer " + os.environ["X_BEARER_TOKEN"]}
S_URL = "https://api.sorsa.io/v3/search-tweets"
S_HEADERS = {"ApiKey": os.environ.get("SORSA_API_KEY", "YOUR_API_KEY")}
def x_ids(query, start, end):
params = {"query": query, "start_time": start,
"end_time": end, "max_results": 100}
ids = set()
while True:
r = requests.get(X_URL, headers=X_HEADERS, params=params, timeout=30)
r.raise_for_status()
page = r.json()
ids |= {t["id"] for t in page.get("data", [])}
token = page.get("meta", {}).get("next_token")
if not token:
return ids
params["next_token"] = token
def sorsa_ids(query, since, until):
body = {"query": f"{query} since:{since} until:{until}",
"order": "latest"}
ids = set()
while True:
r = requests.post(S_URL, headers=S_HEADERS, json=body, timeout=30)
r.raise_for_status()
page = r.json()
ids |= {t["id"] for t in page.get("tweets", [])}
if not page.get("next_cursor"):
return ids
body["next_cursor"] = page["next_cursor"]
query = "from:mira_moves_reads"
a = x_ids(query, "2026-10-30T00:00:00Z", "2026-10-31T00:00:00Z")
b = sorsa_ids(query, "2026-10-30_00:00:00_UTC", "2026-10-31_00:00:00_UTC")
print(f"official X API {len(a)}, Sorsa API {len(b)}, shared {len(a & b)}")
print("only on X:", sorted(a - b)[:10])
print("only on Sorsa API:", sorted(b - a)[:10])
Espera pequeñas diferencias en lugar de cero: los posts borrados entre las dos llamadas desaparecen, y la búsqueda por palabra clave de X deja fuera los retweets desde el 4 de mayo de 2026. La búsqueda reciente de X llega 7 días hacia atrás, así que mantén la ventana dentro de la última semana. Cuando los IDs coincidan en las lecturas que más ejecutas, haz el cambio por etapas:
- Pon ambos clientes detrás de una sola interfaz y un solo indicador de configuración, para que cualquier tipo de lectura pueda volver atrás con un solo valor.
- Mueve el 10% de las lecturas, vigila los errores y
GET /key-usage-info, que es gratis y nunca cuenta contra tus solicitudes, y luego mueve el resto. - Conserva la clave de la API oficial de X solo para escrituras, y baja su límite de gasto y el monto de recarga automática en la Developer Console.
- Elimina las rutas de código de lectura, las listas de campos y la paginación antigua cuando haya pasado un ciclo de facturación completo.
#Saca tus lecturas de la API oficial de X
Una integración v2 de solo lectura pasa a Sorsa API, la API de Twitter hecha para leer datos públicos de X, con un solo encabezado, un endpoint por llamada y un analizador que pierde su unión. La búsqueda cuesta desde $0.07 por 1,000 tweets en lugar de $5.00, los seguidores desde $0.007 por 1,000 en lugar de $10.00, y todos los planes responden a 20 solicitudes por segundo. Empieza por tu lectura más cara, ejecútala en paralelo durante unos días y deja la API oficial de X solo para lo que escribe.
#Preguntas frecuentes
¿A qué API de Twitter debo migrar desde la API oficial de X?
Para leer datos públicos de X, migra a Sorsa API: cada lectura v2 se asigna a un endpoint, los tweets cuestan desde $0.014 por 1,000 por ID y desde $0.07 por búsqueda frente a $5.00 en la API oficial de X, todos los planes funcionan a 20 solicitudes por segundo y la búsqueda llega hasta 2006. Una clave de API reemplaza a OAuth. Sorsa API es de solo lectura, así que conserva la API oficial de X para publicar.
¿Cuánto tarda una migración desde la API oficial de X?
El cambio de código crece con la cantidad de llamadas v2 distintas, no con el volumen de tus datos: cada llamada se convierte en un endpoint, un encabezado y unos pocos campos renombrados. Suma una ejecución en paralelo de unos días en la que ambas API responden las mismas lecturas y comparas los IDs. Publicar, los DM y las demás escrituras se quedan en la API oficial de X, así que solo se mueve la ruta de lectura.
¿Puedo seguir publicando en X después de pasar las lecturas a Sorsa API?
Sí. Conserva la API oficial de X para publicar, responder y enviar DM, y manda las lecturas a Sorsa API. Sorsa API es de solo lectura por diseño: su clave no puede actuar sobre ninguna cuenta de X y nunca necesita tu usuario de X, así que las dos conviven sin conflicto. Si tu ruta de escritura todavía firma con tokens de usuario OAuth 1.0a, X permite desde el 21 de septiembre de 2026 que las apps los cambien por tokens OAuth 2.0.
¿Debo migrar primero de la v1.1 a la v2?
No. El código que todavía llama a lecturas de la v1.1 puede asignarse directamente a Sorsa API: statuses/user_timeline pasa a ser POST /user-tweets, statuses/show pasa a ser POST /tweet-info y users/show pasa a ser GET /info. X clasifica la v1.1 como heredada con soporte limitado y dejó obsoleta su búsqueda v1.1 en 2023, así que detenerte en la v2 significa reescribir para OAuth 2.0 y la facturación por uso antes de volver a migrar.
¿Qué pasó con los planes heredados Basic y Pro de la API de X?
X los pasó al pago por uso: las suscripciones de Basic heredado terminaron en sus fechas de transición posteriores al 1 de junio de 2026, y las de Pro después del 1 de septiembre de 2026. A una cuenta mensual de Pro se le cargó un crédito de $5,000 en la transición, con la recarga automática activada. Ahora las lecturas se cobran por recurso, $0.005 por post y $0.010 por usuario, mientras que los mismos tweets cuestan desde $0.07 por 1,000 por búsqueda en Sorsa API.
#Cómo lo verificamos
El 2 de noviembre de 2026 construimos el mapa de endpoints comparando la propia especificación OpenAPI de X (versión 2.173, 200 rutas) con la referencia en vivo de Sorsa API (45 endpoints), y comprobamos cada operador v2 con la lista de operadores de X y cada reemplazo con los operadores de búsqueda de Sorsa API. Ese mismo día leímos el changelog de X, su página de precios, su documentación de paginación y límites de solicitudes, la referencia del XDK de Python y el cliente de Tweepy 4.17.0, además de los avisos del foro de X sobre el índice de búsqueda, los planes heredados y el cobro de expansiones. No teníamos una clave de Sorsa API para esta revisión, así que la respuesta mostrada usa valores de demostración; ejecutamos los bloques de Python y Node.js contra un mock local que responde en las estructuras documentadas de ambas API.
Fuentes
- X API changelog (docs.x.com) (se abre en una pestaña nueva)
- X API pay-per-usage pricing (docs.x.com) (se abre en una pestaña nueva)
- X API OpenAPI specification (se abre en una pestaña nueva)
- Search operators, X API v2 (docs.x.com) (se abre en una pestaña nueva)
- Search endpoints migrating to a new index (X Developers, May 2026) (se abre en una pestaña nueva)
- Legacy Basic plans moving to pay-per-use (X Developers, May 21, 2026) (se abre en una pestaña nueva)
- Legacy Pro plans moving to pay-per-use (X Developers, August 14, 2026) (se abre en una pestaña nueva)
- X API PPU: expansions update (X Developers, October 23, 2026) (se abre en una pestaña nueva)
- Python XDK (docs.x.com) (se abre en una pestaña nueva)
- Sorsa API reference, OpenAPI
- Migration from the official X API (Sorsa API docs) (se abre en una pestaña nueva)
- Search operators (Sorsa API docs) (se abre en una pestaña nueva)