API de Twitter en PHP: obtén datos de X con cURL, Guzzle y Laravel
Para obtener datos de Twitter/X en PHP, envía solicitudes HTTPS con cURL o Guzzle, pon la clave de API en un encabezado, decodifica el JSON y sigue el cursor página por página; la API oficial de X cobra $0.005 por post.
Respuesta corta: X publica SDKs oficiales solo para Python y TypeScript, así que PHP llega a la API oficial de X mediante clientes de la comunidad como TwitterOAuth, una cuenta de desarrollador y créditos prepagados: $0.005 por post, y $0.015 con su autor a partir del 22 de noviembre de 2026. Para datos públicos, usa Sorsa API: una sola clave, 20 tweets por solicitud desde $0.07 por 1,000; las 100 solicitudes gratis cubren 2,000.
- Endpoint
POST /user-tweets- Tamaño de página
- 20 tweets por solicitud
- Precio
- desde $0.07 por 1,000 tweets
#¿Hay un SDK oficial de la API de X para PHP?
No. X publica SDKs oficiales solo para Python y TypeScript, y su página de herramientas lista tres clientes de PHP de la comunidad que X no mantiene: twitter-api-v2-php, bird-elephant y TwitterOAuth (herramientas y librerías de X (se abre en una pestaña nueva)). TwitterOAuth es el más instalado de los tres, con unas 240 mil instalaciones de Composer al mes. Su versión 9.0.0, publicada el 1 de noviembre de 2026, solo habla con la API v2 y exige PHP 8.3 o posterior.
Los tres son clientes de la API oficial de X, así que cada llamada que hacen necesita una cuenta de desarrollador, las claves de una app y créditos prepagados. 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 los medios 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 TwitterOAuth paga por cada autor que adjunta:
use Abraham\TwitterOAuth\TwitterOAuth;
$x = new TwitterOAuth($apiKey, $apiSecret, null, $bearerToken);
$posts = $x->get('tweets/search/recent', [
'query' => 'laravel lang:en',
'max_results' => 100,
'expansions' => 'author_id',
]);
// 100 posts: $0.50. From Nov 22, 2026, with authors: up to $1.50
Las respuestas viejas apuntan más atrás. El primer resultado de Google para esta búsqueda es J7mbo/twitter-api-php, un wrapper de llamadas v1.1 cuyo repositorio de GitHub está archivado y cuyo último cambio fue en abril de 2020. Todas las tarifas, topes y fechas vigentes de la vía oficial 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 con su clave de API, su secreto de API y su bearer token, que 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 1.0a, un token de acceso y su secreto, para todo lo que publique; el constructor de cuatro argumentos de TwitterOAuth los recibe.
Nada de eso hace falta para leer posts y perfiles públicos. La sección siguiente lo hace con una sola clave y la extensión cURL que la mayoría de las compilaciones de PHP ya incluyen.
#Obtén tweets en PHP con una sola clave de API
Para leer datos de X desde PHP, 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 timelines y búsqueda desde $0.07 por 1,000 tweets. Cada respuesta es JSON simple que json_decode convierte en arreglos, 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.
-
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.
-
Mantén la clave fuera del código. Defínela como variable de entorno, o en
.envpara Laravel, y llama a la API solo desde PHP del lado del servidor: una clave en JavaScript del navegador la ve cualquier visitante.Terminal export SORSA_API_KEY="your_key" # PowerShell: $env:SORSA_API_KEY="your_key" -
Obtén los últimos tweets de una cuenta. Envía el nombre de usuario a
POST /user-tweets(se abre en una pestaña nueva) con la clave en el encabezadoApiKey. Pruébalo primero desde la terminal:Terminal curl -X POST "https://api.sorsa.io/v3/user-tweets" \ -H "ApiKey: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"username": "php_release_notes"}' -
Llámalo desde PHP. El cURL simple no necesita ningún paquete de Composer. Con Guzzle, termina
base_uricon una barra y empieza la ruta sin ella, o se pierde la parte/v3.<?php $key = getenv('SORSA_API_KEY') ?: 'YOUR_API_KEY'; $ch = curl_init('https://api.sorsa.io/v3/user-tweets'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'ApiKey: ' . $key, 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode(['username' => 'php_release_notes']), CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, ]); $body = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); $data = json_decode($body, true, flags: JSON_THROW_ON_ERROR); if ($status !== 200) { exit("HTTP $status: {$data['message']}\n"); } foreach ($data['tweets'] as $tweet) { echo $tweet['created_at'], ' ', mb_substr($tweet['full_text'], 0, 60), "\n"; } -
Lee la página. Cada respuesta trae hasta 20 tweets y un
next_cursormientras quedan más. Los valores de abajo son datos de demostración con la forma documentada.Response200 OK { "tweets": [ { "id": "1985432176540981234", "full_text": "PHP 8.5.12 is out with two security fixes. Upgrade today.", "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": "php_release_notes", "display_name": "PHP Release Notes", "followers_count": 1840, "verified": false } } ], "next_cursor": "DAABCgABF7Y8kQ3wAAA" }
Cada tweet llega con sus conteos y su autor, así que un script de PHP no define expansiones ni listas de campos. Estos son los campos que lee la mayoría de los scripts:
| Campo | Qué contiene |
|---|---|
id | El ID del tweet: 19 dígitos, enviado como string |
full_text | El texto completo del post |
created_at | La hora en ISO 8601, UTC, lista para new DateTimeImmutable() |
likes_count, retweet_count, reply_count, quote_count | Conteos de interacción |
view_count, bookmark_count | Vistas y guardados |
user | El perfil del autor: 19 campos como username y followers_count |
quoted_status, retweeted_status | El tweet citado o republicado, con la misma forma |
id- Qué contiene
- El ID del tweet: 19 dígitos, enviado como string
full_text- Qué contiene
- El texto completo del post
created_at- Qué contiene
- La hora en ISO 8601, UTC, lista para
new DateTimeImmutable()
likes_count,retweet_count,reply_count,quote_count- Qué contiene
- Conteos de interacción
view_count,bookmark_count- Qué contiene
- Vistas y guardados
user- Qué contiene
- El perfil del autor: 19 campos como
usernameyfollowers_count
quoted_status,retweeted_status- Qué contiene
- El tweet citado o republicado, con la misma forma
#Una función de solicitud con reintentos
Envía cada llamada por una sola función que reintente solo lo que un reintento puede arreglar. Un 429 significa que más de 20 solicitudes empezaron dentro de un mismo segundo con tu clave, y un 5xx o una conexión cortada es una falla temporal; ambos se resuelven solos. sorsa() los reintenta hasta cuatro veces, esperando 1, 2, 4 y 8 segundos, y envía los parámetros GET como claves repetidas, la forma que leen los endpoints por lotes de más abajo. El resto de esta guía se apoya en ella.
<?php
const SORSA_API = 'https://api.sorsa.io/v3';
const RETRY = [429, 500, 502, 503, 504];
/** Build a query string; a list becomes a repeated key: a=1&a=2. */
function query(array $params): string
{
$parts = [];
foreach ($params as $name => $value) {
foreach ((array) $value as $one) {
$parts[] = rawurlencode($name) . '=' . rawurlencode($one);
}
}
return implode('&', $parts);
}
/** Send one request; retry 429, 5xx and network errors up to 4 times. */
function sorsa(string $method, string $path, array $params = []): array
{
$url = SORSA_API . $path;
$headers = ['ApiKey: ' . (getenv('SORSA_API_KEY') ?: 'YOUR_API_KEY')];
$options = [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30];
if ($method === 'GET') {
$url .= $params ? '?' . query($params) : '';
} else {
$headers[] = 'Content-Type: application/json';
$options[CURLOPT_POSTFIELDS] = json_encode($params);
}
$options[CURLOPT_HTTPHEADER] = $headers;
for ($attempt = 0; ; $attempt++) {
$ch = curl_init($url);
curl_setopt_array($ch, $options);
$raw = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); // 0: no answer
$again = in_array($status, [0, ...RETRY], true);
if (!$again || $attempt === 4) {
break;
}
sleep(2 ** $attempt); // 1, 2, 4, 8 seconds
}
if ($raw === false) {
throw new RuntimeException('Network error: ' . curl_error($ch));
}
$data = json_decode($raw, true, flags: JSON_THROW_ON_ERROR);
if ($status !== 200) {
throw new RuntimeException("HTTP $status: " . ($data['message'] ?? ''));
}
return $data;
}
Un 401 o un 403 nunca merece un reintento: la clave es incorrecta o falta, o sus solicitudes se agotaron, y el campo message dice cuál es el caso. Las solicitudes fallidas nunca se cobran, así que un reintento no cuesta nada, y todos los endpoints de Sorsa API solo leen, de modo que repetir un POST no puede crear nada dos veces. La función corre en PHP 8.0 y posteriores. Omite curl_close(), que no hace nada desde PHP 8.0, porque los handles de cURL ahora se cierran solos.
#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. Los timelines y POST /search-tweets (se abre en una pestaña nueva) 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 llegó el final. Un generador entrega una página a la vez y mantiene la memoria estable sin importar cuánto dure el trabajo.
require 'sorsa.php';
/** Yield pages of a POST endpoint until the cursor runs out. */
function pages(string $path, array $body, int $maxPages = 100): Generator
{
for ($i = 0; $i < $maxPages; $i++) {
$page = sorsa('POST', $path, $body);
yield $page;
if (empty($page['next_cursor'])) {
return;
}
$body['next_cursor'] = $page['next_cursor'];
}
}
foreach (pages('/search-tweets', ['query' => 'laravel lang:en']) as $page) {
foreach ($page['tweets'] as $tweet) {
echo $tweet['id'], ' @', $tweet['user']['username'], "\n";
}
}
Mantén sin cambios la consulta y order mientras corre un cursor, y trata $maxPages como un presupuesto: 100 páginas son 100 solicitudes y, como máximo, 2,000 tweets. Los endpoints GET reciben el cursor en la cadena de consulta. GET /followers (se abre en una pestaña nueva) devuelve hasta 200 perfiles por página:
$params = ['username' => 'php_release_notes'];
$followers = [];
do {
$page = sorsa('GET', '/followers', $params);
array_push($followers, ...$page['users']);
$params['next_cursor'] = $page['next_cursor'] ?? null;
} while ($params['next_cursor']);
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 de tweets 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. Las respuestas por lote no traen cursor: una respuesta contiene el lote completo.
$tweets = [];
foreach (array_chunk($tweetIds, 100) as $batch) {
$res = sorsa('POST', '/tweet-info-bulk', ['tweet_links' => $batch]);
array_push($tweets, ...$res['tweets']);
}
$profiles = [];
foreach (array_chunk($usernames, 100) as $batch) {
$res = sorsa('GET', '/info-batch', ['usernames' => $batch]);
array_push($profiles, ...$res['users']);
}
Una 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 las 100 solicitudes gratis obtienes así 10,000 tweets.
Para tener en cuenta
GET /info-batchlee una lista como una clave repetida,usernames=a&usernames=b, la forma que define su referencia OpenAPI. Elhttp_build_query()de PHP escribe en su lugarusernames%5B0%5D=a&usernames%5B1%5D=b, y lo mismo hacen la opciónquerycon arreglos de Guzzle y elHttp::get($url, $array)de Laravel, que arman la consulta de la misma manera. Usa la funciónquery()de arriba, oGuzzleHttp\Psr7\Query::build(), que escribe claves repetidas.
#Ejecuta lotes en paralelo a 19 solicitudes por segundo
PHP ejecuta solicitudes en paralelo con curl_multi, y un solo proceso alcanza las 20 solicitudes por segundo por clave de Sorsa API en cuanto el trabajo se divide en solicitudes independientes. Los lotes se dividen sin problema, porque cada uno se sostiene solo. Una cadena de cursor no se puede dividir, ya que cada página necesita el cursor de la anterior, así que corre en paralelo cuentas o ventanas de fechas distintas. parallel() inicia como máximo 19 solicitudes por segundo, una menos que el límite, y devuelve a la fila cualquier 429 o 5xx.
/** POST many bodies side by side, starting at most $rate a second. */
function parallel(string $path, array $bodies, float $rate = 19): array
{
$multi = curl_multi_init();
$queue = array_keys($bodies);
$tries = $results = [];
$next = microtime(true);
$running = 0;
while ($queue || $running) {
if ($queue && microtime(true) >= $next) {
$key = array_shift($queue);
$ch = curl_init(SORSA_API . $path);
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'ApiKey: ' . (getenv('SORSA_API_KEY') ?: 'YOUR_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode($bodies[$key]),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_PRIVATE => (string) $key,
]);
curl_multi_add_handle($multi, $ch);
$next = max($next, microtime(true) - 1 / $rate) + 1 / $rate;
}
curl_multi_exec($multi, $running);
while ($done = curl_multi_info_read($multi)) {
$ch = $done['handle'];
$key = curl_getinfo($ch, CURLINFO_PRIVATE);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$tries[$key] = ($tries[$key] ?? 0) + 1;
if (in_array($status, [0, ...RETRY], true) && $tries[$key] < 5) {
$queue[] = $key; // back in line, it waits its turn
} else {
$results[$key] = json_decode(curl_multi_getcontent($ch), true);
}
curl_multi_remove_handle($multi, $ch);
}
curl_multi_select($multi, 0.01);
}
return $results;
}
$bodies = array_map(fn ($batch) => ['tweet_links' => $batch],
array_chunk($tweetIds, 100));
$tweets = array_merge(...array_column(parallel('/tweet-info-bulk', $bodies),
'tweets'));
Con 19 inicios por segundo, 100 lotes mueven 10,000 tweets en unos 5 segundos, 1,900 tweets por segundo. Mantén un solo bucle parallel() por clave: dos procesos que lo ejecutan en paralelo inician el doble del límite entre los dos. El Pool de Guzzle parece la herramienta para esto, pero su opción concurrency limita las solicitudes en vuelo, no los inicios por segundo, así que un pool de respuestas rápidas aún puede cruzar las 20 por segundo y apoyarse en los reintentos.
#Laravel: las mismas llamadas con el cliente Http
El cliente Http de Laravel envuelve a Guzzle, así que una app de Laravel no necesita ningún paquete adicional. Una macro define la URL base, la clave y una regla de reintentos que repite solo los 429, las respuestas 5xx y las conexiones cortadas; cada controlador, job o comando de Artisan llama entonces a Http::sorsa().
// config/services.php: 'sorsa' => ['key' => env('SORSA_API_KEY')],
// .env: SORSA_API_KEY=YOUR_API_KEY
// app/Providers/AppServiceProvider.php
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Http\Client\RequestException;
use Illuminate\Support\Facades\Http;
// inside the class
public function boot(): void
{
Http::macro('sorsa', fn () => Http::baseUrl('https://api.sorsa.io/v3')
->withHeaders(['ApiKey' => config('services.sorsa.key')])
->timeout(30)
->retry(5, fn (int $attempt) => 1000 * 2 ** ($attempt - 1),
fn (Throwable $e) => $e instanceof ConnectionException
|| ($e instanceof RequestException
&& ($e->response->status() === 429
|| $e->response->serverError()))));
}
retry() espera 1, 2, 4 y 8 segundos entre intentos y lanza una RequestException cuando se agotan los intentos o el estado no vale un reintento, como un 401 o un 403, así que una llamada fallida nunca llega a la caché. Ejecuta los trabajos largos, como miles de solicitudes por lotes, desde un job en cola o un comando de Artisan y no dentro de una solicitud de página.
Un feed en caché gasta solicitudes según el reloj, no según el visitante. El planificador convierte el número de feeds y el tiempo de caché en solicitudes por día y por mes, y en el plan de Sorsa API que las incluye.
- Solicitudes al día
- 288
- Solicitudes al mes
- 8,640
- Plan de Sorsa API que lo cubre
- Starter
- Las 100 solicitudes gratis duran
- unos 8 h
sin importar el tráfico
30 días
10K solicitudes al mes, 86% usado
sin tarjeta, nunca vencen
Todos los visitantes de una misma ventana de 5 minutos leen la copia en caché, así que 100 vistas de página y 1M de vistas cuestan las mismas 288 solicitudes al día.
Las solicitudes dependen de cada cuánto se renueva la caché, nunca de las vistas de página.
#Cuánto cuesta un trabajo en PHP
Sorsa API cobra por solicitud, sin importar lo que devuelva el endpoint: los timelines y la 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 las búsquedas con TwitterOAuth.
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
El perfil del autor viene dentro de cada tweet
La tarifa por 1,000 solicitudes va de $4.90 en Starter a $1.40 en Hyperscale, y cada 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 = sorsa('GET', '/key-usage-info');
echo $usage['remaining_requests'], ' left until ', $usage['valid_until'], "\n";
#Cómo pasar el código de TwitterOAuth a Sorsa API
La mayoría de las lecturas de TwitterOAuth se corresponden con un endpoint de Sorsa API, y el código deja atrás sus expansiones y listas de campos, porque cada tweet ya trae su autor y sus conteos. La tabla empareja rutas comunes de v2 con sus endpoints; 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.
| Llamada de TwitterOAuth | Endpoint de Sorsa API | Qué cambia |
|---|---|---|
get('tweets/search/recent') | POST /search-tweets | Un solo endpoint para la búsqueda reciente y la de archivo, desde 2006 |
get("users/$id/tweets") | POST /user-tweets | Basta con un nombre de usuario, y no hay tope de 3,200 posts |
get('tweets') con ids | POST /tweet-info-bulk | 100 IDs o enlaces por solicitud |
get('users/by') con usernames | GET /info-batch | 100 nombres de usuario por solicitud |
get("users/$id/followers") | GET /followers | 200 seguidores por página |
post('tweets') | ninguno | Solo lectura: publica con la API oficial de X |
get('tweets/search/recent')- Endpoint de Sorsa API
POST /search-tweets- Qué cambia
- Un solo endpoint para la búsqueda reciente y la de archivo, desde 2006
get("users/$id/tweets")- Endpoint de Sorsa API
POST /user-tweets- Qué cambia
- Basta con un nombre de usuario, y no hay tope de 3,200 posts
get('tweets')conids- Endpoint de Sorsa API
POST /tweet-info-bulk- Qué cambia
- 100 IDs o enlaces por solicitud
get('users/by')conusernames- Endpoint de Sorsa API
GET /info-batch- Qué cambia
- 100 nombres de usuario por solicitud
get("users/$id/followers")- Endpoint de Sorsa API
GET /followers- Qué cambia
- 200 seguidores por página
post('tweets')- Endpoint de Sorsa API
- 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 arreglo user, y meta->next_token pasa a ser next_cursor. TwitterOAuth devuelve objetos, mientras que json_decode($body, true) da los arreglos que usa esta guía. El timeline de usuario también llega más allá de los 3,200 posts más recientes, donde se detiene el timeline oficial (se abre en una pestaña nueva).
#Haz hoy tu primera solicitud en PHP
Leer datos de X en PHP necesita la extensión cURL, una clave y cuatro funciones cortas: sorsa() para los reintentos, pages() para los cursores, array_chunk() con los endpoints por lotes para 100 elementos por solicitud, y parallel() para 19 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 timeline o búsqueda desde $0.07 por 1,000, 100 por lote desde $0.014, y 100 solicitudes gratis que no piden tarjeta y nunca vencen. Las mismas llamadas en otros lenguajes están en las guías de la API de Twitter en Python y la API de Twitter en Node.js.
#Preguntas frecuentes
¿Qué API de Twitter debería usar con PHP?
Para leer datos de X desde PHP, usa Sorsa API: una clave en un encabezado ApiKey en lugar de OAuth, JSON simple para json_decode, timelines y 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.
¿Hay una API de Twitter gratuita para PHP?
La API oficial de X no tiene nivel gratuito para desarrolladores nuevos: TwitterOAuth es 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 PHP obtienen 2,000 tweets mediante timelines o búsqueda, o hasta 10,000 tweets en lotes de 100. El panorama completo está en ¿es gratis la API de Twitter?.
¿Twitter (X) usa PHP?
No. Twitter nació sobre Ruby on Rails y desde 2010 pasó su back end a la JVM, con servicios en Scala y Java, como describe el blog de ingeniería de Twitter (se abre en una pestaña nueva). El código de recomendación que X publicó en 2023 y de nuevo en enero de 2026 es en su mayoría Scala, Java, Rust y Python, sin nada de PHP. PHP funciona igual de bien como cliente: los datos de X llegan a un script de PHP como JSON sobre HTTPS.
¿Puedo publicar tweets desde PHP 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 PHP, usa la API oficial de X con tokens de usuario de OAuth 1.0a, como el post('tweets', ...) de TwitterOAuth a $0.015 por post, y deja Sorsa API para las lecturas.
¿Puedo usar Sorsa API en WordPress?
Sí. Las propias funciones HTTP de WordPress envían la misma solicitud: wp_remote_post() con el encabezado ApiKey y un cuerpo JSON, y luego wp_remote_retrieve_body() y json_decode(). Guarda el resultado con set_transient() durante unos minutos, para que las visitas a la página nunca se conviertan en solicitudes, y almacena la clave en wp-config.php o en una variable de entorno, nunca en un script que cargue el navegador.
¿Hay un paquete de Composer para Sorsa API?
No hace falta ningún paquete: Sorsa API es REST simple con JSON, así que la extensión cURL, Guzzle o el cliente Http de Laravel cubren sus 45 endpoints, y las funciones de esta guía caben en unas 100 líneas. La referencia OpenAPI en api.sorsa.io/v3/swagger.json lista cada endpoint, así que un generador como openapi-generator puede construir un cliente de PHP 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 y paginación, 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 PHP en PHP 8.4.27 con Guzzle 8.3.1 e illuminate/http 13 de Laravel contra un mock local que responde con las formas documentadas y devuelve 429 pasadas las 20 solicitudes por segundo: las primeras solicitudes, los reintentos, los dos bucles de cursor, los lotes de 100, las cadenas de consulta, la macro de Laravel y parallel(), que envió 100 solicitudes por lotes a 19 por segundo con un 429 como máximo por ejecución, siempre recuperado por su reintento. El mismo día leímos la tabla de precios de X, su página de herramientas, su documentación de timelines y su aviso sobre expansiones, y las versiones de TwitterOAuth, Guzzle y Laravel en Packagist.
Fuentes
- X API pay-per-usage pricing (docs.x.com) (se abre en una pestaña nueva)
- Tools and libraries: official SDKs and community PHP clients (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)
- Getting Access (docs.x.com) (se abre en una pestaña nueva)
- Timelines: up to 3,200 most recent posts (docs.x.com) (se abre en una pestaña nueva)
- TwitterOAuth documentation (se abre en una pestaña nueva)
- abraham/twitteroauth on Packagist (se abre en una pestaña nueva)
- Laravel HTTP client documentation (se abre en una pestaña nueva)
- New Tweets per second record, and how (Twitter Engineering, 2013) (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)