Rivalwin API — TikTok Ads Commercial
Descripción general
La API de TikTok Ads Commercial permite generar informes de publicidad comercial de forma programática. Utilizando la información del TikTok, los informes analizan los anuncios de un competidor durante un período determinado, proporcionando datos de creatividades (videos e imágenes), alcance estimado, períodos de actividad y análisis de IA.
Base URL: https://rivalwin.com/api/v1/tiktok/companies/
Autenticación
Todas las peticiones requieren una API Key válida enviada en el header X-API-Key.
X-API-Key: tu_api_key_aqui
Rate Limiting
La API aplica dos tipos de limitación:
Rate limit por hora
Cada API Key tiene un límite de peticiones por hora configurado. Los headers de respuesta indican el estado:
X-RateLimit-Limit-Hour: 600
X-RateLimit-Remaining-Hour: 598
Si se excede el límite: 429 Too Many Requests con header Retry-After.
Cooldown entre informes
Existe un tiempo mínimo de espera entre la creación de informes consecutivos (por defecto 10 segundos, configurable por plan/empresa). Si intentas crear un informe antes de que transcurra este tiempo:
{
"error": "cooldown_active",
"message": "Please wait 7 seconds before creating another report.",
"cooldown_seconds": 10,
"retry_after": 7
}
Requisitos de la cuenta
- Su cuenta de Rivalwin debe estar activa.
- El plan contratado debe estar activo y no vencido (contrato o trial).
- El plan debe incluir acceso a la API.
Si alguna de estas condiciones no se cumple, recibirás un error 403 Forbidden con el motivo específico.
Recepción de resultados
Existen dos formas de recibir los datos de un informe completado:
Polling
Consulta el endpoint report_status periódicamente (cada 15-30 segundos) hasta que el estado sea DONE. Es el método más simple y no requiere configuración adicional.
Webhook
Configura una URL de webhook en el módulo Integraciones & API Keys del sistema. Cuando un informe pase al estado DONE, la API enviará automáticamente un POST a la URL configurada con el payload completo del informe (mismo contenido que devuelve report_status en estado DONE).
De esta forma no necesitas hacer polling: simplemente esperas a recibir la notificación en tu servidor.
Endpoints
1. Crear un nuevo informe de TikTok Ads
Crea un informe de publicidad comercial de TikTok Ads de forma asíncrona. Devuelve inmediatamente un hash identificador.
POST /api/v1/tiktok/companies/new_report
Headers
| Header | Tipo | Requerido | Descripción |
|---|---|---|---|
| X-API-Key | string | Sí | Tu API Key |
| Idempotency-Key | string | No | Clave de idempotencia para evitar informes duplicados |
| Content-Type | string | Sí | application/json |
Body (JSON)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
rival_ref | string | Sí | Referencia del rival |
date_from | string | Sí | Fecha inicio del período (YYYY-MM-DD) |
date_to | string | Sí | Fecha fin del período (YYYY-MM-DD) |
country | string | Sí | Código de país ISO de 2 letras (ej: AR, US) |
Ejemplo de petición
curl -X POST https://rivalwin.com/api/v1/tiktok/companies/new_report \
-H "X-API-Key: tu_api_key" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: tiktok-report-20260315" \
-d '{
"rival_ref": "rvl_0c526d93",
"date_from": "2026-03-01",
"date_to": "2026-03-31",
"country": "ES"
}'
Respuesta exitosa (202 Accepted)
{
"hash": "...",
"status": "PENDING",
"created_at": "2026-03-15 16:00:00"
}
Validaciones y restricciones
| Regla | Detalle |
|---|---|
| Rango máximo de fechas | 60 días |
| Fecha fin máxima | Ayer (no se permite el día actual ni futuro) |
| Fecha inicio mínima | Hasta 1 año atrás |
| Formato de fecha | YYYY-MM-DD estricto |
| País | Código ISO de 2 letras |
| Créditos | Se descuentan al crear el informe |
Idempotencia
Si envías el header Idempotency-Key con un valor previamente usado, la API devuelve el informe existente sin crear uno nuevo ni descontar créditos adicionales.
2. Consultar estado del informe
Consulta el estado de un informe y obtiene los datos del reporte cuando está completo.
POST /api/v1/tiktok/companies/report_status
Headers requeridos
| Header | Tipo | Requerido | Descripción |
|---|---|---|---|
| X-API-Key | string | Sí | Tu API Key |
| Content-Type | string | Sí | application/json |
Body (JSON)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
hash | string | Sí | Hash del informe (obtenido de new_report) |
Ejemplo de petición
curl -X POST https://rivalwin.com/api/v1/tiktok/companies/report_status \
-H "X-API-Key: tu_api_key" \
-H "Content-Type: application/json" \
-d '{"hash": "..."}'
Respuesta según estado
PENDING — El informe está en cola:
{
"hash": "...",
"status": "PENDING",
"created_at": "2026-03-15 16:00:00",
"message": "The report is queued and will start processing shortly."
}
PROCESSING — El informe se está generando:
{
"hash": "...",
"status": "PROCESSING",
"created_at": "2026-03-15 16:00:00",
"started_at": "2026-03-15 16:00:05",
"message": "The report is being generated. Please check back in a few moments."
}
DONE — El informe está completo con todos los datos:
{
"hash": "...",
"status": "DONE",
"created_at": "2026-03-15 16:00:00",
"started_at": "2026-03-15 16:00:05",
"completed_at": "2026-03-15 16:02:15",
"report": {
"report_id": 6198,
"platform": "TikTok",
"rival": {
"ref": "rvl_0c526d93",
"name": "Empresa Competidora S.L.",
"logo": "https://rivalwin.com/uploads/logos/empresa.png",
"web": "https://empresa.com",
"description": "Competidor en el sector retail",
"tiktok_id": "7123456789012345678"
},
"sector": "Retail",
"country": "ES",
"campaign_start": "2026-03-01",
"campaign_end": "2026-03-31",
"inform_created": "2026-03-15 16:00:00",
"kpis": {
"ads_total": 23,
"with_video": 18,
"with_image_only": 5
},
"insights": {
"gpt_analysis": "Análisis detallado de la estrategia en TikTok Ads."
},
"ads": [
{
"ad_id": "12345678901234",
"first_shown": "2026-03-01",
"last_shown": "2026-03-28",
"status": "active",
"advertiser": {
"business_id": "7123456789012345678",
"business_name": "Empresa Competidora S.L.",
"paid_for_by": "Empresa Competidora S.L."
},
"video_url": "https://v16-webapp-prime.tiktok.com/video/...",
"thumbnail_url": "https://p16-sign.tiktokcdn-us.com/...",
"image_urls": [],
"reach": "10K-50K",
"estimated_audience": {
"label": "10K-50K",
"min": 10000,
"max": 50000
}
}
]
}
}
ERROR — Error durante la generación:
{
"hash": "...",
"status": "ERROR",
"created_at": "2026-03-15 16:00:00",
"completed_at": "2026-03-15 16:01:00",
"error_message": "Report failed during processing."
}
Estructura completa del reporte
Campos principales (report)
| Campo | Tipo | Descripción |
|---|---|---|
report_id | int | ID interno del informe |
platform | string | Plataforma analizada (TikTok) |
rival | object | Datos del competidor analizado |
sector | string | Sector o industria del competidor |
country | string | País filtrado |
campaign_start | string | Fecha inicio del período analizado |
campaign_end | string | Fecha fin del período analizado |
inform_created | string | Fecha de creación del informe |
kpis | object | Indicadores clave del período |
insights | object | Análisis generado por IA |
ads | array | Lista de anuncios encontrados |
Datos del rival (rival)
| Campo | Tipo | Descripción |
|---|---|---|
ref | string | Referencia del rival |
name | string | Nombre de la empresa |
logo | string | URL del logo (puede ser null) |
web | string | Sitio web (puede ser null) |
description | string | Descripción del competidor |
KPIs (kpis)
| Campo | Tipo | Descripción |
|---|---|---|
ads_total | integer | Total de anuncios encontrados en el período analizado |
with_video | integer | Anuncios que contienen creatividades de video |
with_image_only | integer | Anuncios que solo contienen imágenes (sin video) |
Anuncios (ads[])
Cada anuncio contiene la siguiente información:
| Campo | Tipo | Descripción |
|---|---|---|
ad_id | string | ID único del anuncio en TikTok Ad Library |
first_shown | string | Fecha en que se mostró por primera vez (YYYY-MM-DD) |
last_shown | string | Última fecha en que se vio activo (YYYY-MM-DD) |
status | string | Estado del anuncio (active, inactive, etc.) |
advertiser | object | Datos del anunciante |
video_url | string | URL del video del anuncio (puede ser null) |
thumbnail_url | string | URL de la imagen de portada / thumbnail |
image_urls | array | URLs de imágenes del anuncio |
reach | string | Alcance estimado en formato legible (ej: "10K-50K") |
estimated_audience | object | Desglose numérico del alcance estimado |
Datos del anunciante (advertiser)
| Campo | Tipo | Descripción |
|---|---|---|
business_name | string | Nombre comercial del anunciante |
paid_for_by | string | Entidad que paga por el anuncio |
Audiencia estimada (estimated_audience)
| Campo | Tipo | Descripción |
|---|---|---|
label | string | Rango en formato legible (ej: "10K-50K") |
min | integer | Límite inferior estimado (puede ser null) |
max | integer | Límite superior estimado (puede ser null) |
Ejemplos de código
PHP
<?php
$apiKey = "tu_api_key";
$baseUrl = "https://rivalwin.com/api/v1/tiktok/companies";
// 1. Crear informe
$report = apiCall("$baseUrl/new_report", [
'rival_ref' => 'rvl_0c526d93',
'date_from' => '2026-03-01',
'date_to' => '2026-03-31',
'country' => 'ES',
], $apiKey);
$hash = $report['hash'];
echo "Informe creado: $hash\n";
// 2. Polling hasta completar
do {
sleep(20);
$status = apiCall("$baseUrl/report_status", ['hash' => $hash], $apiKey);
echo "Estado: " . $status['status'] . "\n";
} while (in_array($status['status'], ['PENDING', 'PROCESSING']));
// 3. Procesar resultado
if ($status['status'] === 'DONE') {
$data = $status['report'];
echo "\n=== INFORME TIKTOK ADS ===\n";
echo "Rival: " . $data['rival']['name'] . "\n";
echo "TikTok ID: " . $data['rival']['tiktok_id'] . "\n";
echo "Período: " . $data['campaign_start'] . " → " . $data['campaign_end'] . "\n";
echo "\nKPIs:\n";
echo " Total anuncios: " . $data['kpis']['ads_total'] . "\n";
echo " Con video: " . $data['kpis']['with_video'] . "\n";
echo " Solo imagen: " . $data['kpis']['with_image_only'] . "\n";
echo "\nAnuncios:\n";
foreach ($data['ads'] as $i => $ad) {
echo " " . ($i + 1) . ". Ad ID: {$ad['ad_id']}\n";
echo " Anunciante: {$ad['advertiser']['business_name']}\n";
echo " Período: {$ad['first_shown']} → {$ad['last_shown']}\n";
echo " Alcance: {$ad['reach']}\n";
if (!empty($ad['video_url'])) {
echo " Video: {$ad['video_url']}\n";
}
if (!empty($ad['thumbnail_url'])) {
echo " Thumbnail: {$ad['thumbnail_url']}\n";
}
echo "\n";
}
}
function apiCall(string $url, array $body, string $apiKey): array {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($body),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-API-Key: ' . $apiKey,
],
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
Python
import requests
import time
api_key = "tu_api_key"
base_url = "https://rivalwin.com/api/v1/tiktok/companies"
headers = {
"Content-Type": "application/json",
"X-API-Key": api_key,
}
# 1. Crear informe
report = requests.post(f"{base_url}/new_report", json={
"rival_ref": "rvl_0c526d93",
"date_from": "2026-03-01",
"date_to": "2026-03-31",
"country": "ES",
}, headers=headers).json()
hash_id = report["hash"]
print(f"Informe creado: {hash_id}")
# 2. Polling
while True:
time.sleep(20)
result = requests.post(f"{base_url}/report_status",
json={"hash": hash_id}, headers=headers).json()
print(f"Estado: {result['status']}")
if result["status"] not in ["PENDING", "PROCESSING"]:
break
# 3. Resultados
if result["status"] == "DONE":
data = result["report"]
print(f"\nRival: {data['rival']['name']}")
print(f"TikTok ID: {data['rival']['tiktok_id']}")
print(f"Total anuncios: {data['kpis']['ads_total']}")
print(f"Con video: {data['kpis']['with_video']}")
for ad in data["ads"]:
print(f"\n Ad: {ad['ad_id']}")
print(f" Alcance: {ad['reach']}")
print(f" Visible: {ad['first_shown']} → {ad['last_shown']}")
if ad.get("video_url"):
print(f" Video: {ad['video_url']}")
JavaScript (Node.js)
const API_KEY = "tu_api_key";
const BASE_URL = "https://rivalwin.com/api/v1/tiktok/companies";
async function apiCall(endpoint, body = {}) {
const res = await fetch(`${BASE_URL}/${endpoint}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": API_KEY,
},
body: JSON.stringify(body),
});
return res.json();
}
async function main() {
// 1. Crear informe
const report = await apiCall("new_report", {
rival_ref: "rvl_0c526d93",
date_from: "2026-03-01",
date_to: "2026-03-31",
country: "ES",
});
console.log(`Informe creado: ${report.hash}`);
// 2. Polling
let result;
do {
await new Promise(r => setTimeout(r, 20000));
result = await apiCall("report_status", { hash: report.hash });
console.log(`Estado: ${result.status}`);
} while (["PENDING", "PROCESSING"].includes(result.status));
// 3. Resultados
if (result.status === "DONE") {
const data = result.report;
console.log(`\nRival: ${data.rival.name}`);
console.log(`TikTok ID: ${data.rival.tiktok_id}`);
console.log(`Anuncios: ${data.kpis.ads_total}`);
data.ads.forEach(ad => {
console.log(` ${ad.ad_id} | ${ad.reach} | ${ad.first_shown} → ${ad.last_shown}`);
});
}
}
main().catch(console.error);
Códigos de error
| Código HTTP | Error | Descripción |
|---|---|---|
| 400 | missing_parameters | Faltan parámetros requeridos en el body |
| 400 | invalid_date_from | Formato de date_from inválido (debe ser YYYY-MM-DD) |
| 400 | invalid_date_to | Formato de date_to inválido (debe ser YYYY-MM-DD) |
| 400 | invalid_date_range | Rango de fechas inválido (futuro, invertido o > 60 días) |
| 400 | invalid_country | Código de país inválido (debe ser 2 letras ISO o ALL_COUNTRIES) |
| 400 | rival_no_tiktok_id | El rival no tiene un TikTok Business ID configurado |
| 401 | missing_api_key | No se envió el header X-API-Key |
| 401 | invalid_api_key | La API Key no es válida o está revocada |
| 402 | insufficient_credits | Créditos insuficientes para generar el informe |
| 403 | account_inactive | La cuenta de la empresa está desactivada |
| 403 | plan_inactive | El plan contratado está desactivado |
| 403 | contract_expired | El contrato ha expirado |
| 403 | trial_expired | El período de prueba ha expirado |
| 403 | api_not_in_plan | El plan no incluye acceso a la API |
| 404 | rival_not_found | El rival_ref no existe o está inactivo |
| 404 | rival_data_not_found | Datos del rival no encontrados en el sistema |
| 404 | job_not_found | No se encontró un informe con el hash proporcionado |
| 429 | rate_limit_exceeded | Se excedió el límite de peticiones por hora |
| 429 | cooldown_active | Debe esperar antes de crear otro informe |
| 500 | internal_error | Error interno del servidor |
Notas importantes
- Los informes se generan de forma asíncrona. La creación devuelve un
hashinmediatamente y el informe se procesa en segundo plano. - El tiempo de generación típico es de 1 a 5 minutos, dependiendo del volumen de anuncios y la generación del análisis IA.
- La mayoría de los anuncios de TikTok son videos. El campo
video_urlcontiene la URL directa al video cuando está disponible. - El campo
ads_data_urlcontiene una URL firmada (válida por 1 hora) que permite descargar el JSON crudo completo de anuncios. - Cada informe consume créditos según el sistema.