{"openapi":"3.1.1","info":{"title":"Pouls · API publique des sondages présidentielle 2027","version":"1","description":"Lecture du contrat pré-agrégé utilisé par Pouls. Données sourcées, marges d'erreur et contexte des enquêtes. Un sondage n'est pas une prédiction. Les champs inconnus doivent être ignorés pour accepter les ajouts compatibles."},"servers":[{"url":"https://api.pouls.app"}],"externalDocs":{"description":"Usage, fraîcheur des données et précautions de lecture","url":"https://pouls.app/api"},"security":[],"paths":{"/v1/polls.json":{"get":{"operationId":"getPolls","summary":"Lire les moyennes, tendances, sondages individuels et duels mesurés","description":"Accès public sans authentification ni paramètres. Le contrat est pré-calculé par un cron toutes les 30 minutes ; une requête ne déclenche normalement aucun recalcul. Vérifier updatedAt et conserver la dernière copie valide en cas d'indisponibilité. Renvoyer l'ETag reçu dans If-None-Match : 304 sans corps si le contrat n'a pas changé. CORS autorise les lectures depuis toutes les origines.","parameters":[{"name":"If-None-Match","in":"header","required":false,"description":"ETag de la copie détenue. Forme faible (W/) acceptée : Cloudflare l'utilise après compression.","schema":{"type":"string"}}],"responses":{"200":{"description":"Contrat JSON agrégé, avec sa version, sa source et sa date de génération.","headers":{"Cache-Control":{"description":"no-store : gérer explicitement la dernière copie valide côté client.","schema":{"type":"string","const":"no-store"}},"ETag":{"description":"Empreinte du contrat servi, à renvoyer dans If-None-Match.","schema":{"type":"string"}},"Access-Control-Allow-Origin":{"description":"Lecture autorisée depuis toute origine.","schema":{"type":"string","const":"*"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PollsContract"}}}},"304":{"description":"Contrat inchangé depuis l'ETag fourni : réutiliser la copie détenue. Aucun corps.","headers":{"ETag":{"description":"ETag du contrat courant, identique à celui fourni.","schema":{"type":"string"}}}},"default":{"description":"Erreur HTTP si aucun contrat ne peut être servi ou construit. Le format du corps d'erreur n'est pas garanti ; conserver la dernière copie valide."}}}}},"components":{"schemas":{"PollsContract":{"type":"object","properties":{"version":{"type":"integer","minimum":1,"description":"Version du contrat, actuellement 1. Les champs additifs ne changent pas cette version ; une nouvelle valeur signale un changement incompatible."},"updatedAt":{"type":"string","format":"date-time","description":"Horodatage de génération des données ; ne désigne pas la date de terrain du dernier sondage."},"source":{"$ref":"#/components/schemas/SourceMeta"},"window":{"$ref":"#/components/schemas/WindowMeta"},"sondages":{"type":"array","items":{"$ref":"#/components/schemas/SondageEntry"},"description":"Moyennes pondérées de premier tour, triées par score décroissant, toutes hypothèses confondues."},"tendance":{"type":"array","items":{"$ref":"#/components/schemas/TrendSeries"}},"recentPolls":{"type":"array","items":{"$ref":"#/components/schemas/PollDetail"},"description":"Sondages récents de premier tour : une entrée par hypothèse testée, pas par enquête indépendante."},"duels":{"type":"array","items":{"$ref":"#/components/schemas/DuelEntry"},"description":"Face-à-face de second tour réellement mesurés, sans projection."},"latestPollId":{"type":["string","null"],"description":"Identifiant du dernier sondage, ou null si aucun sondage n'est disponible."},"newPolls":{"type":"array","items":{"type":"string"},"description":"Identifiants apparus depuis la génération précédente du contrat ; pas un diff propre à chaque client."}},"required":["version","updatedAt","source","window","sondages","tendance","recentPolls","duels","latestPollId","newPolls"]},"SourceMeta":{"type":"object","properties":{"name":{"type":"string"},"url":{"type":"string","format":"uri"},"credit":{"type":"string"}},"required":["name","url","credit"]},"WindowMeta":{"type":"object","properties":{"days":{"type":"integer","minimum":0,"description":"Fenêtre glissante de premier tour, élargie si la densité des sondages est insuffisante."},"nPolls":{"type":"integer","minimum":0,"description":"Nombre de sondages distincts de la fenêtre, après déduplication des hypothèses."},"instituts":{"type":"array","items":{"type":"string"}},"from":{"type":"string","format":"date"},"to":{"type":"string","format":"date"}},"required":["days","nPolls","instituts","from","to"]},"SondageEntry":{"type":"object","properties":{"candidatId":{"type":"string"},"candidat":{"type":"string"},"parti":{"type":"string"},"famille":{"type":"string"},"couleur":{"type":"string"},"score":{"type":"number","minimum":0,"maximum":100},"marge":{"type":"number","minimum":0,"description":"Marge ± en points de pourcentage. Celle d'un sondage typique, pas d'un échantillon cumulé."},"tendance":{"type":"string","enum":["up","down","stable"],"description":"Hausse/baisse si la variation dépasse la marge ; stable sinon."},"delta":{"type":"number","description":"Variation en points face à la fenêtre précédente. Une variation dans la marge n'est pas significative."},"nPolls":{"type":"integer","minimum":0}},"required":["candidatId","candidat","parti","famille","couleur","score","marge","tendance","delta","nPolls"]},"TrendPoint":{"type":"object","properties":{"date":{"type":"string","format":"date"},"value":{"type":"number","minimum":0,"maximum":100,"description":"Moyenne glissante qu'aurait affichée Pouls à cette date."},"nPolls":{"type":"integer","minimum":0,"description":"Nouveaux sondages du bucket, pas nombre de sondages de la moyenne glissante."},"marge":{"type":"number","minimum":0,"description":"Marge ± en points de pourcentage. Celle d'un sondage typique, pas d'un échantillon cumulé."}},"required":["date","value"]},"TrendSeries":{"type":"object","properties":{"candidatId":{"type":"string"},"candidat":{"type":"string"},"couleur":{"type":"string"},"points":{"type":"array","items":{"$ref":"#/components/schemas/TrendPoint"}}},"required":["candidatId","candidat","couleur","points"]},"PollCandidate":{"type":"object","properties":{"candidatId":{"type":"string"},"candidat":{"type":"string"},"couleur":{"type":"string"},"intentions":{"type":"number","minimum":0,"maximum":100},"marge":{"type":"number","minimum":0,"description":"Marge ± en points pour ce candidat dans ce sondage individuel."}},"required":["candidatId","candidat","couleur","intentions","marge"]},"PollDetail":{"type":"object","properties":{"pollId":{"type":"string"},"institut":{"type":"string"},"commanditaire":{"type":"string"},"debut":{"type":"string","format":"date"},"fin":{"type":"string","format":"date"},"echantillon":{"type":"integer","minimum":0},"population":{"type":"string"},"hypothese":{"type":"string"},"tour":{"type":"string"},"candidats":{"type":"array","items":{"$ref":"#/components/schemas/PollCandidate"}}},"required":["pollId","institut","commanditaire","debut","fin","echantillon","population","hypothese","tour","candidats"]},"DuelSide":{"type":"object","properties":{"candidatId":{"type":"string"},"candidat":{"type":"string"},"couleur":{"type":"string"},"score":{"type":"number","minimum":0,"maximum":100},"marge":{"type":"number","minimum":0,"description":"Marge ± en points de pourcentage. Celle d'un sondage typique, pas d'un échantillon cumulé."}},"required":["candidatId","candidat","couleur","score","marge"]},"DuelEntry":{"type":"object","properties":{"id":{"type":"string"},"a":{"$ref":"#/components/schemas/DuelSide"},"b":{"$ref":"#/components/schemas/DuelSide"},"nPolls":{"type":"integer","minimum":0},"lastFin":{"type":"string","format":"date"},"instituts":{"type":"array","items":{"type":"string"}}},"required":["id","a","b","nPolls","lastFin","instituts"]}}}}