{
  "_author": "Simon-Pierre Boucher <contact@spboucher.ai>",
  "_file": "data/sources.json",
  "_desc": "Registre des sources connectées — famille, mode d'accès, cadence (CLAUDE.md §9, §14, §19)",
  "updated": "2026-08-18",
  "sources": [
    {
      "id": "listes-medias",
      "famille": "decouverte",
      "palier": 3,
      "acces": "dataset local (listes & palmarès médias publics compilés : billie, frank, metricool, Favikon QC, canadult…)",
      "signal_identite": "listes_medias (0.85)",
      "cadence": "mensuelle (recompilation du dataset)",
      "notes": "376 créateurs québécois, handles par plateforme + audiences approximatives"
    },
    {
      "id": "wikidata-qc",
      "famille": "decouverte",
      "palier": 3,
      "acces": "API SPARQL publique Wikidata (nés/résidant au Québec, occupation créateur, handles sociaux curés)",
      "signal_identite": "base_publique (0.85)",
      "cadence": "hebdomadaire",
      "notes": "humoristes, youtubeurs, vidéastes web, streamers, podcasteurs notables ; is_minor levé via l'année de naissance publique"
    },
    {
      "id": "onlyqueb",
      "famille": "decouverte",
      "palier": 3,
      "acces": "requêtes directes (sitemap.xml + JSON-LD ProfilePage des pages profil publiques SSR)",
      "signal_identite": "cross_link (0.90) — liens sameAs auto-déclarés par la personne",
      "cadence": "hebdomadaire",
      "notes": "annuaire public onlyqueb.com — créateurs québécois sur abonnement (18+) ; nom public, photo, liens OnlyFans/Instagram/TikTok/X"
    },
    {
      "id": "youtube-recherche",
      "famille": "decouverte",
      "palier": 3,
      "acces": "requêtes directes (recherche publique YouTube filtre « chaînes », ytInitialData) — ~120 requêtes thématiques × marqueurs QC",
      "signal_identite": "profil_source (0.98) — la chaîne est le compte d'origine de la fiche",
      "cadence": "quotidienne (watch) — la couverture s'accumule à chaque passage",
      "notes": "filtre conservateur : marqueur québécois requis dans titre/description de la chaîne"
    },
    {
      "id": "wikipedia-qc",
      "famille": "decouverte",
      "palier": 3,
      "acces": "API MediaWiki publique (fr+en, catégories de créateurs QC : Vidéaste web canadien, Humoriste/Blogueur québécois, Canadian YouTubers/podcasters/Twitch streamers/TikTokers…) + API Wikidata wbgetentities (handles sociaux curés de l'infobox) — sans clé",
      "signal_identite": "base_publique (0.85)",
      "cadence": "hebdomadaire",
      "notes": "vague 3 — complète wikidata-qc (SPARQL) : catégories éditoriales → intro d'article (marqueur QC OBLIGATOIRE pour les catégories pan-canadiennes) → handles P2003/P7085/P2397/P2002/P5797/P2013 + site officiel P856 ; personnes décédées exclues, is_minor via P569, fiche seulement si ≥1 lien curé"
    },
    {
      "id": "palmares-apple",
      "famille": "decouverte",
      "palier": 2,
      "acces": "API marketing OFFICIELLE d'Apple (rss.marketingtools.apple.com, top 100 podcasts Canada, sans clé) + lookup iTunes groupé + tête de flux RSS pour l'attribution QC",
      "signal_identite": "profil_source (0.98)",
      "cadence": "quotidienne (watch) — le palmarès bouge chaque jour",
      "notes": "vague 3 — balados QC qui percent dans le top 100 CA (metrics.chart_rank_ca). Palmarès évalués 2026-08-18 : Spotify podcastcharts → API morte (HTTP 500) ; ADISQ → SPA JavaScript + artistes sans liens sociaux ; Bible urbaine → articles en prose sans handles (liaison ambiguë interdite §12) → Apple retenu. Fusion naturelle avec balados-itunes (même clé podcast:collectionId)"
    },
    {
      "id": "balados-itunes",
      "famille": "decouverte",
      "palier": 2,
      "acces": "API de recherche iTunes/Apple Podcasts (officielle, gratuite, sans clé)",
      "signal_identite": "profil_source (0.98)",
      "cadence": "quotidienne (watch)",
      "notes": "podcasteurs québécois : nom, artiste, pochette, genre, nb d'épisodes, flux RSS ; marqueur QC requis"
    },
    {
      "id": "instagram-profil",
      "famille": "enrichissement",
      "palier": 4,
      "acces": "Scrapfly (profil public web_profile_info — pas d'API publique de lecture)",
      "signal_identite": "cross_link (0.90) pour les liens de bio découverts",
      "cadence": "hebdomadaire (gros créateurs) / mensuelle",
      "notes": "abonnés, badge, bio, liens de bio → link_in_bio_url + comptes croisés"
    },
    {
      "id": "tiktok-profil",
      "famille": "enrichissement",
      "palier": 4,
      "acces": "Scrapfly (JSON d'hydratation de la page publique @handle)",
      "signal_identite": "cross_link (0.90) pour le lien de bio",
      "cadence": "hebdomadaire (gros créateurs) / mensuelle",
      "notes": "abonnés, badge, bio, lien de bio"
    },
    {
      "id": "youtube",
      "famille": "enrichissement",
      "palier": 2,
      "acces": "YouTube Data API v3 (officielle) si YOUTUBE_API_KEY, sinon voie publique complète SANS clé (vague 3) : page de chaîne (ytInitialData) + InnerTube browse du panneau « À propos » (clé publique embarquée) + page /videos",
      "signal_identite": "api_officielle (0.95) quand confirmé par l'API ; la voie publique ne touche pas au score",
      "cadence": "quotidienne (watch) — cap 300 chaînes/passage, rotation 14 j (≈4 200 comptes → convergence ~2 semaines)",
      "notes": "abonnés, nb de vidéos, vues totales (si affichées), date de la dernière vidéo (dormant >12 mois), description → bio, bannière + avatar, pays affiché"
    },
    {
      "id": "twitch",
      "famille": "enrichissement",
      "palier": 2,
      "acces": "API GQL publique du site web officiel (gql.twitch.tv, Client-ID public du client web — SANS clé, requêtes groupées 20 profils/POST) ; voie Helix officielle reprise si TWITCH_CLIENT_ID/SECRET fournis",
      "signal_identite": "api_officielle (0.95) via Helix ; la voie GQL publique ne touche pas au score",
      "cadence": "hebdomadaire",
      "notes": "vague 3 — abonnés (followers.totalCount, que Helix ne donne plus sans jeton de modérateur depuis 2023), description → bio, avatar, partenaire/affilié, dernière diffusion + jeu (fraîcheur). Empreinte TLS : requests accepté (vérifié 2026-08-18), repli curl-binaire prévu"
    },
    {
      "id": "link-in-bio",
      "famille": "enrichissement",
      "palier": 1,
      "acces": "requêtes directes (pages publiques SSR : linktr.ee, beacons.ai, bio.link, msha.ke, campsite.bio, snipfeed.co), repli Firecrawl",
      "signal_identite": "link_in_bio (0.95) — le signal le plus fort (§12.1)",
      "cadence": "mensuelle",
      "notes": "une page → TOUS les comptes du créateur ; consomme les link_in_bio_url découvertes par instagram-profil/tiktok-profil"
    },
    {
      "id": "balados-rss",
      "famille": "enrichissement",
      "palier": 1,
      "acces": "requêtes directes (flux RSS auto-déclarés des balados — AUCUNE clé) ; cap 150 flux/passage, re-visite 7 j, timeout court, fetch parallèle poli",
      "signal_identite": "cross_link (0.90) pour le lien officiel <link> du flux",
      "cadence": "quotidienne (watch) — rotation complète des 433 balados en ~3 jours, puis hebdomadaire",
      "notes": "nb d'épisodes réels, date du dernier épisode (détection dormant >12 mois), pochette → avatar, description → bio, catégories iTunes → niches, <link> → compte site-web/plateforme. Substitut sans clé de Podcast Index."
    },
    {
      "id": "podcastindex",
      "famille": "enrichissement",
      "palier": 2,
      "statut": "prêt — clés requises",
      "acces": "API Podcast Index (officielle, GRATUITE — inscription requise : https://api.podcastindex.org ; PODCASTINDEX_API_KEY/SECRET dans .env) — sauté proprement sans clés",
      "signal_identite": "api_officielle (0.95)",
      "cadence": "quotidienne (watch), re-visite 7 j par balado",
      "notes": "episodeCount, lastUpdateTime, itunesId, image, catégories des balados par feed_url. En attendant les clés, balados-rss couvre les mêmes champs sans clé."
    },
    {
      "id": "x-profil",
      "famille": "enrichissement",
      "palier": 3,
      "acces": "requêtes directes (service public de syndication des widgets officiels : syndication.twitter.com/srv/timeline-profile — objet user complet dans __NEXT_DATA__ ; repli très limité page x.com) ; cap 150/passage, re-visite 7 j, délai 2 s",
      "signal_identite": "aucun nouveau rattachement — métriques du compte déjà rattaché seulement",
      "cadence": "quotidienne (watch), rotation hebdomadaire",
      "notes": "abonnés + abonnements + badge + avatar X. Ancien widget followbutton MORT (vérifié 2026-08-18 : réponse vide) ; page x.com : mur de connexion après ~15-20 req/IP (constaté 2026-08-18) → repli seulement. Facebook : page publique bloquée sans clé (HTTP 400, vérifié 2026-08-18) et Scrapfly non justifié → aucune voie fiable, on passe."
    },
    {
      "id": "bio-liens",
      "famille": "enrichissement",
      "palier": 1,
      "acces": "AUCUNE requête — liaison croisée à partir des URLs COMPLÈTES déjà stockées dans les bios publiques (jamais de « @handle » textuel ambigu)",
      "signal_identite": "cross_link (0.90) — le créateur pointe lui-même vers son autre compte",
      "cadence": "quotidienne (watch), en dernier du pipeline",
      "notes": "passe conservatrice §12 : patrons sûrs seulement, cap 5 nouveaux comptes par fiche"
    },
    {
      "id": "twitch-decouverte",
      "famille": "decouverte",
      "palier": 3,
      "acces": "API GQL publique du site officiel de Twitch (Client-ID public web, sans clé) : recherche searchFor par ~43 termes QC paginée + répertoire des diffusions EN DIRECT en français (streams languages:[FR], 20 pages)",
      "signal_identite": "profil_source (0.98) — la chaîne est le compte d'origine de la fiche",
      "cadence": "quotidienne (watch) — qui est en direct change chaque jour, la couverture s'accumule",
      "notes": "vague 4 — marqueur québécois OBLIGATOIRE dans nom/bio/titre du stream ; abonnés, avatar, partenaire/affilié, dernier direct et jeu captés à la source ; niche déduite du jeu (Just Chatting → lifestyle, Music → musique…)"
    },
    {
      "id": "kick-decouverte",
      "famille": "decouverte",
      "palier": 4,
      "acces": "Scrapfly ASP sans render_js (Cloudflare bloque le direct) : kick.com/api/search par ~27 termes QC + fiche v2 par chaîne retenue (cap 120/passage — budget Scrapfly)",
      "signal_identite": "profil_source (0.98) pour la chaîne Kick ; cross_link (0.90) pour les liens sociaux AUTO-DÉCLARÉS du profil v2 (instagram/x/youtube/tiktok/facebook/discord)",
      "cadence": "quotidienne (watch)",
      "notes": "vague 4 — marqueur québécois requis dans pseudo/slug/bio ; la fiche v2 expose les liens sociaux remplis par le créateur lui-même : meilleure matière première du rattachement §12.1"
    },
    {
      "id": "region-bio",
      "famille": "enrichissement",
      "palier": 1,
      "acces": "AUCUNE requête — relit les bios publiques déjà en base : ville DÉCLARÉE avec tournure locative (« de Sherbrooke », « based in Montreal ») ou gentilé (« montréalaise ») → région §7 + ville",
      "signal_identite": "n/a (ne touche pas aux comptes)",
      "cadence": "quotidienne (watch), tout dernier du pipeline",
      "notes": "vague 4 — liste de ~70 villes SÛRES seulement (pas d'Alma/Waterloo/Varennes : prénoms, homonymes hors QC, particules) ; abstention si villes de régions différentes ; ne remplit que les champs vides"
    },
    {
      "id": "instagram-apify",
      "famille": "enrichissement",
      "palier": 4,
      "acces": "acteur Apify maison ka-instagram (endpoint web public web_profile_info, proxy résidentiel)",
      "signal_identite": "cross_link (0.90) pour les liens de bio",
      "cadence": "quotidienne (rotation 5 j, lot <=400)",
      "notes": "profil ultra détaillé : abonnés, bio, liens, 12 derniers posts (likes/commentaires/vues/musique/lieu), engagement, cadence, profils reliés"
    },
    {
      "id": "tiktok-apify",
      "famille": "enrichissement",
      "palier": 4,
      "acces": "acteur Apify maison ka-tiktok (page publique @handle, proxy résidentiel)",
      "signal_identite": "cross_link (0.90) pour le lien de bio",
      "cadence": "quotidienne (rotation 5 j, lot <=300)",
      "notes": "abonnés, coeurs, vidéos, bio, badge, commerce/live + vidéos récentes si SSR"
    },
    {
      "id": "x-apify",
      "famille": "enrichissement",
      "palier": 3,
      "acces": "acteur Apify maison ka-x (syndication officielle des widgets, sans clé)",
      "signal_identite": "cross_link (0.90) pour le site du profil",
      "cadence": "quotidienne (rotation 5 j, lot <=300)",
      "notes": "profil complet + 20 tweets récents avec métriques, hashtags, cadence ; remplace la voie locale 429 (TLS)"
    },
    {
      "id": "facebook-apify",
      "famille": "enrichissement",
      "palier": 4,
      "acces": "acteur Apify maison ka-facebook (HTML public de page, proxy résidentiel) — EXPÉRIMENTAL",
      "signal_identite": "—",
      "cadence": "hebdomadaire (rotation 7 j, lot <=100)",
      "notes": "abonnés/catégorie/avatar de pages publiques ; found=false fréquent (mur de connexion)"
    },
    {
      "id": "threads-apify",
      "famille": "enrichissement",
      "palier": 4,
      "acces": "acteur Apify maison ka-threads (JSON embarqué de la page publique, proxy résidentiel)",
      "signal_identite": "—",
      "cadence": "hebdomadaire (rotation 7 j, lot <=60)",
      "notes": "abonnés, bio, badge + posts récents si embarqués"
    },
    {
      "id": "snapchat-apify",
      "famille": "enrichissement",
      "palier": 3,
      "acces": "acteur Apify maison ka-snapchat (__NEXT_DATA__ de snapchat.com/add)",
      "signal_identite": "—",
      "cadence": "hebdomadaire (rotation 7 j, lot <=60)",
      "notes": "abonnés, bio, badge, catégorie, story/spotlight/lenses, comptes reliés"
    },
    {
      "id": "instagram-decouverte",
      "famille": "decouverte",
      "palier": 4,
      "acces": "acteur Apify maison ka-instagram mode recherche (topsearch web public, proxy résidentiel)",
      "signal_identite": "profil_source (0.98)",
      "cadence": "quotidienne (40 requêtes tournantes sur ~160 combinaisons thème x QC)",
      "notes": "découverte de créateurs IG par mots-clés québécois"
    },
    {
      "id": "youtube-apify",
      "famille": "enrichissement",
      "palier": 3,
      "acces": "acteur Apify maison ka-youtube (ytInitialData de /videos, sans clé)",
      "signal_identite": "—",
      "cadence": "quotidienne (rotation 5 j, lot <=300)",
      "notes": "abonnés, nb vidéos, pays, description + vidéos récentes avec vues ; complète le connecteur youtube (API) quand la clé manque"
    },
    {
      "id": "twitch-apify",
      "famille": "enrichissement",
      "palier": 3,
      "acces": "acteur Apify maison ka-twitch (GQL public Client-ID web, sans clé)",
      "signal_identite": "—",
      "cadence": "quotidienne (rotation 5 j, lot <=300)",
      "notes": "abonnés, partenaire/affilié, live en cours, dernière diffusion, jeux + vidéos récentes"
    },
    {
      "id": "kick-apify",
      "famille": "enrichissement",
      "palier": 4,
      "acces": "acteur Apify maison ka-kick (API v2 publique, proxy résidentiel pour Cloudflare)",
      "signal_identite": "cross_link (0.90) pour les liens sociaux auto-déclarés",
      "cadence": "quotidienne (rotation 5 j, lot <=120)",
      "notes": "abonnés, live, catégories + liens sociaux déclarés → découverte croisée"
    },
    {
      "id": "onlyfans-apify",
      "famille": "enrichissement",
      "palier": 4,
      "acces": "acteur Apify maison ka-onlyfans (page publique, proxy résidentiel) — EXPÉRIMENTAL",
      "signal_identite": "—",
      "cadence": "hebdomadaire (rotation 7 j, lot <=100)",
      "notes": "posts/photos/vidéos/J'aime, prix d'abonnement, bio ; found=false fréquent (mur Cloudflare)"
    },
    {
      "id": "fansly-apify",
      "famille": "enrichissement",
      "palier": 3,
      "acces": "acteur Apify maison ka-fansly (API v1 publique, lots de 20)",
      "signal_identite": "—",
      "cadence": "hebdomadaire (rotation 7 j, lot <=200)",
      "notes": "abonnés, bio, badge, compteurs de médias, avatar"
    },
    {
      "id": "tavily-seed",
      "famille": "decouverte",
      "palier": 3,
      "acces": "dataset local moissonné par le Tavily scout (outils/creator-harvest.py) : handles extraits des annuaires/palmarès/agences QC",
      "signal_identite": "listes_medias (0.85)",
      "cadence": "par vague (re-moisson périodique)",
      "notes": "expansion massive du bassin ; existence vérifiée ensuite par lenrichissement Apify"
    },
    {
      "id": "snowball-ig",
      "famille": "decouverte",
      "palier": 1,
      "acces": "base interne : profils reliés Instagram (related_profiles) captés par instagram-apify",
      "signal_identite": "mention (0.60) — à confirmer par enrichissement",
      "cadence": "quotidienne (parcours en largeur, cap 4000/vague)",
      "notes": "levier déchelle : chaque profil IG suggère ~10-20 comptes similaires"
    },
    {
      "id": "patreon-apify",
      "famille": "enrichissement",
      "palier": 3,
      "acces": "acteur Apify maison ka-patreon (données embarquées de la page publique)",
      "signal_identite": "—",
      "cadence": "hebdomadaire (rotation 7 j)",
      "notes": "patrons, publications, nom, avatar"
    },
    {
      "id": "discord-apify",
      "famille": "enrichissement",
      "palier": 2,
      "acces": "acteur Apify maison ka-discord (API publique d'invitation, code sensible à la casse depuis l'URL)",
      "signal_identite": "—",
      "cadence": "hebdomadaire (rotation 7 j)",
      "notes": "membres approximatifs, en ligne, boosts, nom/description du serveur"
    }
  ]
}