En esta página
- Descripción general
- Primeros pasos
- Automatizar con un agente
- Autenticación
- Tokens y precios
- API de panel SMM
- services
- add
- status
- balance
- cancel
- API de la plataforma
- Catálogo
- Cuenta
- Doble factor e inicio de sesión
- Preferencias y avisos
- Campañas
- Crear un pedido
- Editar un pedido
- Registros y analíticas
- Perfiles de voto y proxy
- Tokens de Discord
- Facturación y pagos
- Carrito guardado
- Superficies internas
- Formatos de respuesta
- Objeto sitio
- Objeto campaña
- Estado de la campaña
- Progreso y reembolsos
- Ejemplo de principio a fin
- Errores
- Límites de frecuencia
- Límites y notas
Descripción general
Toplistbot expone dos APIs HTTP. Ambas son JSON sobre HTTPS, ambas gastan el mismo saldo de tokens, y cualquiera de las dos basta para lanzar campañas sin abrir nunca el panel.
API de panel SMM
Un endpoint compatible con Perfect Panel. Si tu panel ya habla el protocolo SMM estándar, apúntalo aquí y funciona sin tocar código.
API de la plataforma
La API REST que hay detrás del panel: explorar el catálogo, crear y dirigir campañas, leer registros de entrega voto a voto, gestionar perfiles, proxies y facturas.
URL base
https://backend.toplistbot.com/api
https://backend.toplistbot.comLa referencia siguiente está escrita contra estos dos hosts. El que uses decide cómo te autenticas — consulta Autenticación.
Primeros pasos
De una cuenta nueva a una campaña en marcha en cinco pasos. Todo lo de abajo usa el endpoint SMM, la vía más rápida; la API de la plataforma funciona igual una vez que tengas un JWT.
Crea una cuenta
Regístrate y verifica tu correo. La verificación abona 100 tokens gratis en tu saldo, suficiente para lanzar una campaña real antes de gastar nada.
Copia tu clave de API
Abre tu panel y genera una clave de API. Trátala como una contraseña: gasta tu saldo de tokens. Puedes regenerarla cuando quieras, lo que invalida la anterior al instante.
Encuentra el servicio que quieres
Lista todos los sitios en los que puedes hacer pedidos. Cada entrada tiene un id numérico de servicio y una tarifa en tokens por cada 1.000 acciones. Anota el id del sitio en el que quieres promocionar.
cURLcurl -X POST https://backend.toplistbot.com/api/v2 -d "key=YOUR_API_KEY" -d "action=services"Haz tu primer pedido
Envía el id del servicio, la URL sobre la que se ejecuta la campaña y cuántas acciones lanzar. El coste se descuenta al momento y la respuesta te da un id de pedido.
cURLcurl -X POST https://backend.toplistbot.com/api/v2 \ -d "key=YOUR_API_KEY" \ -d "action=add" \ -d "service=9" \ -d "link=https://arena-top100.com/index.php?a=in&u=yourserver" \ -d "quantity=1000"Sigue la entrega
Consulta el id del pedido para ver cuánto se ha entregado. Cuando el flujo te convenza, conecta las mismas llamadas a tu propio panel o a tus scripts.
cURLcurl -X POST https://backend.toplistbot.com/api/v2 -d "key=YOUR_API_KEY" -d "action=status" -d "orders=184223"
Conectar una instancia de Perfect Panel
Si usas Perfect Panel o software de panel SMM compatible, no necesitas escribir código: añade Toplistbot como proveedor con estos ajustes e importa la lista de servicios.
- URL de la API
- https://backend.toplistbot.com/api/v2
- Clave de API
- YOUR_API_KEY
- Método HTTP
- POST
Empieza con una cantidad pequeña en un solo sitio para confirmar que se acepta el formato de tu enlace antes de aumentar el volumen. Un enlace incorrecto también consume tokens.
Automatizar con un agente de IA
Esta página tiene un gemelo en texto plano escrito para máquinas. Dale esa URL a un agente junto con tu clave API y tendrá todo lo necesario: la lista completa de endpoints, las formas de petición y respuesta, la aritmética de precios, los códigos de error y ejemplos completos.
Referencia legible por máquinas
Un solo documento, sin autenticación y sin JavaScript. Descárgalo, pégalo en un prompt o pasa la URL a una herramienta que sepa navegar.
https://toplistbot.com/llms.txtPrompt inicial
Pega esto en Claude o en cualquier agente capaz de hacer peticiones HTTP. Guarda la clave en una variable de entorno en lugar de escribirla en el mensaje.
Read https://toplistbot.com/llms.txt — it is the complete Toplistbot API reference.
My API key is in the TOPLISTBOT_KEY environment variable. Using the API-key
surface (paths without the /api prefix):
1. list the sites in the catalog that cost under 20 tokens per 1,000 votes
2. tell me my token balance
3. propose a campaign for <my vote URL> that fits a budget of <N> tokens
Do not place the order until I confirm the cost.Una clave API es la credencial adecuada para un agente: no caduca, no le afecta la autenticación en dos pasos y rotarla desde el panel revoca el acceso al instante si alguna vez lo necesitas.
Autenticación
Hay dos credenciales, y cuál necesitas depende de la ruta más que del endpoint. Casi todos los endpoints están montados dos veces.
El prefijo /api decide la credencial
El mismo manejador atiende ambas rutas. Quita el prefijo /api y la API de plataforma acepta una clave API de larga duración; mantenlo y el endpoint espera un JWT obtenido al iniciar sesión.
| Ruta | Credencial | Úsala para |
|---|---|---|
| /api/orders/getAll | JWT | Cualquier cosa donde una persona inicie sesión |
| /orders/getAll | Clave API | Scripts, tareas programadas, agentes |
Para automatizar, usa mejor las rutas sin prefijo. No hay inicio de sesión, ni caducidad, ni sesión que mantener viva — una sola clave lo hace todo, y la autenticación en dos pasos nunca se interpone.
Clave API
Envía tu clave como campo `key` en cualquier petición: parámetro de consulta, campo de formulario, campo JSON o cabecera `Authorization: Bearer`. Genérala y rótala desde el panel. Un GET a /api/v2 devuelve un estado ok y es una forma barata de comprobar que la clave sigue viva.
# any of these three carry the key
curl "https://backend.toplistbot.com/orders/getAll?key=YOUR_API_KEY"
curl -X POST https://backend.toplistbot.com/orders/pause -d "key=YOUR_API_KEY" -d "id=184223"
curl https://backend.toplistbot.com/orders/getAll -H "Authorization: Bearer YOUR_API_KEY"curl https://backend.toplistbot.com/api/v2?key=YOUR_API_KEYJWT
Inicia sesión para recibir un token y envíalo como bearer token en las rutas /api. Los tokens caducan, así que llama a /auth/refresh antes de que lo hagan. Si la cuenta tiene la autenticación en dos pasos activada, el inicio de sesión también necesita `two_factor_code`.
curl -X POST https://backend.toplistbot.com/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"..."}'{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"token_type": "bearer",
"expires_in": 3600,
"user": { "id": 4211, "email": "[email protected]", "tokens": 528.41, "...": "..." }
}curl https://backend.toplistbot.com/api/orders/getAll \
-H "Authorization: Bearer YOUR_JWT"Llamar desde un navegador
Todas las rutas responden con Access-Control-Allow-Origin abierto, así que una página, una extensión del navegador o un agente que corra en el navegador pueden llamar a la API directamente, sin que montes un proxy propio. La advertencia de arriba sigue en pie: una clave que envías al navegador es una clave que has publicado, así que esto es para tus propias herramientas, no para una página pública.
// Works from a page, an extension, or a browser-based agent.
const sites = await fetch(
'https://backend.toplistbot.com/orders/getAllWebsites'
).then(r => r.json())Tu clave API gasta saldo real de tokens. Mantenla en el servidor: cualquier clave enviada a un navegador o subida a un repositorio debe considerarse comprometida y rotarse desde el panel.
Tokens y precios
Las campañas se pagan en tokens, comprados por adelantado. Cada sitio publica una tarifa —cuántos tokens cuesta lanzar 1.000 acciones de campaña allí— que la acción services devuelve como `rate`.
cost_in_tokens = (rate * quantity) / 1000Un sitio con tarifa 13 cuesta 13 tokens por 1.000 acciones, así que un pedido de 500 cuesta 6,5 tokens. El coste se descuenta al aceptar el pedido, y cancelar devuelve el resto no gastado.
Las respuestas de balance y status indican un campo de moneda USD por compatibilidad con Perfect Panel, pero el valor es un saldo de tokens, no dólares. Trata el número como tokens.
API de panel SMM
Un solo endpoint lo hace todo. Envía un campo `action` en cada POST para elegir la operación; toda petición lleva además tu `key`.
https://backend.toplistbot.com/api/v2| Acción | Parámetros |
|---|---|
services | — |
add | service, link, quantity, interval? |
status | orders |
balance | — |
cancel | orders |
`refill` y `refill_status` se aceptan por compatibilidad y ambas responden "no implementado". Aquí nada es recargable; vuelve a pedir en su lugar.
action=services
Lista todos los sitios en los que puedes hacer pedidos, con su tarifa actual y sus límites. Usa el id `service` en tus llamadas add.
curl -X POST https://backend.toplistbot.com/api/v2 \
-d "key=YOUR_API_KEY" \
-d "action=services"[
{
"service": 9,
"name": "arena-top100.com 1000 upvotes",
"type": "Default",
"category": "Votes",
"rate": 15,
"min": 1,
"max": 50000,
"refill": false,
"cancel": true
}
]`rate` va en tokens por cada 1.000 acciones. `min` es 1 y `max` es 50000 en todos los servicios.
action=add
Crea una campaña y descuenta su coste de tu saldo de inmediato.
| Parámetro | Tipo | Descripción |
|---|---|---|
keyobligatorio | string | Tu clave de API. |
actionobligatorio | string | Debe ser `add`. |
serviceobligatorio | integer | Id de servicio de la acción services. |
linkobligatorio | url | La URL sobre la que se ejecuta la campaña. Debe ser una URL válida. |
quantityobligatorio | integer | Número de acciones a lanzar, entre 1 y 50000. |
interval | integer | Acciones por hora. Por defecto 15, con tope de 4000, y no puede superar el máximo propio del sitio. |
curl -X POST https://backend.toplistbot.com/api/v2 \
-d "key=YOUR_API_KEY" \
-d "action=add" \
-d "service=9" \
-d "link=https://arena-top100.com/index.php?a=in&u=yourserver" \
-d "quantity=1000" \
-d "interval=60"{
"order_id": 184223
}action=status
Devuelve el progreso de uno o varios pedidos. Pasa un solo id para obtener un objeto simple, o una lista separada por comas.
curl -X POST https://backend.toplistbot.com/api/v2 \
-d "key=YOUR_API_KEY" \
-d "action=status" \
-d "orders=184223"{
"charge": 13.5,
"start_count": 0,
"status": "Completed",
"remains": 1000,
"currency": "USD"
}Con varios ids la respuesta se indexa por id de pedido, y los pedidos desconocidos o ajenos devuelven una entrada de error en lugar de hacer fallar toda la petición.
{
"184223": { "charge": 13.5, "start_count": 0, "status": "Completed", "remains": 1000, "currency": "USD" },
"184224": { "error": "Incorrect order ID" }
}Lee `remains`, no `status`
`status` siempre vale literalmente "Completed". El campo existe porque todo cliente Perfect Panel lo exige, y los paneles tratan cualquier otro valor como candidato a recarga, algo que esta plataforma no ofrece. El progreso está en los números: `remains` son los votos aceptados que quedan por entregar, así que `remains == 0` significa que el pedido ha terminado. `start_count` es lo entregado hasta ahora y `charge` lo que ha costado. Ambos se miden contra la tasa de aceptación del sitio, así que cuentan los votos que compraste y no los intentos en bruto.
`status` y `cancel` aceptan como máximo 100 ids de pedido por llamada. Agrupa en lugar de iterar: una llamada con 100 ids es mucho más barata para ambas partes que 100 llamadas.
action=balance
Devuelve tu saldo restante de tokens.
curl -X POST https://backend.toplistbot.com/api/v2 \
-d "key=YOUR_API_KEY" \
-d "action=balance"{
"balance": 528.41,
"currency": "USD"
}action=cancel
Detiene un pedido y devuelve el resto no gastado a tu saldo. Los pedidos completados no se pueden cancelar.
curl -X POST https://backend.toplistbot.com/api/v2 \
-d "key=YOUR_API_KEY" \
-d "action=cancel" \
-d "orders=184223,184224"[
{ "order": "184223", "cancel": 1, "refund": 4.5 },
{ "order": "184224", "cancel": { "error": "Incorrect order ID" } }
]API de la plataforma
La misma API REST que usa el panel. Las rutas de abajo están escritas en su forma con clave API, sin el prefijo /api. Añade /api y cambia la clave por un JWT para usar la forma con sesión; los endpoints marcados como JWT solo existen bajo /api.
Catálogo y descubrimiento
Público, sin credencial. getAllWebsites es el endpoint por el que empezar: trae el id, el precio, el techo por hora y la tasa de aceptación que necesitas para calcular el precio y dar forma a un pedido.
- GET
/orders/getAllWebsitesPúblicaEl catálogo completo: cada sitio con tarifas, límites y metadatos - GET
/orders/getAllBasicWebsitesDetailsPública20 nombres de sitio al azar, para widgets y autocompletados - POST
/orders/getWebsiteDetailsByNamePúblicaUn sitio por su nombre exacto - POST
/products/getSuggestionsPúblicaSitios relacionados con un conjunto de ids - GET
/products/demand?days=30PúblicaCuánto se ha pedido cada sitio últimamente - GET
/products/tokensPúblicaPaquetes de tokens que puedes comprar - POST
/products/suggestClave APIPídenos que añadamos un sitio nuevo - GET
/api/news/timelinePúblicaNovedades del producto
curl "https://backend.toplistbot.com/orders/getAllWebsites"Pide menos
El catálogo completo pesa unos 665 KB repartidos en 396 sitios, y dos campos con los que nunca vas a pedir son la mitad de ese peso: un bloque JSON de popularidad y la descripción de marketing. Proyecta solo los campos que usas para pedir y descarta los sitios inactivos y se queda en unos 40 KB.
# The whole catalog is ~665 KB across 396 sites.
# Projected to what you actually order with: ~40 KB.
curl -s "https://backend.toplistbot.com/orders/getAllWebsites" \
| jq '[.[]
| select(.active == 1)
| {id, name, price_per_1000, max_per_hour, accept_rate, subscribeable}]'Para un agente de IA esa es la diferencia entre unos 170.000 tokens y 10.000: entre que la primera llamada funcione o que agote la ventana de contexto. Proyecta antes de analizar.
Cuenta y sesiones
El registro necesita un navegador: está protegido por un desafío de Cloudflare. Regístrate una vez en app.toplistbot.com y automatiza todo lo demás.
- POST
/api/auth/registerPúblicaCrear una cuenta: solo por navegador, protegido por captcha - POST
/api/auth/loginPúblicaCambiar credenciales por un JWT - POST
/api/auth/refreshJWTEmitir un JWT nuevo a partir de uno que caduca - POST
/api/auth/logoutJWTInvalidar el JWT actual - GET
/api/auth/user-profileJWTLa cuenta conectada, con saldo y clave API - GET
/api/userJWTEl mismo objeto de usuario, en una ruta más corta - GET
/api/api_tokenClave APIResolver una clave API a su dueño: úsalo para validar una clave - POST
/api/auth/reset-api-keyJWTRotar tu clave API; la anterior muere al instante - POST
/api/auth/fingerprintJWTRegistrar una huella de navegador en la cuenta - POST
/api/auth/ipJWTRegistrar la IP actual de la cuenta - POST
/api/auth/forgot-passwordPúblicaEnviar por correo un enlace de restablecimiento, válido 60 minutos - POST
/api/auth/reset-passwordPúblicaFijar una contraseña nueva con el token enviado por correo
Doble factor e inicio de sesión
El doble factor protege el inicio de sesión con contraseña. No se aplica a las claves API, y por eso una clave es la mejor credencial para trabajo desatendido.
- POST
/api/2fa/enableJWTIniciar el alta: devuelve el secreto, la URL del QR y los códigos de recuperación - POST
/api/2fa/verifyJWTConfirmar un código de seis dígitos y activar el doble factor - POST
/api/2fa/disableJWTDesactivar el doble factor - POST
/api/account/verification/requestJWTEnviar un enlace de verificación a la dirección conectada - GET
/api/account/verification/confirm?token=PúblicaMostrar la página de confirmación: no escribe nada - POST
/api/account/verification/confirmPúblicaConfirmar la verificación - GET
/api/auth/googlePúblicaIniciar sesión con Google - GET
/api/auth/google/callbackPúblicaRetorno del inicio de sesión con Google - GET
/api/auth/discordPúblicaIniciar sesión con Discord - GET
/api/auth/discord/callbackPúblicaRetorno del inicio de sesión con Discord
Preferencias y avisos
Interruptores de correo y notificaciones, y el historial de avisos de la cuenta.
- GET
/api/user/email-preferencesJWTEstado de suscripción a correos de marketing - POST
/api/user/email-preferencesJWTCambiarlo - GET
/api/user/notification-preferencesJWTPreferencia de avisos de voto en vivo - POST
/api/user/notification-preferencesJWTCambiarlo: debe ser un booleano JSON real - GET
/api/user/alerts?limit=20JWTAvisos de la cuenta, del más reciente al más antiguo, paginados con ?before - POST
/api/user/alerts/readJWTMarcar un aviso como leído - POST
/api/user/alerts/dismissJWTDescartar un aviso - GET
/api/email/unsubscribe?token=PúblicaBaja en un clic desde un token enviado por correo
Campañas
Crear campañas, dirigirlas mientras corren y cerrarlas. Es el núcleo de la API de plataforma.
- GET
/orders/getAllClave APITus campañas, de la más reciente a la más antigua, con su sitio incluido - GET
/orders/get/{id}Clave APIUna campaña - POST
/orders/checkoutClave APICrear campañas y cobrar del saldo - POST
/orders/updateClave APIEditar una campaña - POST
/orders/pauseClave APIPausar una campaña en marcha - POST
/orders/unpauseClave APIReanudar una campaña pausada - POST
/orders/archiveClave APIArchivar una campaña - POST
/orders/unarchiveClave APIRestaurar una campaña archivada - PATCH
/orders/updateLimitClave APIFijar o quitar el tope diario de votos
POST /orders/checkout
El cuerpo es un array JSON de líneas de carrito en el nivel superior, no un objeto. Cada línea es una campaña. Todo el carrito se valida antes de cobrar nada, y el cobro y las inserciones son una sola transacción: un pedido ocurre entero o no ocurre.
Una línea de cantidad fija
El caso habitual: entregar un número concreto de votos a una URL.
| Parámetro | Tipo | Descripción |
|---|---|---|
idobligatorio | integer | Id del sitio, de /orders/getAllWebsites. |
amountobligatorio | integer | Votos a entregar. 0 o más, hasta 2.147.483.647. |
ownNameobligatorio | url | La URL de voto. Se guarda como el campo `url` del pedido. |
custom_max_per_hour | integer | Techo de entrega, limitado al máximo del propio sitio. |
extra_col | string | Campo de texto libre que viaja con el pedido. |
curl -X POST "https://backend.toplistbot.com/orders/checkout?key=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '[
{
"id": 9,
"amount": 1000,
"ownName": "https://arena-top100.com/index.php?a=in&u=yourserver",
"custom_max_per_hour": 60
}
]'200 OK
Successfully purchased with token balanceUna línea de suscripción
Para sitios cuyo indicador `subscribeable` vale 1. Se cobra a partir de subscription_price_1d del sitio multiplicado por los días y por el descuento del nivel: Weekly es 0,90, Monthly es 0,80 y cualquier otro es 1,00. La cantidad entregada se deriva en el servidor a partir del propio subscription_speed del sitio, así que nada de lo que envíes la cambia.
[
{
"type": "subscription",
"website": { "id": 9 },
"subscription_days": 30,
"tier": { "name": "Monthly" },
"url": "https://arena-top100.com/index.php?a=in&u=yourserver"
}
]Respuestas
200Todas las líneas se crearon y se cobró del saldo. El cuerpo es texto plano.400El cuerpo no era JSON válido.402Saldo insuficiente. El mensaje indica cuántos tokens faltan y no se cobró nada.422Una o más líneas están mal. No se cobró nada.429El mismo carrito se envió en los últimos 60 segundos. Reintenta tras la espera indicada.
Un 422 señala la línea culpable: los errores se indexan como items.[index].[field], así que un carrito con tres líneas malas se arregla en un viaje y no en tres.
{
"errors": {
"items.2.amount": ["Enter 0 or more votes; a negative amount is not allowed."]
}
}POST /orders/update
`id` es obligatorio; envía solo los campos que cambias. Los pedidos de suscripción no se pueden modificar.
| Parámetro | Tipo | Descripción |
|---|---|---|
idobligatorio | integer | La campaña a editar. |
amount_to_do | integer | Nuevo total de votos. Subirlo cobra la diferencia, bajarlo la devuelve, y hay 30 segundos de espera entre cambios. |
url | url | La URL de voto. |
custom_name | string | Tu propia etiqueta para la campaña. |
custom_max_per_hour | integer | Techo de entrega. |
username_profile_id | integer | Asociar un perfil de voto. |
proxy_profile_id | integer | Asociar un perfil de proxy. |
http_referral | url | Referente a enviar con cada voto. |
extra_col | string | Campo de texto libre. |
curl -X POST "https://backend.toplistbot.com/orders/update?key=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"id":184223,"amount_to_do":2000,"custom_name":"EU launch"}'Tope diario
`type: "delete"` quita el tope. En ese caso el validador sigue exigiendo `max_votes_per_day`: envía cualquier entero.
curl -X PATCH "https://backend.toplistbot.com/orders/updateLimit?key=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"id":184223,"max_votes_per_day":500,"type":"set"}'Registros y analíticas
Datos de entrega, voto a voto y agregados. Leen una base de datos de registros aparte y son más lentos que el resto de la API: consúltalos cada minutos, no cada segundos.
- GET
/orders/logs/{id}Clave APIRegistro de entrega voto a voto de una campaña - GET
/orders/graph/{id}Clave APISerie temporal de una campaña, lista para graficar - GET
/api/orders/graph/summaryJWTUna serie que agrupa todas tus campañas - GET
/orders/grouped/usernames/{id}Clave APIEntregas agrupadas por el usuario que votó - POST
/orders/averageClave APIEntrega media de varias campañas - GET
/api/logs/{id}/filtered-graphJWTSerie temporal filtrada
Perfiles de voto y proxy
Un perfil de voto es una lista con nombre de usuarios con los que vota una campaña. Un perfil de proxy es una lista de países permitidos para las IP que usa. Asocia cualquiera de los dos a una campaña con username_profile_id o proxy_profile_id en /orders/update.
- GET
/advanced/profile/getClave APITus perfiles de voto - GET
/advanced/profile/get/{id}Clave APIUn perfil de voto - POST
/advanced/profile/createClave APICrear un perfil de voto, o sobrescribir uno por id - DELETE
/advanced/profile/delete/{id}Clave APIBorrar un perfil de voto - GET
/advanced/profile/proxy/getClave APITus perfiles de proxy - GET
/advanced/profile/proxy/get/{id}Clave APIUn perfil de proxy - POST
/advanced/profile/proxy/createClave APICrear un perfil de proxy, o sobrescribir uno por id - DELETE
/advanced/profile/proxy/delete/{id}Clave APIBorrar un perfil de proxy
Tokens de Discord
Para listas que autentican a los votantes a través de Discord. Añadir el mismo token dos veces se rechaza como duplicado.
- GET
/api/discord-tokensJWTTus tokens de Discord - POST
/api/discord-tokensJWTAñadir un token - GET
/api/discord-tokens/statsJWTUso acumulado de tus tokens - GET
/api/discord-tokens/{id}JWTUn token - PATCH
/api/discord-tokens/{id}JWTCambiar un token o su estado activo - DELETE
/api/discord-tokens/{id}JWTEliminar un token - PUT
/api/discord-tokens/{id}/toggleJWTAlternar un token entre activo e inactivo
Facturación y pagos
Comprar tokens siempre termina en una página de pago alojada, así que recargar no puede ser totalmente automático. Todo lo posterior a la recarga sí.
- GET
/invoices/getClave APIHistorial de facturación - GET
/api/subscriptions/subscriptionsJWTSuscripciones activas - GET
/products/tokensByUserClave APIPaquetes de tokens con el precio de tu cuenta - POST
/company/getClave APITu dirección de facturación - POST
/company/createClave APIFijarla: country, region, city, address, postalCode - GET
/api/stripe/checkout?product_id=JWTUna URL de Stripe Checkout para un paquete de tokens - GET
/api/stripe/subscription?plan=JWTUna URL de Stripe Checkout para un plan - GET
/api/stripe/portalJWTUna URL del portal de facturación de Stripe - GET
/api/stripe/documentsJWTFacturas y recibos de Stripe - GET
/coinpayments/checkoutClave APIUna URL de pago con criptomonedas
Carrito guardado
El carrito del panel, guardado en el servidor para que sobreviva a un cambio de dispositivo. No lo necesitas para crear pedidos: /orders/checkout acepta el carrito en la propia petición.
- GET
/api/cartJWTEl carrito guardado - PUT
/api/cartJWTReemplazarlo - POST
/api/cartJWTReemplazarlo, igual que PUT - DELETE
/api/cartJWTVaciarlo
Superficies internas
Existen para Stripe, el planificador de tareas y el desafío antiabuso del registro. Se autentican con secretos compartidos o firmas y no forman parte de la superficie de integración: se listan aquí solo para que el inventario esté completo.
- POST
/api/stripe/webhookEventos de pago de Stripe, autenticados por firma - POST
/api/jobs/tickEjecuta las tareas pendientes, autenticado por secreto compartido - POST
/api/pow/challengeDesafío de prueba de trabajo del registro - POST
/api/logs/updateSeguimiento de actividad de correo - POST
/api/order/{email}Crea un pedido en otra cuenta: solo lista blanca de administradores - GET
/reset-password/{token}La antigua página de restablecimiento renderizada en servidor, mantenida por los enlaces ya enviados - GET
/PúblicaComprobación de estado
Formatos de respuesta
Casi todo lo que vas a leer viene en dos objetos: el sitio, que devuelven los endpoints del catálogo, y la campaña, que devuelven los de pedidos. Cada uno tiene unas cuarenta columnas; las tablas de abajo son las que de verdad necesita una integración.
Tres campos llegan como cadenas JSON aunque contengan números: accept_rate y timeout en el sitio, y custom_max_per_hour en la campaña. Conviértelos antes de hacer cuentas o acabarás concatenando cadenas en lugar de sumando.
{
"accept_rate": "70", // string, not number
"timeout": "150000", // string, not number
"custom_max_per_hour": "60" // string, not number
}El objeto sitio
Lo devuelven /orders/getAllWebsites y /orders/getWebsiteDetailsByName, y viene incrustado en cada campaña como `website`.
| Parámetro | Tipo | Descripción |
|---|---|---|
id | integer | El id del sitio. Se envía como `id` en una línea de carrito, o como `service` en el endpoint SMM. |
name | string | Nombre visible, y la cadena exacta con la que compara /orders/getWebsiteDetailsByName. |
price_per_1000 | number | Tokens por cada 1.000 votos aceptados. Es el número que usa la fórmula de coste. |
accept_rate | string | Porcentaje de votos enviados que se aceptan. De él dependen todos los cálculos de progreso y de reembolso. |
max_per_hour | integer | El techo de entrega del propio sitio. Tanto custom_max_per_hour como el interval del SMM se recortan a este valor. |
active | integer | 1 significa que se puede pedir. Los sitios inactivos también se devuelven, así que fíltralos tú. |
vote_reset_time | integer | Horas que deben pasar antes de que la misma identidad pueda volver a votar. |
speed_changeable | integer | 1 significa que el sitio respeta una velocidad de entrega personalizada. |
referer_must_be_set | integer | 1 significa que hay que fijar http_referral en la campaña. |
optional_data_possible | integer | 1 significa que el sitio acepta el campo optional_data de la campaña. |
track_votes | integer | 1 significa que hay registros de entrega voto a voto para las campañas de este sitio. |
subscribeable | integer | 1 significa que se aceptan líneas de suscripción. |
subscription_price_1d | number | Tokens por día de suscripción, antes del descuento por nivel. |
subscription_speed | integer | Votos por hora que entrega una suscripción. La cantidad se deriva de aquí en el servidor, nunca de tu petición. |
Los campos que no aparecen aquí alimentan la propia interfaz del panel. Léelos si quieres, pero no forman parte del contrato de integración y pueden cambiar sin aviso.
El objeto campaña
Lo devuelven /orders/getAll y /orders/get/. Fíjate en lo que falta: no hay campo de estado.
| Parámetro | Tipo | Descripción |
|---|---|---|
id | integer | El id de la campaña. Todos los endpoints de Campañas lo reciben como `id`. |
vote_website_id | integer | El sitio en el que corre esta campaña. |
website | object | El objeto sitio completo, incrustado. Está en /orders/getAll y no en /orders/get/. |
url | string | La URL de voto: el `ownName` que enviaste al crear el pedido. |
custom_name | string | Tu propia etiqueta para la campaña, o null. |
amount_to_do | integer | Votos aceptados comprados. Cuenta votos aceptados, no intentos. |
amount_done | integer | Votos enviados hasta ahora. Unidad distinta a amount_to_do: mira la sección de progreso. |
running | integer | 1 entregando, 0 en pausa. |
done | integer | 1 significa cerrada: cancelada, reembolsada y con ambas cantidades a cero. No es un indicador de finalización. |
archive | integer | 1 significa archivada. Las campañas archivadas se siguen devolviendo en /orders/getAll. |
custom_max_per_hour | string | Tu techo de entrega para esta campaña, como cadena. |
max_votes_per_day | integer | Tope diario de votos, o null si no hay tope. |
is_subscription | integer | 1 significa suscripción. Las suscripciones no se pueden editar tras la compra. |
paused_unpaused | datetime | Cuándo se pausó, reanudó o redimensionó la campaña por última vez. Es lo que inicia la espera de 30 segundos entre ediciones. |
Los campos que no aparecen aquí alimentan la propia interfaz del panel. Léelos si quieres, pero no forman parte del contrato de integración y pueden cambiar sin aviso.
¿Está corriendo? ¿Ha terminado?
Una campaña no tiene campo de estado, así que lo deduces de cuatro columnas. Evalúa estas condiciones en orden y quédate con la primera que se cumpla.
| Comprobación | Significa |
|---|---|
1done === 1 | Cancelada. Se reembolsó el resto no gastado, ambas cantidades se pusieron a cero y se marcó como archivada. |
2running === 0 | La pausaste tú. Reanúdala con /orders/unpause. |
3remaining_accepted === 0 | Se ha entregado todo lo comprado. |
4running === 1 | Funcionando con normalidad. |
5archive === 1 | Oculta en el panel, pero se sigue devolviendo en /orders/getAll. Fíltrala si quieres que tu lista coincida con la del panel. |
El orden importa. Cancelar fija done y archive a la vez, así que comprobar archive primero presentaría una campaña cancelada como simplemente archivada, y comprobar el restante antes que running presentaría una campaña en pausa como si estuviera entregando.
Progreso y reembolsos
amount_to_do cuenta votos aceptados; amount_done cuenta envíos. Solo se acepta accept_rate por ciento de los envíos, así que están en unidades distintas y restar uno del otro directamente es un error.
Este es el error de integración más común, y falla en silencio: los números siguen pareciendo razonables y la barra de progreso simplemente está mal. Con una tasa de aceptación del 70 por ciento, una campaña terminada se lee como un 70 por ciento; con el 50 por ciento, una campaña entregada a medias se lee como si no hubiera empezado. Convierte los envíos a votos aceptados primero, siempre.
// accept_rate arrives as a STRING, and it is per-site, not global.
const rate = Number(order.website.accept_rate)
// amount_done counts SUBMISSIONS; amount_to_do counts ACCEPTED votes.
// Convert before comparing them.
const delivered = order.amount_done * rate / 100
const remaining = Math.max(order.amount_to_do - delivered, 0)
const percent = 100 * delivered / order.amount_to_do
// What cancelling right now would put back on your balance:
const refund = (remaining / 1000) * order.website.price_per_1000La misma cuenta pone precio a una cancelación: el reembolso es el resto no gastado al precio de lista del sitio, así que puedes saber cuánto vale parar una campaña antes de decidirte.
Una campaña de principio a fin
Seis llamadas, una clave API, sin navegador y sin inicio de sesión. Lo mismo con el endpoint SMM son tres llamadas — services, add, status — y no toca la API de plataforma.
KEY=YOUR_API_KEY
BASE=https://backend.toplistbot.com
# 1. What can I order, and what does it cost?
curl -s "$BASE/orders/getAllWebsites" \
| jq '.[] | {id, name, price_per_1000, max_per_hour}'
# 2. What can I afford?
curl -s -X POST "$BASE/api/v2" -d "key=$KEY" -d "action=balance"
# 3. Launch it. cost = price_per_1000 * amount / 1000
curl -s -X POST "$BASE/orders/checkout?key=$KEY" \
-H "Content-Type: application/json" \
-d '[{"id":9,"amount":1000,
"ownName":"https://arena-top100.com/index.php?a=in&u=me",
"custom_max_per_hour":60}]'
# 4. Find the campaign that was just created.
curl -s "$BASE/orders/getAll?key=$KEY" | jq '.[0] | {id, url, amount_to_do, amount_done}'
# 5. Watch it. Poll every few minutes.
curl -s "$BASE/orders/graph/184223?key=$KEY"
# 6. Slow it down, or stop it.
curl -s -X PATCH "$BASE/orders/updateLimit?key=$KEY" \
-H "Content-Type: application/json" -d '{"id":184223,"max_votes_per_day":200,"type":"set"}'
curl -s -X POST "$BASE/orders/pause?key=$KEY" \
-H "Content-Type: application/json" -d '{"id":184223}'Errores
Los errores llegan con su código HTTP correspondiente. Los fallos de validación devuelven un objeto `errors` indexado por nombre de campo.
400La petición no se pudo leer: JSON mal formado, o una acción que el endpoint SMM no conoce.401Credencial ausente, caducada o incorrecta.402Saldo insuficiente. No se cobró nada.403Autenticado, pero sin permiso para hacer esto.404No existe ese registro, incluido uno que pertenece a otra persona.409Entra en conflicto con el estado actual de la cuenta, como activar el doble factor dos veces.422La validación falló. El cuerpo nombra cada campo.429Límite de frecuencia. El mensaje indica cuánto esperar.500Culpa nuestra. No se cobró nada.503Una dependencia no está disponible. Reintenta más tarde.
{
"errors": {
"quantity": ["The quantity must be at least 1."]
}
}Cuando el cuerpo de la petición era un array, las claves de error llevan el índice de la línea que falló.
Unos pocos endpoints responden en texto plano y no en JSON: /orders/checkout, /orders/pause y /orders/updateLimit entre ellos. Decide según el código de estado, no según la forma del cuerpo.
Límites de frecuencia
Un 429 siempre indica cuánto esperar. Respétalo en lugar de reintentar a ciegas.
- El mismo carrito se acepta como mucho una vez cada 60 segundos.
- El total de votos de una campaña se puede cambiar una vez cada 30 segundos.
- Los intentos de inicio de sesión se limitan por dirección y por IP.
- Restablecer contraseña: 3 por dirección y 10 por IP cada 15 minutos.
- `status` y `cancel` aceptan como máximo 100 ids de pedido por llamada.
- Consulta el progreso cada minutos, no cada segundos. La entrega se mide en votos por hora.
Límites y notas
- La cantidad del pedido debe estar entre 1 y 50000 acciones.
- El intervalo es 15 por hora por defecto y tiene un tope de 4000. Pedir más que el máximo del propio sitio se rechaza con un 400 que indica el límite.
- Las acciones `refill` y `refill_status` no están implementadas: crea un pedido nuevo en su lugar.
- El campo `status` siempre vale literalmente "Completed" y no es una señal de progreso. Usa `remains == 0` para saber que un pedido ha terminado, y `start_count` para lo entregado.
- Las suscripciones no se pueden editar tras la compra: pausa o cancela.
- Los sitios de listados fijan sus propias reglas y las cambian con el tiempo. Eres responsable de asegurarte de que tu uso cumple los términos de cualquier sitio en el que promociones. No prometemos ninguna posición ni clasificación concreta.
