Marketplace ClickBank — captura v2 (API-first)¶
Status: implementado e verificado em produção (2026-09-10). A API interna não é observável no caminho real (ver "Resultado do primeiro probe"); o DOM com detecção dinâmica de página é o caminho confiável e cobre o catálogo acessível inteiro (~1.233 ofertas).
Contexto¶
Não existe feed XML público/anônimo do Marketplace ClickBank (verificado em
docs/integrations/clickbank.md). O caminho aprovado é a captura autenticada via
navegador dedicado (Playwright + perfil persistente). Até a v1, a captura era
100% DOM (role=gridcell), o que trazia:
- ~25 páginas com scroll virtual e pausas humanas (lento);
- apenas rank/título/vendor/EPC/CVR/gravity (sem URL, categoria, JV tools, rebill);
- categoria
"Uncategorized"em 100% dos itens → complianceLOWfalso; - fragilidade a qualquer mudança de layout da SPA.
Arquitetura v2 (API-first com fallback DOM)¶
- Probe (
scripts/marketplace-scraper.js): hooks defetch/XHRjá capturavam as respostas da API interna. Agora também gravam metadados sanitizados emmeta.apiProbe:{ path, params (apenas nomes), itemCount, responseKeys, itemKeys, totalHint }. Nenhum header, token ou payload é exportado. - Captura:
discoverApi()navega uma página para descobrir o endpoint de listagem e os parâmetros de paginação;captureViaApi()reexecuta a chamada (mesmos headers de sessão, page size da própria API) — 1 request por página, sem depender do DOM. Falha em qualquer etapa → fallback automático para o caminho DOM (captureViaDom), que permanece intacto. - Cobertura:
meta.fieldCoverageconta itens com dado real por campo (category,url,affiliate_tools_url,hoplink_base,vendor_domain,rebill_rate,funnel_type, …). O dialog do Radar exibe essa cobertura antes da importação. - Compliance: categoria ausente/
Uncategorized→UNKNOWN(nuncaLOW), tanto no servidor quanto no parser do feed XML. - Automação: LaunchAgent local (
com.brandvelia.marketplace-capture) rodascripts/marketplace-daily.mjsdiariamente às 03:30; capturascompletesão auto-importadas via/api/cron/marketplace-capture-import(BearerCRON_SECRET, hash canônico recomputado, service-role, workspace único). Capturaspartialcontinuam exigindo revisão manual no Radar.
Como executar o spike empírico (uma vez)¶
- Instalar o agente: duplo clique em
scripts/install-capture-agent.command. - Fazer login na janela dedicada do ClickBank (primeira vez).
- Rodar:
pnpm capture:marketplacee, no Radar, Capturar do Marketplace. - Ler o
apiProbenocapture_metado snapshot importado (ou nos arquivosscripts/captures/marketplace-*.json→meta.apiProbe). - Preencher a tabela abaixo com o endpoint real e os campos disponíveis.
Resultado do primeiro probe (2026-09-10)¶
| Item | Valor observado |
|---|---|
| Endpoint de listagem | não observável — a SPA não emite request de ofertas após a injeção (dados carregados antes / paginação client-side); meta.apiProbe fica null e o caminho cai no DOM |
| Parâmetros de paginação | #/results?sortField=rank&sortDescending=false&resultsPerPage=50&offset=N (via hash; usado como fallback) |
| Page size | 50 (o valor antigo de 30 observado em outro perfil vinha da preferência da grade) |
| Total do catálogo (UI) | ~1.233 ofertas em 25 páginas; a paginação do MUI anuncia mais páginas (83) mas as páginas 26+ repetem os mesmos itens — o controle de completude mede a passada global e ignora o excedente da UI |
| Campos ricos (categoria/URL/JV/rebill) | indisponíveis na grade (0/1237); categoria/compliance vêm de detectComplianceRisk por termo do nome do produto |
| Mode efetivo da captura | dom com fallback de navegação por hash |
| Resultado real | 1.237 ofertas, status=complete, auto-import via /api/cron/marketplace-capture-import → 1.237 upserts em bv_marketplace_snapshots, 100% vinculadas ao run |
| Compliance apurado | UNKNOWN 834 · MEDIUM 151 · HIGH 15 (nunca LOW sem categoria real) |
Falhas reais corrigidas neste ciclo¶
- Tela de login aceita como lista: heurística antiga injetava o scraper na página de login (0 células, run travado). Agora exige gridcells/linhas reais.
- Página presumida em 50: o perfil podia renderizar 30/página e a captura truncava em 750. O tamanho vem do texto de paginação/linhas renderizadas.
- "Próxima" some na página 25: fallback de navegação por hash avança até o fim do catálogo.
- Run travado sem progresso: watchdog de 6 min no driver +
state.failedexplícito no scraper (erro/lista vazia) em vez de esperar 45 min. - View do PostgREST desatualizada:
bv_marketplace_snapshotsnão expunhamarketplace_sync_run_id; migration recria as views e restaura grants. - Log duplicado do agente: removida a escrita dupla (stdout + arquivo).
- Auto-import frágil: 3 tentativas com backoff no agente diário.
Contrato de aceite da captura v2¶
- Captura completa em ≤ 1/3 do tempo da v1 (API-first) com fallback DOM estável.
- Top 300 com ≥ 90% de categoria real (quando a API expõe categoria).
- Nenhum item importado com
compliance_status = LOWsem categoria real. - Reexecução no mesmo dia não duplica (
workspace_id, affiliate_network, identity_key, snapshot_date). - Retenção: snapshots com mais de 90 dias são removidos pelo housekeeping.