---
name: scrape_tiktok_creative
kind: function
lang: py
domain: datascience
version: "1.0.0"
purity: impure
signature: "def scrape_tiktok_creative(country: str = 'ES', kind: str = 'hashtag', limit: int = 50, period: int = 7) -> list[dict]"
description: "Capta tendencias del TikTok Creative Center (hashtags, canciones, creadores y videos virales con metricas reales) via su API JSON interna creative_radar_api. Headers realistas con requests, paginacion, parseo tolerante a cambios de schema. Devuelve filas 1:1 con la tabla Postgres tiktok_trends. Impure: hace HTTP a un endpoint interno no publico que puede romperse o exigir anti-bot."
tags: [tiktok, social, trends, market-intel, datascience]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [requests]
params:
  - name: country
    desc: "Codigo ISO de pais del ranking (ej. 'ES', 'US', 'MX'). El Creative Center segmenta las tendencias por mercado. Default 'ES'."
  - name: kind
    desc: "Tipo de tendencia: 'hashtag' (default, el mas estable), 'song', 'creator' o 'video'. Cada uno usa un endpoint interno distinto. Empieza por hashtag si no estas seguro."
  - name: limit
    desc: "Numero maximo de filas a devolver. El endpoint pagina de 50 en 50; la funcion concatena paginas hasta alcanzar limit o agotar resultados. Default 50."
  - name: period
    desc: "Ventana temporal en dias. Solo acepta 7 (default), 30 o 120 — el endpoint rechaza otros valores con error de validacion."
output: "Lista de dicts con EXACTAMENTE las claves: country (str), kind (str), name (str|None), rank (int|None), views (int|None, BIGINT), growth_pct (float|None), industry (str|None), url (str|None). Mapea 1:1 con la tabla Postgres tiktok_trends (sin id/snapshot_date/scraped_at). Devuelve [] si el endpoint responde OK pero sin items para el segmento. Lanza ValueError (kind/period invalidos) o RuntimeError (403 anti-bot, HTTP de error, JSON invalido, code de error logico)."
tested: false
tests: []
test_file_path: ""
file_path: "python/functions/datascience/scrape_tiktok_creative.py"
notes: |
  ESTRATEGIA: el Creative Center (ads.tiktok.com/business/creativecenter) es una
  SPA JS-rendered, pero alimenta sus rankings desde una API interna de facto bajo
  https://ads.tiktok.com/creative_radar_api/v1/popular_trend/... Esta funcion habla
  directamente con ese endpoint con requests (mucho mas barato que un navegador
  headless CUANDO responde). El parseo tolera variaciones del schema (data.list,
  data.hashtags, data.items...) y nombres de campo distintos por kind.

  REALISMO: en pruebas reales desde un entorno headless/datacenter el endpoint
  respondio con code=40101 ("no permission") — rechazo anti-bot por falta de los
  tokens de sesion firmados (anonymous-user-id, user-sign, timestamp) que la SPA
  genera en cliente y que no se pueden falsear fuera del navegador. La funcion NO
  inventa datos: en ese caso lanza RuntimeError con un mensaje claro. Se considera
  el comportamiento esperado, no un bug de la funcion.
---

## Ejemplo

```python
from datascience.scrape_tiktok_creative import scrape_tiktok_creative

# Top 50 hashtags virales en Espana, ultimos 7 dias.
rows = scrape_tiktok_creative(country="ES", kind="hashtag", limit=50, period=7)
# rows[0] -> {
#   "country": "ES", "kind": "hashtag", "name": "fyp", "rank": 1,
#   "views": 12450000, "growth_pct": 42.0, "industry": "Entertainment",
#   "url": "https://ads.tiktok.com/business/creativecenter/hashtag/fyp/pc/en"
# }

# Canciones en tendencia en US, ventana de 30 dias.
songs = scrape_tiktok_creative(country="US", kind="song", limit=20, period=30)

# Las filas casan 1:1 con un INSERT en la tabla Postgres tiktok_trends
# (sin id/snapshot_date/scraped_at, que los pone la BD).
```

## Cuando usarla

Usala cuando necesites market intelligence de TikTok: detectar hashtags, canciones,
creadores o productos virales por pais con metricas reales (views, ranking,
crecimiento) para alimentar la tabla `tiktok_trends`, un dashboard de tendencias o
un analisis de oportunidad de contenido. Empieza por `kind="hashtag"` (el endpoint
mas estable) antes de probar song/creator/video. Si el fetch HTTP devuelve
RuntimeError por anti-bot, baja al browser MCP/CDP del ecosistema.

## Gotchas

- **El endpoint interno NO es una API publica versionada.** `creative_radar_api/v1/popular_trend`
  es un contrato de facto que TikTok cambia sin aviso: ruta, parametros, schema del
  JSON y claves de campo pueden romperse en cualquier deploy. El parseo es tolerante
  pero no inmune; si TikTok mueve la lista a otra ruta, la funcion devuelve [] o
  lanza RuntimeError.
- **Anti-bot real y frecuente.** Desde IPs de datacenter o entornos headless el
  endpoint suele responder `403` o `code=40101 (no permission)`. Los rankings se
  sirven solo a clientes con los tokens de sesion firmados que la SPA genera en
  navegador (`anonymous-user-id`, `user-sign`, `timestamp`). Esos tokens NO se
  pueden falsear con requests. **Verificado en self-test: respondio code=40101.**
- **Alternativa robusta cuando el HTTP esta bloqueado:** usar el browser MCP/CDP del
  ecosistema (regla `flow_replay.md`) navegando el Creative Center con una sesion de
  chrome real, dejando que el cliente genere los tokens, y leyendo el JSON de la
  respuesta XHR o el DOM renderizado. Es mas caro pero pasa el anti-bot.
- **No inventa datos.** Si no puede extraer de verdad, lanza una excepcion clara con
  el codigo HTTP / code logico para diagnostico, en vez de devolver filas falsas.
- **growth_pct heuristico:** el Creative Center expresa el crecimiento como ratio
  (0.42) o como porcentaje (42) segun campo/version; la funcion normaliza ratios en
  [-1, 1] a porcentaje (*100). Si TikTok cambia la convencion, revisar `_row_from_item`.
- **Rate limiting:** la paginacion hace una request por pagina de 50. Para `limit`
  altos puedes encadenar varias requests rapidas — anade backoff propio si scrapeas
  muchos paises seguidos para no acelerar el bloqueo.
