Por Sorsa Editorial
Actualizado: julio de 2026. Agregada la oferta inicial de 100 solicitudes gratis a la guía de inicio y de precios, y refrescada la comparación de costo de la API oficial de X para su precio de pago por uso vigente.
Conclusión clave: Twitter (X) te deja verificar cinco acciones de usuario a través de una API: follows, retweets, tweets citados, comentarios y membresía de comunidad. Cada revisión devuelve un único resultado verdadero/falso. Los likes se volvieron privados en junio de 2024 y ya no pueden ser verificados por ninguna herramienta. La propiedad de la cuenta se prueba haciendo que el usuario publique un código único.
Correr un sorteo, un programa de embajadores, o una campaña de quests que premia acciones en X se rompe en el momento en que los usuarios falsos, las tareas a medio terminar y los bots empiezan a llenar tu formulario. Para el participante trescientos, la revisión manual es un caso perdido y las casillas por sistema de honor no valen nada. Lo que de verdad necesitas es una API que responda, para cada participante, «¿esta persona realmente hizo lo que dijo?». Sorsa API, un proveedor alternativo de API de Twitter/X, expone cada verificación como un endpoint de sí/no: pasa un usuario y una acción, recibe un booleano de vuelta. No hay apretón de manos OAuth ni aprobación de cuenta de desarrollador, solo un encabezado ApiKey; el límite de tasa es de 20 solicitudes por segundo en todos los planes; cada cuenta empieza con 100 solicitudes gratis (por única vez, sin tarjeta requerida, válidas en los 40 endpoints y sin expiración), suficientes para verificar una campaña de prueba completa antes de comprometerte; y con precio de tarifa plana por solicitud (desde $0.0049 en el plan de entrada, bajando a aproximadamente $0.002 en Pro) cuesta una fracción de reconstruir las mismas revisiones en la API oficial de X, que cobra por recurso a lo largo de listas completas. Sorsa es de solo lectura, así que verifica y lee datos públicos pero no publica, no sigue, ni envía DMs; para acciones de escritura seguirías usando la API oficial.
Nosotros construimos y operamos estos endpoints, y a lo largo de docenas de campañas hemos corrido y auditado este patrón, en su mayoría para agencias de creator-marketing y herramientas de sorteos. Los endpoints son simples. Las trampas (cuentas privadas, usuarios suplantados, granjas de bots, la capacidad perdida de revisar likes) no lo son. Esta guía recorre cada revisión con Python funcional, luego las cose en un pipeline de campaña completo: un participante primero, luego verificación masiva para decenas de miles. Si prefieres correr todo el flujo sin escribir código, nuestra solución de verificación de sorteos envuelve estas mismas revisiones detrás de una UI.
Tabla de contenidos
- Qué puedes y qué no puedes verificar en X
- Cómo funciona la verificación en la API oficial de X vs Sorsa
- Revisión 1: ¿El usuario siguió una cuenta?
- Revisión 2: ¿El usuario hizo retweet de un tweet?
- Revisión 3: ¿El usuario citó un tweet?
- Revisión 4: ¿El usuario comentó un tweet?
- Revisión 5: ¿El usuario es miembro de una comunidad?
- Construir un pipeline completo de verificación de campaña
- Verificar participantes en masa
- Verificar la propiedad de la cuenta
- Anti-fraude: revisiones de calidad de cuenta
- Puntuación de recompensa ponderada por influencia
- En la práctica: 47,000 participantes en 14 días
- Costo por participante
- Cómo empezar
- Preguntas frecuentes
Qué puedes y qué no puedes verificar en X {#what-you-can-and-cannot-verify-on-x}
Cinco acciones de usuario en X son verificables a través de una API hoy: un follow, un retweet, un tweet citado, un comentario y unirse a una Comunidad. Cada una mapea a un endpoint y devuelve un booleano. Los likes ya no son verificables por ninguna herramienta, pública o de terceros, porque X los hizo privados en junio de 2024. Las vistas tampoco son verificables.
| Acción | Endpoint | Método | Devuelve | ¿Verificable? |
|---|---|---|---|---|
| El usuario sigue una cuenta | /check-follow | POST | {follow: true/false} | Sí |
| El usuario hizo retweet de un tweet | /check-retweet | POST | {retweet: true/false} | Sí |
| El usuario citó un tweet | /check-quoted | POST | {status: "quoted" / "retweet" / "not_found"} | Sí |
| El usuario comentó un tweet | /check-comment | GET | {commented: true/false, tweet: {...}} | Sí |
| El usuario se unió a una Comunidad de X | /check-community-member | POST | {is_member: true/false} | Sí |
| El usuario le dio like a un tweet | (ninguno) | (ninguno) | (ninguno) | No, privado desde junio de 2024 |
| El usuario vio/generó impresión en un tweet | (ninguno) | (ninguno) | (ninguno) | No |
La restricción de los likes es la sorpresa más común. En junio de 2024, X hizo los likes privados para todos: solo el autor de una publicación puede ver quién le dio like, y ninguna API (incluida la API oficial de X) puede responder «¿el usuario A le dio like al tweet B?» ya. Si tienes plantillas de campaña viejas que incluyen «Dale like a esta publicación», reemplaza esa tarea con un retweet o un comentario. Ambos siguen siendo totalmente verificables y de todos modos producen señales de engagement más fuertes.
Todo lo demás en la tabla de arriba es una sola llamada a la API. La autenticación es un solo encabezado ApiKey (sin flujo OAuth, sin aprobación de app), y la respuesta es JSON plano. El resto de esta guía es el cómo-hacerlo práctico.
Cómo funciona la verificación en la API oficial de X vs Sorsa {#how-verification-works-on-the-official-x-api-vs-sorsa}
La API oficial de X no tiene endpoint que responda directamente si un usuario específico siguió, hizo retweet, citó o comentó. Reconstruyes cada respuesta extrayendo listas completas de seguidores, retweeters o quienes responden y buscando en ellas, cobrado por recurso obtenido. Una API de verificación construida para el propósito, en cambio, devuelve un booleano por revisión en una sola solicitud.
Esta es la razón más grande por la que los equipos recurren a una capa de verificación dedicada, y vale la pena verla lado a lado. Los números de abajo son las tarifas actuales de pago por uso de la API oficial de X y el precio de tarifa plana por solicitud de Sorsa.
| Tarea | API oficial de X (pago por uso) | Sorsa API |
|---|---|---|
| ¿Un usuario siguió una cuenta? | Sin endpoint directo. Pagina la lista completa de seguidores o seguidos de la cuenta y búscala; cada perfil devuelto se cobra como una lectura de usuario ($0.010 cada uno). | Una solicitud /check-follow devuelve follow: true/false. |
| ¿Un usuario hizo retweet de un tweet? | Sin endpoint directo. Extrae la lista completa de retweeters y búscala por el usuario. | Una solicitud /check-retweet devuelve retweet: true/false, con paginación para listas muy grandes. |
| ¿Un usuario citó un tweet? | Sin endpoint directo. Extrae los tweets citados y empareja al autor. | Una solicitud /check-quoted devuelve quoted, retweet o not_found. |
| ¿Un usuario comentó? | Sin endpoint directo. Extrae la lista de respuestas y busca el usuario. | Una solicitud /check-comment devuelve commented: true/false más la respuesta. |
| ¿Un usuario es miembro de una comunidad? | Sin endpoint público equivalente. | Una solicitud /check-community-member devuelve is_member: true/false. |
| Autenticación | OAuth 2.0 con un token Bearer y una app de desarrollador aprobada. | Un solo encabezado ApiKey. Sin cola de aprobación. |
| Cobro | Por recurso obtenido: $0.005 por publicación, $0.010 por perfil de usuario, con un tope mensual de 2,000,000 de lecturas de publicación. | De tarifa plana por solicitud: 1 llamada = 1 solicitud, desde $0.0049 (Starter) hasta aproximadamente $0.002 (Pro), cada endpoint incluido. |
| Límite de tasa | Varía por endpoint, en ventanas fijas. | 20 solicitudes por segundo en todos los planes. |
Hay una segunda ventaja, más silenciosa, de revisar contra la audiencia completa en lugar de una muestra. La interfaz pública de X y la mayoría de los selectores de sorteos gratuitos solo muestran la porción más reciente de una audiencia, a menudo los últimos 100 o así retweeters o quienes responden, así que un ganador sacado de ellos excluye silenciosamente a todos los que participaron antes. La verificación contra la lista completa evita eso: el /check-retweet de Sorsa pagina 100 entradas a la vez a través de toda la lista, y las revisiones directas por usuario responden por un participante específico sin importar dónde se sitúe en la audiencia.
Si tu meta son los datos de engagement subyacentes en lugar de una respuesta de sí/no (la lista completa de quienes responden, quienes citan o retweeters y sus métricas), ese es un trabajo distinto, cubierto en nuestra guía de la API de engagement de Twitter.
Revisión 1: ¿El usuario siguió una cuenta? {#check-1-did-the-user-follow-an-account}
La tarea de campaña más común («Sigue a @YourBrand para participar»). El endpoint /check-follow responde esto directamente. La lógica del endpoint es «¿user_2 sigue a user_1?», así que user_1 es la marca y user_2 es el participante.
Endpoint: POST https://api.sorsa.io/v3/check-follow
Parámetros
Provee un identificador para la marca (la cuenta seguida) y uno para el participante.
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
username_1 | string | Uno de | El usuario de la marca. |
user_link_1 | string | estos | O la URL del perfil de la marca. |
user_id_1 | string | O el ID numérico de usuario de la marca. | |
username_2 | string | Uno de | El usuario del participante. |
user_link_2 | string | estos | O la URL del perfil del participante. |
user_id_2 | string | O el ID numérico de usuario del participante. |
Python
import requests
API_KEY = "YOUR_API_KEY"
BASE = "https://api.sorsa.io/v3"
HEADERS = {"ApiKey": API_KEY, "Content-Type": "application/json"}
def check_follow(brand_handle: str, participant_handle: str) -> dict:
resp = requests.post(
f"{BASE}/check-follow",
headers=HEADERS,
json={"username_1": brand_handle, "username_2": participant_handle},
timeout=15,
)
resp.raise_for_status()
return resp.json()
result = check_follow("YourBrand", "participant123")
if result["follow"]:
print("Follow verified.")
elif result.get("user_protected"):
print("Account is private; follow cannot be confirmed.")
else:
print("Not following.")
Respuesta
{
"follow": true,
"user_protected": false
}
Casos límite que hay que conocer
Si user_protected es true, la cuenta del participante es privada y su grafo de follows no es visible para ningún tercero. Tienes tres opciones: rechazar la entrada, pedir al participante que haga su cuenta pública para la verificación, o usar la verificación de propiedad de cuenta (cubierta abajo) para confirmar que es dueño del usuario y luego aceptar la entrada con una anulación manual. En nuestra experiencia, menos del 1% de los participantes de sorteos tiene cuentas privadas, así que un rechazo duro con un mensaje claro suele estar bien.
Esta revisión responde una dirección de una relación de follow para una campaña. Para revisar un solo follow o follows mutuos fuera del contexto de una campaña, revisa nuestra guía sobre cómo revisar si una cuenta sigue a otra, o corre una revisión puntual en el navegador con la herramienta de revisión de follows sin código.
Revisión 2: ¿El usuario hizo retweet de un tweet? {#check-2-did-the-user-retweet-a-tweet}
«Haz retweet de esta publicación para participar.» Mecánica estándar para impulsar el alcance. El endpoint /check-retweet devuelve un booleano y pagina para listas grandes.
Endpoint: POST https://api.sorsa.io/v3/check-retweet
Parámetros
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
tweet_link | string | Sí | URL del tweet a verificar. |
username | string | Uno de | Usuario del participante. |
user_link | string | estos | O la URL del perfil. |
user_id | string | O el ID numérico de usuario. | |
next_cursor | string | No | Paginación para tweets con > 100 retweets. |
Python
def check_retweet(tweet_link: str, participant_handle: str) -> bool:
cursor = None
for _ in range(5): # check up to 500 retweets total
body = {"tweet_link": tweet_link, "username": participant_handle}
if cursor:
body["next_cursor"] = cursor
resp = requests.post(f"{BASE}/check-retweet", headers=HEADERS, json=body, timeout=15)
resp.raise_for_status()
data = resp.json()
if data["retweet"]:
return True
cursor = data.get("next_cursor")
if not cursor:
return False
return False
Cómo funciona la paginación
Cada llamada escanea los 100 retweets más recientes. Si tu tweet tiene miles de retweets y el usuario hizo retweet temprano, su acción puede estar más abajo en la lista y requerir paginación. El ejemplo de arriba tiene un tope de 5 páginas (los 500 retweets más recientes) para mantener la verificación rápida. Para la mayoría de las campañas esto es más que suficiente porque los participantes tienden a hacer retweet en cuestión de horas después de ver el aviso, así que su acción se sienta en la parte superior de la lista.
Esta es la diferencia práctica frente a sacar un ganador desde la interfaz nativa de X o un selector gratuito, que típicamente solo ven el lote más reciente. Como /check-retweet recorre la lista completa de 100 en 100, un retweeter temprano se encuentra con la misma confiabilidad que uno tardío. La API oficial de X no ofrece una revisión directa equivalente: tendrías que extraer la lista completa de retweeters y buscarla tú mismo, con configuración de OAuth, contabilidad de límite de tasa por ventanas y tu propia lógica de paginación encima.
Revisión 3: ¿El usuario citó un tweet? {#check-3-did-the-user-quote-a-tweet}
«Cita esto con tu opinión.» Esto es más valioso que un retweet simple porque el tweet citado agrega el comentario propio del participante y amplifica la campaña con texto personalizado.
Endpoint: POST https://api.sorsa.io/v3/check-quoted
El endpoint /check-quoted es inteligente para distinguir una cita de un retweet simple, devolviendo uno de tres estados.
Python
def check_quoted(tweet_link: str, participant_handle: str) -> dict:
resp = requests.post(
f"{BASE}/check-quoted",
headers=HEADERS,
json={"tweet_link": tweet_link, "username": participant_handle},
timeout=15,
)
resp.raise_for_status()
return resp.json()
data = check_quoted("https://x.com/YourBrand/status/1234567890", "participant123")
if data["status"] == "quoted":
print(f"Quote verified on {data['date']}: {data['text']}")
elif data["status"] == "retweet":
print("Retweeted without commentary; does not satisfy quote requirement.")
else:
print("No quote or retweet found.")
Por qué importa el texto de la cita
La respuesta incluye el texto completo de la cita y la fecha, que puedes canalizar hacia una revisión de calidad antes de aprobar la entrada. Una campaña que requiere «cita con tu opinión sobre el nuevo producto» merece más que una cita de una palabra como «genial». La mayoría de los equipos que corren estas campañas aplican una regla de caracteres mínimos (típicamente de 30 a 50 caracteres), una revisión de groserías y un hashtag requerido si la campaña usa uno.
def quote_is_acceptable(quote_text: str, min_length: int = 30, required_hashtag: str = None) -> bool:
if len(quote_text.strip()) < min_length:
return False
if required_hashtag and required_hashtag.lower() not in quote_text.lower():
return False
return True
Revisión 4: ¿El usuario comentó un tweet? {#check-4-did-the-user-comment-on-a-tweet}
«Deja un comentario bajo esta publicación.» El único endpoint de verificación que usa GET en lugar de POST.
Endpoint: GET https://api.sorsa.io/v3/check-comment
Parámetros (query string)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
tweet_link | string | Sí | URL del tweet. |
username | string | Uno de | Usuario del participante. |
user_link | string | estos | O la URL del perfil. |
user_id | string | O el ID numérico de usuario. |
Python
def check_comment(tweet_link: str, participant_handle: str) -> dict:
resp = requests.get(
f"{BASE}/check-comment",
headers={"ApiKey": API_KEY},
params={"tweet_link": tweet_link, "username": participant_handle},
timeout=15,
)
resp.raise_for_status()
return resp.json()
data = check_comment("https://x.com/YourBrand/status/1234567890", "participant123")
if data["commented"]:
text = data["tweet"]["full_text"]
print(f"Comment verified: {text[:120]}")
else:
print("No comment found.")
Respuesta y calidad del comentario
Cuando commented es true, la respuesta de /check-comment incluye el objeto de tweet completo del comentario en sí: texto, métricas de engagement, detección de idioma, marca de tiempo. Usa esto para forzar longitud mínima, palabras clave requeridas o rechazar respuestas de spam de solo emoji. En una campaña donde el comentario es toda la tarea de engagement, el estándar de calidad debería ser más alto que un solo emoji.
def comment_is_acceptable(comment: dict, min_length: int = 20, required_keyword: str = None) -> bool:
text = comment.get("full_text", "").strip()
if len(text) < min_length:
return False
if required_keyword and required_keyword.lower() not in text.lower():
return False
# Reject emoji-only or single-word comments
if len(text.split()) < 3:
return False
return True
Revisión 5: ¿El usuario es miembro de una comunidad? {#check-5-is-the-user-a-community-member}
«Únete a nuestra Comunidad de X para participar.» Útil cuando quieres que el participante sea una parte sostenida de la comunidad en lugar de un retweeter de una sola vez.
Endpoint: POST https://api.sorsa.io/v3/check-community-member
def check_community_member(community_id: str, participant_handle: str) -> bool:
resp = requests.post(
f"{BASE}/check-community-member",
headers=HEADERS,
json={"community_id": community_id, "username": participant_handle},
timeout=15,
)
resp.raise_for_status()
return resp.json().get("is_member", False)
is_member = check_community_member("1966045657589813686", "participant123")
print("Member" if is_member else "Not a member")
El ID de comunidad es la cadena numérica larga en la URL de la comunidad (x.com/i/communities/<id>). El endpoint /check-community-member devuelve un booleano limpio. Las comunidades a menudo son una señal más duradera que un retweet de una sola vez porque unirse a una comunidad señala intención de permanecer involucrado.
Construir un pipeline completo de verificación de campaña {#building-a-full-campaign-verification-pipeline}
En una campaña real, los participantes completan varias tareas. Aquí hay un patrón que corre las cinco revisiones para un participante, devuelve un resultado estructurado, y aplica reglas de calidad al comentario y a la cita.
from dataclasses import dataclass, field
@dataclass
class CampaignConfig:
brand_handle: str
tweet_to_retweet: str
tweet_to_quote: str
tweet_to_comment: str
community_id: str
required_hashtag: str = ""
min_quote_length: int = 30
min_comment_length: int = 20
@dataclass
class ParticipantResult:
username: str
follow: bool = False
retweet: bool = False
quote: bool = False
quote_text: str = ""
comment: bool = False
comment_text: str = ""
community: bool = False
completed: int = field(init=False, default=0)
def total(self) -> int:
return sum([self.follow, self.retweet, self.quote, self.comment, self.community])
def verify_participant(username: str, cfg: CampaignConfig) -> ParticipantResult:
r = ParticipantResult(username=username)
# Follow
r.follow = check_follow(cfg.brand_handle, username)["follow"]
# Retweet
r.retweet = bool(check_retweet(cfg.tweet_to_retweet, username))
# Quote tweet (with quality check)
quote_data = check_quoted(cfg.tweet_to_quote, username)
if quote_data["status"] == "quoted":
r.quote_text = quote_data.get("text", "")
r.quote = quote_is_acceptable(r.quote_text, cfg.min_quote_length, cfg.required_hashtag)
# Comment (with quality check)
comment_data = check_comment(cfg.tweet_to_comment, username)
if comment_data.get("commented"):
r.comment_text = comment_data["tweet"].get("full_text", "")
r.comment = comment_is_acceptable(comment_data["tweet"], cfg.min_comment_length)
# Community
r.community = check_community_member(cfg.community_id, username)
r.completed = r.total()
return r
cfg = CampaignConfig(
brand_handle="YourBrand",
tweet_to_retweet="https://x.com/YourBrand/status/111111111",
tweet_to_quote="https://x.com/YourBrand/status/222222222",
tweet_to_comment="https://x.com/YourBrand/status/333333333",
community_id="1966045657589813686",
required_hashtag="#YourLaunch",
)
result = verify_participant("participant123", cfg)
print(f"@{result.username}: {result.completed}/5 tasks done")
Un solo participante cuesta 5 solicitudes de API (una por tarea). Al límite de tasa universal de Sorsa de 20 solicitudes por segundo, un hilo worker puede verificar aproximadamente 4 participantes por segundo de forma secuencial. Para la mayoría de las campañas que corren la verificación una vez por envío, eso es más que suficiente margen.
Verificar participantes en masa {#verifying-participants-in-bulk}
Cuando una campaña tiene miles de participantes y quieres verificarlos en lote (por ejemplo, antes de anunciar ganadores), el patrón se ve así. Nota el manejo del límite de tasa, la salida CSV, y el diseño reanudable (escribe una fila inmediatamente después de cada participante para que una caída no pierda el progreso).
import csv
import time
from pathlib import Path
def verify_campaign_batch(usernames: list[str], cfg: CampaignConfig, output_file: str) -> None:
fields = ["username", "follow", "retweet", "quote", "comment", "community",
"completed", "quote_text", "comment_text"]
already_done = set()
out_path = Path(output_file)
if out_path.exists():
with out_path.open() as f:
already_done = {row["username"] for row in csv.DictReader(f)}
mode = "a" if out_path.exists() else "w"
with out_path.open(mode, newline="") as f:
writer = csv.DictWriter(f, fieldnames=fields)
if mode == "w":
writer.writeheader()
for i, username in enumerate(usernames):
if username in already_done:
continue
try:
r = verify_participant(username, cfg)
writer.writerow({
"username": r.username,
"follow": r.follow,
"retweet": r.retweet,
"quote": r.quote,
"comment": r.comment,
"community": r.community,
"completed": r.completed,
"quote_text": r.quote_text,
"comment_text": r.comment_text,
})
f.flush()
print(f"[{i+1}/{len(usernames)}] @{username}: {r.completed}/5")
except requests.HTTPError as e:
if e.response.status_code == 429:
print("Rate limit hit, sleeping 5s and retrying...")
time.sleep(5)
continue
print(f"[{i+1}] @{username}: ERROR {e}")
time.sleep(0.25) # stay safely under 20 req/s with 5 reqs per participant
participants = open("entries.txt").read().splitlines()
verify_campaign_batch(participants, cfg, "campaign_results.csv")
Este patrón verifica aproximadamente 14,000 participantes por hora en un solo hilo. Si lo paralelizas a lo largo de dos o tres workers (respetando aún el techo global de 20 solicitudes/s), puedes llegar a 30,000 por hora. Para la mayoría de las campañas con menos de 100,000 entradas, la verificación secuencial de un solo hilo termina de la noche a la mañana.
Verificar la propiedad de la cuenta {#verifying-account-ownership}
Antes de que un participante pueda ganar algo, quizá quieras probar que realmente es dueño del usuario de X que envió. Sin este paso, cualquiera puede pegar un usuario famoso en tu formulario y reclamar el premio. El patrón estándar: genera un código único, pide al participante que publique un tweet que lo contenga, luego revisa su línea de tiempo reciente en busca del código.
import secrets
def generate_verification_code(prefix: str = "VERIFY") -> str:
return f"{prefix}-{secrets.token_hex(4)}"
def verify_account_ownership(username: str, expected_code: str) -> bool:
"""Check if the user posted a tweet containing the verification code."""
resp = requests.post(
f"{BASE}/user-tweets",
headers=HEADERS,
json={"username": username},
timeout=15,
)
resp.raise_for_status()
tweets = resp.json().get("tweets", [])
for tweet in tweets:
if expected_code in tweet.get("full_text", ""):
return True
return False
# Workflow
code = generate_verification_code()
print(f"Ask the user to post a tweet containing: {code}")
# ... user posts the tweet ...
if verify_account_ownership("participant123", code):
print("Account ownership confirmed.")
else:
print("Code not found in recent tweets.")
Este es el mismo mecanismo que usan la mayoría de las plataformas serias de sorteos y de embajadores. El participante puede borrar el tweet después de la verificación si quiere, ya que solo necesitas confirmar la publicación una vez.
Anti-fraude: revisiones de calidad de cuenta {#anti-fraud-account-quality-checks}
Las campañas automatizadas atraen bots, y las plataformas que corren mecánicas de quests a gran escala invierten fuertemente en prevención de sybil como resultado. Unas pocas revisiones a nivel de API descartan a los infractores obvios sin necesitar un sistema completo de detección de sybil. Cada una es una llamada adicional a /info.
from datetime import datetime, timezone
def is_legitimate_account(
username: str,
min_age_days: int = 30,
min_tweets: int = 10,
min_followers: int = 5,
) -> tuple[bool, dict]:
resp = requests.get(
f"{BASE}/info",
headers={"ApiKey": API_KEY},
params={"username": username},
timeout=15,
)
resp.raise_for_status()
profile = resp.json()
created = datetime.fromisoformat(profile["created_at"].replace("Z", "+00:00"))
age_days = (datetime.now(timezone.utc) - created).days
checks = {
"account_age_ok": age_days >= min_age_days,
"has_tweets": profile.get("tweets_count", 0) >= min_tweets,
"has_followers": profile.get("followers_count", 0) >= min_followers,
"not_protected": not profile.get("protected", False),
}
return all(checks.values()), checks
Tres observaciones de correr esto en producción:
- Una antigüedad mínima de 30 días atrapa la mayoría de las cuentas de bot recientes. Las granjas de bots típicamente registran cuentas en lotes y las usan en cuestión de días. Un piso de 30 días elimina a la mayoría. Súbelo a 90 días si tu campaña es de alto valor.
- Las cuentas con cero tweets casi siempre son falsas. Un mínimo de 5 a 10 tweets existentes es una señal fuerte de uso humano real.
- El ratio de seguidores a seguidos importa menos de lo que crees. Las personas reales con 50 seguidores y 800 seguidos son comunes (consumidores pasivos). No uses el ratio como filtro primario.
Estos umbrales despejan los bots obvios de forma barata. Para campañas de alto valor donde quieras ir más lejos y puntuar cuánto de la propia base de seguidores de un participante se ve falsa o inactiva, nuestra guía sobre auditar seguidores falsos recorre esa pasada más profunda.
Aplica esta revisión antes de correr cualquiera de las cinco revisiones de verificación. Si is_legitimate_account devuelve False, ahorras 5 solicitudes de verificación en un participante que de todos modos habrías rechazado.
Puntuación de recompensa ponderada por influencia {#influence-weighted-reward-scoring}
No todos los participantes tienen el mismo alcance. Un retweet de un creador con 50,000 seguidores vale más para una campaña de marca que uno de una cuenta con 50. El arreglo directo es ponderar el valor en puntos de cada tarea por una función logarítmica del conteo de seguidores del participante.
import math
BASE_POINTS = {"follow": 10, "retweet": 15, "quote": 25, "comment": 20, "community": 10}
def get_follower_count(username: str) -> int:
resp = requests.get(
f"{BASE}/info",
headers={"ApiKey": API_KEY},
params={"username": username},
timeout=15,
)
resp.raise_for_status()
return resp.json().get("followers_count", 0)
def calculate_weighted_points(result: ParticipantResult) -> dict:
followers = get_follower_count(result.username)
# log scaling: 100 followers -> 2x, 10K -> 4x, 1M -> 6x
multiplier = max(1.0, math.log10(followers + 1))
total = 0
breakdown = {}
for task, base in BASE_POINTS.items():
if getattr(result, task):
points = round(base * multiplier)
breakdown[task] = points
total += points
return {"followers": followers, "multiplier": round(multiplier, 2),
"breakdown": breakdown, "total": total}
El resultado: una cuenta con 50 seguidores que completa las cinco tareas gana unos 80 puntos. Una cuenta con 50,000 seguidores que completa las mismas tareas gana unos 380 puntos. La campaña premia el alcance de forma proporcional sin pagarles a las microcelebridades lo mismo que a las cuentas de alcance cero.
Para campañas con sabor cripto, puedes reemplazar el multiplicador de conteo de seguidores con el Sorsa Score, que mide el reconocimiento de una cuenta entre los KOLs, proyectos y VCs de cripto. Dos cuentas pueden tener conteos de seguidores similares pero Sorsa Scores muy diferentes si una es una voz cripto-nativa y la otra es una cuenta de interés general.
En la práctica: 47,000 participantes en 14 días {#in-practice-47000-participants-in-14-days}
Una agencia de creator-marketing con la que trabajamos lanzó un sorteo de 14 días para una marca de artículos para el hogar directa al consumidor. La mecánica de la campaña era estándar: seguir a la marca, hacer retweet del tweet de lanzamiento, citarlo con un hashtag de marca, comentar en un segundo tweet y unirse a su Comunidad de X. Tres ganadores recibirían cada uno la renovación amueblada de una habitación valuada en aproximadamente $4,500.
Los envíos llegaban vía una landing page de campaña. Para el día 14 tenían 47,000 entradas.
Las campañas previas de la agencia, corridas con casillas por sistema de honor, típicamente veían de 50 a 60 por ciento de completados falsos o parciales, lo que requería días de revisión manual antes de anunciar ganadores. Esta vez usaron el pipeline de verificación por API de arriba. Números de la corrida:
- 47,000 envíos totales
- 235,000 solicitudes de verificación (5 por participante)
- 47,000 llamadas
/infoadicionales para los pasos de anti-fraude y de ponderación por influencia - Uso total de API: ~282,000 solicitudes a lo largo de la ventana de la campaña
- Plan usado: Enterprise (500K solicitudes/mes a $899)
- 31,200 participantes pasaron las 5 tareas
- 8,400 participantes pasaron de 3 a 4 tareas (elegibles para el nivel de premio parcial)
- 7,400 participantes rechazados de plano (fallaron la revisión de calidad de cuenta o completaron de 0 a 2 tareas)
- Tiempo de moderación manual ahorrado: ~120 horas (su estimación, basada en campañas pasadas a escala similar)
El costo de correr la verificación a esta escala (un mes del plan Enterprise) fue menos que un solo día de tiempo de moderador. La agencia ahora usa el mismo pipeline como plantilla para cada campaña de marca que corre.
Divulgación: Sorsa API es nuestro producto, y las cifras de arriba describen un despliegue de cliente anonimizado y compuesto en lugar de un compromiso con nombre. Las afirmaciones técnicas son precisas y la ventaja de costo es una propiedad real del precio de tarifa plana por solicitud; para tu propia campaña, corre un piloto pequeño antes de comprometerte con un flujo de trabajo.
Costo por participante {#cost-per-participant}
Una verificación completa de cinco tareas, con anti-fraude y puntuación por influencia encima, toma 7 solicitudes de API por participante:
- 1 solicitud para
/info(anti-fraude más conteo de seguidores para la puntuación) - 5 solicitudes para las cinco revisiones de verificación
- 1 solicitud opcionalmente para la verificación de propiedad de cuenta (si se implementa)
Sorsa usa precio de tarifa plana: 1 llamada a la API = 1 solicitud de la cuota mensual, sin importar el endpoint. En el plan Pro ($199/mes, 100,000 solicitudes), eso son aproximadamente 14,000 participantes totalmente verificados al mes. En Enterprise ($899/mes, 500,000 solicitudes), unos 71,000. Hay planes personalizados disponibles por encima de ese techo.
| Tamaño de la campaña | Solicitudes necesarias | Plan recomendado | Precio del plan |
|---|---|---|---|
| Hasta 1,400 participantes | ~10,000 | Starter | $49/mes |
| Hasta 14,000 participantes | ~100,000 | Pro | $199/mes |
| Hasta 71,000 participantes | ~500,000 | Enterprise | $899/mes |
| 71,000+ participantes | Personalizado | Contactar a ventas | Personalizado |
A tarifas del plan Pro, una verificación completa de 7 solicitudes cuesta aproximadamente $0.014 por participante. Para una campaña de 10,000 participantes, eso son aproximadamente $140 en solicitudes de API.
El contraste con la API oficial de X viene del modelo de cobro, no de un solo precio de titular. X cobra por recurso obtenido, $0.005 por publicación y $0.010 por perfil de usuario, y no tiene endpoint directo de revisión, así que verificar acciones significa extraer listas completas de retweeters y seguidores y pagar por cada ítem en ellas, bajo un tope mensual de 2 millones de lecturas de publicación y OAuth 2.0. Sorsa cobra una tarifa plana por cada llamada /check-* sin importar qué tan grande sea la audiencia subyacente. El desglose completo por plan y por endpoint para ambos proveedores vive en nuestra guía de precios de la API de Twitter, y si todavía estás sopesando opciones, nuestro repaso de alternativas a la API de Twitter compara el campo más amplio en costo y capacidades.
Cómo empezar {#getting-started}
Puedes tener una revisión de verificación funcional corriendo en unos minutos. No hay aprobación de cuenta de desarrollador que esperar ni flujo OAuth que conectar: crea una clave, ponla en el encabezado ApiKey, y llama a /check-follow. Cada cuenta empieza con 100 solicitudes gratis, una asignación por única vez que no necesita tarjeta, nunca expira, y cubre los 40 endpoints, así que puedes verificar un pequeño lote de prueba antes de pagar nada. Después de eso el plan de entrada es $49 por 10,000 solicitudes, cada endpoint está incluido en cada nivel, y el límite de tasa es de 20 solicitudes por segundo.
- Prueba los endpoints sin código en el playground de la API.
- Lee el quickstart para hacer tu primera solicitud autenticada.
- Sigue el recorrido de verificación de campañas para el pipeline de extremo a extremo en los docs.
- Para campañas por encima de 500,000 solicitudes al mes o un límite de tasa más alto, habla con ventas.
Para el menú completo de endpoints de verificación y formas de respuesta, revisa la referencia de endpoints de verificación.
Preguntas frecuentes {#faq}
¿Puedo verificar los likes de Twitter por API?
No. X hizo los likes privados en junio de 2024, y a partir de 2026 ninguna API pública ni de terceros puede responder si un usuario le dio like al tweet de otro. Este es un cambio a nivel de plataforma, no una limitación de Sorsa. Si tu plantilla de campaña todavía pide likes, reemplaza esa tarea con un retweet o un comentario, ambos siguen siendo totalmente verificables y cargan señales de engagement más fuertes.
¿Necesito OAuth o aprobación de cuenta de desarrollador para verificar acciones de Twitter?
No con Sorsa API. La autenticación es un solo encabezado ApiKey, sin apretón de manos OAuth, sin URLs de callback, y sin revisión de app ni cola de aprobación. La API oficial de X sí requiere OAuth 2.0 y una app de desarrollador aprobada, y cobra por recurso obtenido, lo que hace la verificación a escala más lenta de configurar y más cara que un modelo de tarifa plana por solicitud.
¿Puedo revisar si alguien hizo retweet de un tweet que tiene miles de retweets?
Sí. El endpoint /check-retweet escanea 100 retweets por llamada y devuelve un next_cursor para paginar. El código de ejemplo de esta guía tiene un tope de 5 páginas (500 retweets), que suele ser suficiente porque los participantes normalmente hacen retweet en cuestión de horas después de recibir el aviso. Para tweets donde necesites escanear más profundo, sube el tope de páginas y la revisión sigue recorriendo la lista completa.
¿Cómo verifico que alguien realmente es dueño del usuario de Twitter que ingresó?
Genera un código corto único, pide al participante que publique un tweet que lo contenga, luego usa el endpoint user-tweets para escanear su línea de tiempo reciente en busca de ese código. Hay un ejemplo funcional en la sección de propiedad de cuenta de esta guía. Este es el patrón estándar que usan las plataformas serias de sorteos y de embajadores para evitar que la gente ingrese un usuario que no controla.
¿Qué pasa si un participante tiene una cuenta privada (protegida)?
Las respuestas de check-follow y check-quoted incluyen una bandera user_protected. Cuando es true, el grafo de follows de la cuenta no está expuesto y no puedes confirmar de forma programática un follow o una cita. Tus opciones son pedir al participante que haga su perfil público para la verificación, rechazar la entrada con un mensaje claro, o correr la verificación de propiedad de cuenta y aceptar con una anulación manual. En una campaña típica, menos del 1% de los participantes tiene cuentas privadas.
¿Cómo evito que los bots hagan trampa en mi sorteo?
Usa tres capas de defensa a nivel de API: una revisión de calidad de cuenta vía el endpoint /info para antigüedad mínima, conteo de tweets y seguidores; revisiones de calidad de comentario y cita para longitud mínima y un hashtag requerido; y la verificación de propiedad de cuenta antes de que salga cualquier premio. Ninguna de estas es un sistema completo de detección de sybil, pero juntas eliminan los casos de victoria fácil que drenan la mayoría de las campañas, y cada cuenta rechazada también te ahorra las solicitudes de verificación que habrías gastado en ella.
¿Verificar el engagement es más barato que usar la API oficial de X?
Para la verificación en específico, sí. La API oficial de X no tiene un endpoint directo de revisión, así que reconstruyes cada respuesta extrayendo listas completas de retweeters o seguidores y pagando por recurso, $0.005 por publicación y $0.010 por perfil, bajo un tope de 2 millones de lecturas de publicación. Sorsa cobra una tarifa plana por revisión sin importar el tamaño de la audiencia, desde $0.0049 hasta unos $0.002 por solicitud, así que una verificación completa de campaña cuesta una fracción pequeña del costo.
¿Puedo usar estas revisiones para casos de uso no de marketing?
Sí. Los usos comunes fuera del marketing incluyen restringir el acceso a un canal privado de Discord verificando que un usuario sigue a la marca antes de otorgar un rol, rastrear el cumplimiento de amplificación social de empleados o socios, y validar reclamos de atribución enviados por usuarios en programas de afiliados. Cada uno es la misma revisión booleana única, solo que aplicada fuera del contexto de un sorteo.
Revisado por Keksich, fundador de Sorsa, marketer e investigador de la API de X.
Cómo se armó esta guía: se apoya en nuestro trabajo práctico construyendo y operando los endpoints de verificación de Sorsa, en llamadas en vivo contra la API misma, y en la documentación de Sorsa API para los detalles de endpoints y respuestas. La comparación de costo usa el precio actual de pago por uso de la API oficial de X y sus páginas de políticas como referencia de sus tarifas, el tope de 2 millones de lecturas de publicación, y su requisito de OAuth. Verificado en junio de 2026.