Aller au contenu

API turf.bzh - documentation

Accès programmatique aux données hippiques turf.bzh : programme, partants exacts, cotes PMU en direct, arrivées, Quinté+. Format JSON, lecture seule. Que vous vouliez remplir un tableur, recevoir une alerte ou brancher un bot, commencez par les Premiers pas ci-dessous : une URL qui marche dans votre navigateur suffit.

Vous ne programmez pas ? Cette page n'est pas pour vous, et vous n'en avez pas besoin.

Débloquer la Licence API (49,90 € une fois) Le pack IA Claude / Codex Modèles prêts à l'emploi Gérer ma clé

Les sections sont repliées. Le champ ci-dessus cherche dans les 89 champs de l'API et dans le texte de toutes les sections. Dépliez tout si vous préférez la recherche de votre navigateur.

Télécharger cette documentation

La même chose qu'ici, à plat et sans mise en page, pour la donner à un assistant ou la garder hors ligne. Le contenu est reconstruit à chaque téléchargement depuis le code de l'API : il ne peut pas décrire une version qui n'est plus en service.

03/11/2026 Ce qui vient d'arriver

Version 1.6.0 (03/11/2026) : le statut D4 de la journée, et la sortie précédente de chaque cheval. /v1/journees/{date}/d4 rend, pour chaque cheval de trot déferré des 4 pieds ou qui ne l'est plus, ce que montre la page Sélection Trot D4 : statut, nombre de courses sans D4, rang dans la série, dernier D4 et sa date, premier D4 connu, risque de faute et taux de fautes attendu pour le profil. Avec champs=tout, chaque partant porte aussi Alloc_Precedente, Place_Precedente et Rapport_Alloc, trois colonnes du fichier CSV de la journée qui manquaient à l'API. Le D4, la sortie précédente.

Version 1.5.0 (02/11/2026) : la masse de chaque cote, et ce qui rend complets les rapports du jour. /cotes, /cotes/historique et /journees/{date}/cotes disent de quelle masse vient la cote : point_de_vente, ou en_ligne quand les points de vente ne publiaient pas encore de cote exploitable. Les rapports d'une course du jour portent meta.complet_motif : statut_pmu quand le PMU a déclaré l'arrivée complète, delai quand les 2 masses sont lues 90 minutes après le départ sans ce signal. Le détail.

Les cotes en direct de toute la journée, en un seul appel. /v1/journees/{date}/cotes sert la cote du moment de chaque partant de chaque course, sa cote du matin, la probabilité du marché, la Cote BZH et l'écart entre les deux, avec trois vues : par partant, par course, ou les seuls partants qui passent vos filtres. Le pack Excel version 2.1 l'exploite dans 3 feuilles qui s'actualisent seules toutes les 5 minutes. Télécharger le pack.

Le pack Excel version 2 couvre tous les points d'accès. 38 requêtes Power Query prêtes à l'emploi, un onglet Réglages pour la clé, la date et la période, un catalogue des adresses. Télécharger le pack.

Version 1.4.0 (16/10/2026) : l'évolution du poids et de la valeur d'un cheval de galop. Chaque partant d'une course de galop porte désormais six champs calculés : de combien son poids et sa valeur de handicap ont changé depuis sa dernière course, et les chiffres de cette course. Ce sont ceux des colonnes « Évol. poids » et « Évol. valeur » du tableau des partants. Ils sortent d'office avec champs=tout, pour une course et pour une journée. Aucune adresse nouvelle, aucun champ n'est retiré ni renommé.

L'évolution du poids et de la valeur, six champs par partant

Pour chaque partant d'une course de galop (plat, haies, steeple, cross), l'API donne l'écart de poids et l'écart de valeur de handicap avec sa dernière course, et les chiffres de cette dernière course. Un cheval qui porte 3 kg de plus qu'à sa dernière sortie sort avec delta_poids = 3.

ChampCe qu'il contient
delta_poidsLe poids du jour moins celui de la dernière course, en kilos, arrondi à 0,1. Négatif : le cheval porte moins. La dernière course peut être dans une autre discipline.
delta_valeurLa valeur de handicap du jour moins celle de la dernière course, arrondie à 0,1. Positif : le handicapeur juge le cheval meilleur. Calculé seulement si les deux courses sont dans la même discipline.
poids_precLe poids porté à la dernière course, en kilos.
valeur_precLa valeur de handicap de la dernière course, donnée seulement si cette course est dans la même discipline que celle du jour.
date_precLa date de la dernière course, au format AAAA-MM-JJ.
disc_precLa discipline de la dernière course : P, H, S, C, A ou M.
GET /api/v1/courses/2026-10-16/R1C1
GET /api/v1/journees/2026-10-16/partants

Les six champs sont servis par ces deux adresses avec champs=tout, qui est le jeu par défaut. champs=base et champs=indicateurs gardent exactement les colonnes d'hier, et /v1/dossier ne change pas.

Galop seulement. Au trot, les six champs valent null. Ils valent aussi null pour un cheval sans course précédente connue, pour un non partant, et pour la valeur hors handicap (le plat sans handicap n'a pas de valeur). Un champ inconnu n'est jamais un 0 : null veut dire inconnu, 0 veut dire stable.

Connus avant le départ. La dernière course est la dernière course non NP du cheval cherchée strictement avant le jour de la course : rien de ce qui se passe le jour même n'entre dans le calcul, et un backtest ne triche pas avec le futur. La base de courses commence en février 2025 : un cheval dont la dernière course est plus ancienne n'a pas d'écart.

À savoir. Le poids et la valeur du jour viennent de la base : pour la journée en cours, ils peuvent différer de ce que le tableau des partants affiche, qui lit le programme du PMU. Un poids qui baisse n'est pas toujours un avantage réel (jockey plus léger, décharge d'apprenti, changement de catégorie) : les poids sont comparés tels qu'enregistrés. Les exports mensuels de la base gardent leurs colonnes actuelles.

Le statut D4 de la journée, une ligne par cheval concerné

Pour chaque course de trot en France, attelé et monté, l'API donne les partants déferrés des 4 pieds aujourd'hui ou à leur course précédente, avec ce qu'affiche la page Sélection Trot D4 : le statut de ferrure, l'historique de la série et le risque de faute mesuré pour le profil. Ce sont les mêmes calculs que ceux de la page, pas une version à part.

GET /api/v1/journees/2026-09-30/d4
GET /api/v1/journees/2026-09-30/d4?statut=frais,plus_d4
GET /api/v1/journees/2026-09-30/d4?format=csv
StatutCe qu'il dit
frais1re D4 : déferré des 4 pieds après une course sans D4.
enchaine2e ou 3e course de suite déferré des 4 pieds.
habitueAu moins la 4e course de suite déferré des 4 pieds.
plus_d4D4 à sa course précédente, mais pas à celle-ci.
inconnuD4 aujourd'hui, mais la ferrure de sa course précédente n'est pas connue.
ChampCe qu'il contient
statut, statut_libelleLe statut ci-dessus, et son libellé tel qu'il s'affiche sur la page.
courses_sans_d4_avantPour une 1re D4 seulement : le nombre de courses sans D4 qui la précèdent. null sinon.
rang_serie_d4Le rang de la course dans la série de D4 en cours (1 pour une 1re D4, 2 pour la suivante). null pour un cheval qui n'est pas D4.
dernier_d4_place, dernier_d4_dateLa place obtenue lors du dernier D4 connu du cheval et sa date (AAAA-MM-JJ). D signifie disqualifié.
premier_d4_connutrue pour une 1re D4 sans aucun D4 sur au moins 8 courses connues.
risque_faute, risque_faute_profilLe niveau du risque de faute (eleve, habituel ou faible) et le profil mesuré, en clair. Le niveau n'est eleve ou faible que si l'écart avec l'attendu est significatif et d'au moins 10 %.
taux_faute_profil_pct, taux_faute_attendu_pct, nb_chevaux_profilLe taux de disqualifiés observé pour ce profil depuis février 2025, le taux attendu pour le même profil à cotes égales (une constante du profil, qui ne dépend pas de la cote du cheval ce jour-là), et le nombre de chevaux du profil.
note_ia, cote, arriveeLa note IA, la cote (celle du moment, ou la cote finale d'une course courue) et la place à l'arrivée quand la course est courue.

Chaque ligne porte aussi la course (code_course, heure, hippodrome, discipline, distance), le cheval (numero, idcheval, cheval, driver) et sa ferrure du jour. Les non-partants connus sont écartés. Le paramètre statut accepte un ou plusieurs statuts séparés par des virgules. Le meta donne etat (passe, jour ou demain), source, complet, calcule_le et compteurs, le nombre de chevaux de la journée par statut, sans les partants non classés d'une course courue.

Une série ne se compte que sur une course précédente confirmée. Le statut exige que la dernière course enregistrée du cheval soit bien sa dernière course : la musique du jour doit commencer par la place obtenue à cette course. Sans cette confirmation, un cheval D4 sort en inconnu et un cheval qui n'est pas D4 n'est pas listé. La base de courses commence en février 2025, et elle ne contient pas toutes les courses.

Le jour même et le lendemain. Comme la page, le calcul lit aussi le programme du PMU : ferrure de dernière minute, non-partants, cote du moment, et les courses du lendemain, que la base ne connaît pas encore (source vaut base+flux, ou flux le lendemain). Il le fait aussi pour une journée de moins de 3 jours dont les résultats manquent. Si le PMU ne répond pas, source vaut base : la ferrure est celle chargée la nuit et la cote du jour est vide. Une liste vide avec un source vide veut dire que rien n'a pu être calculé (programme du lendemain pas encore publié, par exemple), pas qu'aucun cheval n'est D4. Une journée passée est définitive quand toutes ses courses sont courues (complet vaut true).

Bornes. Du 01/02/2025 au lendemain. Une date antérieure ou plus lointaine est refusée en 422 ; une journée sans course de trot en France rend une liste vide. Un appel compte pour un appel de quota.

À savoir. Un statut de ferrure ne désigne pas un gagnant. Sur la mesure du site, les chevaux dont c'est le premier D4 connu commettent nettement plus de fautes que ceux de même cote, et l'écart à la victoire n'est pas significatif. Croisez le statut avec la classe de la course, le driver et la cote.

La sortie précédente, trois champs de plus par partant

Avec champs=tout, qui est le jeu par défaut, chaque partant porte trois champs de plus, ceux du fichier CSV de la journée. Ils décrivent la dernière course du cheval, jamais celle du jour.

ChampCe qu'il contient
Alloc_PrecedenteL'allocation de la dernière course du cheval, en euros.
Place_PrecedenteLa place obtenue à cette course : un rang, ou une lettre (D pour disqualifié, A ou T pour un arrêt ou une chute).
Rapport_AllocL'allocation du jour divisée par Alloc_Precedente : sous 0,90 le cheval descend de catégorie, au-dessus de 1,10 il monte.
GET /api/v1/journees/2026-09-30/partants?champs=tout
GET /api/v1/courses/2026-09-30/R1C1

Les trois champs sont vides quand aucune sortie précédente n'est connue : en septembre 2026, ils étaient renseignés pour 85 % des partants. Ils sont connus avant le départ : sur un échantillon de 5 966 partants de fin septembre, Place_Precedente était identique à la place de la course précédente du cheval dans la base dans 100 % des cas, et Alloc_Precedente dans 99,9 %. Rapport_Alloc est exactement l'allocation du jour divisée par Alloc_Precedente. champs=base et champs=indicateurs gardent leurs colonnes, et /v1/schema décrit les trois champs.

Les Stats Premium des favoris, une ligne par course

Pour chaque course, sur des courses de référence comparables (même hippodrome, distance, discipline, fourchette de partants et catégorie, élargies pas à pas quand l'échantillon est trop maigre) : la part des courses où l'un des 3 favoris du PMU a gagné, puis a fini placé, et les rapports moyens Simple gagnant et Simple placé pour 1 €. Chaque ligne porte son échantillon (courses_reference, favoris_observes), le niveau de la comparaison et son perimetre en clair.

GET /api/v1/journees/2026-10-11/stats-premium
GET /api/v1/stats-premium?du=2026-09-01&au=2026-09-30&format=csv
GET /api/v1/courses/2026-10-11/R1C1/stats-premium

Ce sont exactement les chiffres affichés sur le site, sans calcul à part : ceux du programme du jour, figés le matin (source vaut programme), ou, pour une journée que le programme n'a jamais figée, le même calcul avec les mêmes critères (calcul). La route par course sert les chiffres de la page de la course (page_course) et la liste de ses courses de référence ; le jour même, ils sont identiques à ceux du programme. Aucune triche avec le futur : pour une course du jour J, seules des courses antérieures à J servent de référence.

Une période n'est jamais servie incomplète. Si des journées jamais affichées au programme restent à calculer, la réponse est un 503 avec error.code = calcul_en_cours, le nombre de journées prêtes et un Retry-After de quelques secondes : relancez la même adresse, les journées déjà calculées sont gardées.

Indicatif : ces chiffres décrivent le passé de courses comparables, ils ne prédisent pas la course.

Le jour même : les rapports dès leur publication

Une course du jour, ou de la veille avant notre collecte de 05:00, est lue en direct sur le flux du PMU, les deux masses, puis gardée en cache : les lignes sont celles que la collecte archivera le lendemain. Le meta porte alors source : direct, lu_le (l'heure de lecture), masses, statut_pmu et complet : true quand les deux masses sont lues et que le PMU a déclaré l'arrivée complète (complet_motif : statut_pmu), ou, à défaut de ce signal, quand les deux masses sont lues 90 minutes après le départ (complet_motif : delai). À false, la réponse est juste mais peut encore s'enrichir (une masse publiée avec retard) : relisez-la plus tard, la course est relue au PMU toutes les 10 minutes au plus. La collecte de 05:00 fait foi pour les deux masses : le lendemain, la réponse porte source : collecte. Tant que les rapports ne sont pas publiés, la route répond 404 et dit pourquoi dans error.reason, et quand revenir dans error.retry_after (en secondes), error.disponible_vers (une heure) et l'en-tête Retry-After.

error.reasonCe qui se passeQuand revenir
pas_encore_courueLa course n'est pas partie.Le jour de la course : environ 30 minutes après le départ, 1 heure au plus par réponse. Pour une date future, la réponse ne donne pas d'heure.
arrivee_pas_encore_definitiveCourse courue, arrivée provisoire.2 minutes, puis 5 minutes passé 45 minutes après le départ.
rapports_pas_encore_publiesArrivée définitive, rapports pas encore publiés.Idem.
aucun_rapport_publieLe PMU ne publie pas de rapport pour cette course (certaines courses internationales et PMH), ou l'arrivée n'est toujours pas définitive 3 heures après le départ.Inutile.
course_annulee / course_inconnueCourse annulée, ou absente du programme.Inutile.
pmu_indisponible (503)Le flux du PMU ne répond pas, ou répond lentement.1 minute, ou quelques secondes si une lecture est déjà en cours.

Interroger plus souvent que Retry-After n'apporte rien : la réponse est la même jusqu'à la publication, et chaque appel compte dans votre quota. Pour suivre l'arrivée elle-même en temps réel, /arrivee reste l'adresse prévue.

Les évolutions précédentes et leur détail sont dans le journal des modifications. Le contrat de l'API v1 ne change pas : des champs sont ajoutés, jamais retirés ni renommés. Vos scripts existants continuent de fonctionner sans retouche.

Échantillon gratuit : 7 journées réelles à télécharger

Avant de prendre la Licence API, voyez ce que l'API renvoie vraiment. Ce zip contient ses réponses telles quelles, sans retouche, pour 7 journées complètes, du jeudi 1er au mercredi 7 octobre 2026 : 403 courses, toutes disciplines. Il sert à vérifier la structure des fichiers et à les brancher sur votre tableur ou votre logiciel. Le téléchargement est libre, sans compte ni clé.

Télécharger le zip (5,2 Mo)

28,5 Mo une fois décompressé. Empreinte SHA-256 du zip :
ed7ee924db52f11818fb2fcb12d7e5289461f229977ed9870ec0acb705266543

Ce que contient le zip

FichierContenu
LISEZMOI.txtLe mode d'emploi du zip : chaque fichier, les journées, les points d'attention.
AAAA-MM-JJ/partants.json et .csvTous les partants de la journée, tous les champs (champs=tout) : cotes, ELO, notes et probabilités IA, et, au galop, l'évolution du poids et de la valeur.
AAAA-MM-JJ/resultats.jsonLes arrivées complètes des courses de la journée.
AAAA-MM-JJ/rapports.json et .csvLes rapports définitifs des deux masses du PMU, en ligne et point de vente, pour tous les paris.
AAAA-MM-JJ/cotes_historique.csvTous les relevés de la cote Simple Gagnant de la journée, avec l'heure de chaque relevé : une ligne par relevé et par partant.
AAAA-MM-JJ/cotes_historique/RxCy.jsonLa série de chaque course, avec l'hippodrome et l'heure de départ programmée.
partants.csv, rapports.csv, cotes_historique.csv (à la racine)Les journées mises bout à bout, avec un seul en-tête : environ 4 600 partants, 9 900 lignes de rapports et 98 000 relevés de cotes.
schema.jsonLe dictionnaire des champs, avec leur taux de remplissage mesuré chaque nuit.
manifest.json, journal.txtChaque fichier avec son empreinte SHA-256 ; chaque appel fait à l'API avec son code HTTP, échecs compris. Rien n'est caché.

À savoir avant de vous en servir

  • Les cotes sont les cotes Simple Gagnant du flux du PMU, relevées par turf.bzh tout au long de la journée. Il n'existe pas de série Simple Placé avant le départ, seulement les rapports définitifs. Un 0 renvoyé par le PMU tant que le marché n'est pas ouvert n'est pas une cote : il n'est pas publié, la cellule reste vide.
  • La cadence est celle de notre collecteur, qui passe toutes les 5 minutes. Mesuré sur les Quinté+ du 27/07 au 05/09/2026 : un relevé toutes les 30 à 35 minutes au-delà de 2 h avant le départ, toutes les 15 minutes environ entre 2 h et 30 min, puis toutes les 5 à 10 minutes jusqu'au départ.
  • Avant le 27 juillet 2026, aucune série de cotes n'existe, et elle ne peut pas être reconstituée.
  • Quatre courses du 5 octobre (R8C1, R8C5, R8C7, R8C8) n'ont pas de série de cotes : l'API répond 404 pour elles, et le journal le montre.
  • Un champ vide veut dire inconnu, jamais zéro. Les six champs d'évolution du poids et de la valeur ne concernent que le galop et restent vides au trot.
  • Usage : évaluation personnelle. L'usage commercial et la redistribution des données ne sont pas permis (CGV, article 18).

Pour aller plus loin

Les mêmes fichiers s'obtiennent, pour n'importe quelle date de la base (depuis février 2025), avec la Licence API et une clé :

GET /api/v1/journees/{date}/partants?champs=tout
GET /api/v1/rapports?date={date}
GET /api/v1/courses/{date}/{RxCy}/cotes/historique

Ce zip a demandé environ 840 appels, espacés de 1,2 seconde pour rester sous la limite de 60 requêtes par minute (10 000 par jour et par clé). Pour charger la base entière, les exports mensuels en CSV compressé (GET /api/v1/exports) sont plus pratiques que les appels course par course.

Premiers pas en 5 minutes

Le chemin le plus court, sans écrire une ligne de code : une adresse qui marche dans votre navigateur.

  1. Débloquez la Licence API (49,90 € une fois) sur api-licence.php, avec un abonnement turf.bzh actif.
  2. Générez votre clé sur Ma clé API (elle ne s'affiche qu'une fois, copiez-la).
  3. Collez cette adresse dans la barre de votre navigateur, en remplaçant la clé, et validez :
    https://www.turf.bzh/api/v1/programme?api_key=tbz_live_VOTRE_CLE
    Vous voyez le programme du jour en JSON. Si ça s'affiche, vous êtes prêt.
  4. Pour un tableau propre qui s'ouvre dans Excel, ajoutez &format=csv à la fin de l'adresse.
Prendre un modèle prêt à l'emploi
Pratique, mais à connaître : la clé dans l'adresse (?api_key=) convient à un test rapide ou à un fichier gardé sur votre ordinateur. Ne la mettez jamais dans une page web publique ni un lien partagé : dans ce cas, préférez l'en-tête Authorization côté serveur (voir Authentification).

Accès

Deux conditions, vérifiées à chaque requête :

  • La Licence API (paiement unique, à vie) : api-licence.php
  • Un abonnement turf.bzh actif. En cas de résiliation, l'API est suspendue ; elle se réactive seule au réabonnement, sans re-payer.

URL de base : https://www.turf.bzh/api/v1/ - HTTPS uniquement. Les réponses ne sont jamais mises en cache (Cache-Control: no-store) : ce que vous recevez est frais.

Une clé, deux usages. La même clé couvre les endpoints DONNÉES (programme, partants, cotes en direct, arrivées, base historique - illimités) et les endpoints CHATBZH (question à l'agent, méthodes), qui consomment vos crédits IA comme sur le site.

Authentification

Générez votre clé personnelle sur Ma clé API (affichée une seule fois). Envoyez-la à chaque requête, de préférence en en-tête :

Authorization: Bearer tbz_live_VOTRE_CLE

Alternatives tolérées : l'en-tête X-Api-Key: tbz_live_..., ou le paramètre ?api_key=tbz_live_... pour les outils qui ne savent pas poser d'en-tête (Google Sheets, tableurs, liens de test). Attention avec le paramètre d'URL : la clé peut traîner dans des historiques. En cas de doute, régénérez-la (l'ancienne meurt immédiatement).

La clé est personnelle : pas de partage, pas d'usage mutualisé. Chaque clé est auditée.

Quickstart

curl - le programme du jour

curl -H "Authorization: Bearer tbz_live_VOTRE_CLE" \
  "https://www.turf.bzh/api/v1/programme"

Python - cotes live d'une course, toutes les 60 secondes

import requests, time

KEY = "tbz_live_VOTRE_CLE"
URL = "https://www.turf.bzh/api/v1/courses/2026-11-05/R1C4/cotes"

while True:
    r = requests.get(URL, headers={"Authorization": f"Bearer {KEY}"}, timeout=15)
    j = r.json()
    if "error" in j:
        print("Erreur:", j["error"]["message"])
        break
    for p in j["data"]["partants"]:
        print(p["num"], p["cheval"], p["cote"])
    time.sleep(60)  # restez au-dessus de 30 s : les cotes sont cachées côté serveur

Google Sheets - partants dans une feuille

Le plus simple, sans en-tête à gérer : la fonction IMPORTDATA avec la clé en paramètre et &format=csv. Mettez votre clé dans une cellule (ici A1) :

=IMPORTDATA("https://www.turf.bzh/api/v1/programme?api_key=" & A1 & "&format=csv")

Pour poser un vrai en-tête Authorization (plus propre), passez par Apps Script (Extensions > Apps Script) :

function partants() {
  var url = "https://www.turf.bzh/api/v1/courses/2026-11-05/R1C4";
  var res = UrlFetchApp.fetch(url, {
    headers: { Authorization: "Bearer tbz_live_VOTRE_CLE" }
  });
  var data = JSON.parse(res.getContentText()).data;
  var sheet = SpreadsheetApp.getActiveSheet();
  sheet.clear();
  sheet.appendRow(["Num", "Cheval", "Driver", "Cote BZH", "Note IA"]);
  data.partants.forEach(function (p) {
    sheet.appendRow([p.num, p.name, p.jockey_driver, p.Cote_BZH, p.Note_IA]);
  });
}

Galerie de cas d'usage

Tutoriel - Excel qui se remplit tout seul

L'astuce : la même clé accepte ?api_key= dans l'URL et ?format=csv pour renvoyer un tableau propre. Excel n'a alors aucun en-tête à gérer, il suffit de lui donner une adresse.

Le plus simple : le pack prêt à l'emploi

Téléchargez le Pack Excel turf.bzh : 41 requêtes Power Query, une par point d'accès (journée, période de dates, course, cheval, personne, compte), plus un suivi de bankroll. Tout est déjà dans le classeur : vous collez votre clé dans la cellule jaune de l'onglet Réglages, vous réglez la date et la période, et chaque onglet s'actualise d'un clic (Données > Actualiser). L'onglet Catalogue donne l'adresse prête à l'emploi de chaque appel. Vous n'avez aucun code à écrire ni à copier. Le tutoriel complet du pack montre chaque étape en captures et en animations.

Vous avez téléchargé le pack avant le 20/10/2026 ? Cette version-là ne couvrait que 3 points d'accès. Retéléchargez le classeur, puis recopiez votre clé, votre date et votre course dans l'onglet Réglages. Rien d'autre à refaire.

Le faire vous-même (Power Query)

Vérifiez d'abord que votre Excel sait le faire. Power Query est intégré d'origine à partir d'Excel 2016 sur Windows (onglet Données > Récupérer et transformer). Sur Excel 2010 et 2013, c'était un complément à installer séparément, et Microsoft ne le met plus à jour ; pour Excel 2010 il réclamait la version Professionnel Plus (avec Software Assurance). Il n'a jamais existé pour Excel 2007. Côté Mac, Excel 2016 et Excel 2019 pour Mac n'ont pas Power Query du tout : il faut Excel pour Microsoft 365 pour Mac. Si vous n'avez rien de tout cela, ce n'est pas bloquant : le CSV s'ouvre directement dans n'importe quel tableur, et Google Sheets fait la même chose avec IMPORTDATA.
  1. Ruban Données > Obtenir des données > À partir d'autres sources > Requête vide.
  2. Ouvrez Affichage > Éditeur avancé et collez ce code (adaptez l'endpoint) :
    let
        Cle = "tbz_live_VOTRE_CLE",
        Source = Csv.Document(
            Web.Contents("https://www.turf.bzh",
                [ RelativePath = "api/v1/programme",
                  Query = [ api_key = Cle, #"format" = "csv" ] ]),
            [Delimiter=";", Encoding=65001, QuoteStyle=QuoteStyle.Csv]),
        Promu = Table.PromoteHeaders(Source, [PromoteAllScalars=true])
    in
        Promu
  3. Fermer et charger. À la première fois, Excel demande comment se connecter à la source : choisissez Anonyme, puis Se connecter. S'il demande aussi un niveau de confidentialité, cochez Ignorer les vérifications des niveaux de confidentialité pour ce fichier, ou choisissez Organisationnel, jamais Privé.
  4. Ensuite, Données > Actualiser tout met le tableau à jour quand vous voulez.
Le piège classique de Power Query (erreur "source de données dynamique") vient d'une URL construite en un seul morceau. On l'évite en gardant une adresse de base fixe (https://www.turf.bzh) et en passant le reste via RelativePath et Query, comme ci-dessus. Le pack Excel applique déjà cette recette. Rappel : votre clé est en clair dans le classeur, gardez-le sur votre ordinateur.
Copiez le code depuis cette page, pas depuis une cellule Excel. Quand on copie une cellule qui contient des retours à la ligne, Excel entoure tout le bloc de guillemets : Power Query lit alors le code comme un simple texte et vous renvoie une seule cellule au lieu du tableau. Si vous obtenez une ligne unique commençant par let, c'est cela qui s'est produit.

Les 19 adresses qui rendent un tableau

19 adresses acceptent ?format=csv. Elles renvoient un fichier CSV en UTF-8 avec le point-virgule comme séparateur, donc directement lisible par Excel, LibreOffice, Google Sheets, ou n'importe quel outil capable d'aller chercher une adresse web. Aucune n'exige Power Query : elles marchent avec un Excel ancien, un script, ou simplement en enregistrant le fichier depuis votre navigateur.

Les voici en entier, prêtes à copier. Remplacez VOTRE_CLE par la vôtre, et adaptez la date et le code course :

# Le programme du jour
https://www.turf.bzh/api/v1/programme?api_key=VOTRE_CLE&format=csv

# Les partants d'une course, avec tous les indicateurs
https://www.turf.bzh/api/v1/courses/2026-11-05/R1C4?api_key=VOTRE_CLE&format=csv

# Les indicateurs de chaque partant de cette course
https://www.turf.bzh/api/v1/courses/2026-11-05/R1C4/indicateurs?api_key=VOTRE_CLE&format=csv

# Le dossier complet de cette course, une ligne par partant
https://www.turf.bzh/api/v1/courses/2026-11-05/R1C4/dossier?api_key=VOTRE_CLE&format=csv

# Les cotes de cette course, en direct
https://www.turf.bzh/api/v1/courses/2026-11-05/R1C4/cotes?api_key=VOTRE_CLE&format=csv

# Toute la série des cotes de cette course, du matin au départ
https://www.turf.bzh/api/v1/courses/2026-11-05/R1C4/cotes/historique?api_key=VOTRE_CLE&format=csv

# Le classement du jour pour un indicateur
https://www.turf.bzh/api/v1/tops?indicateur=ELO_Cheval&n=10&api_key=VOTRE_CLE&format=csv

# Les value bets du jour
https://www.turf.bzh/api/v1/value-bets?n=10&api_key=VOTRE_CLE&format=csv

# Tous les partants d'une journee entiere, cote de depart comprise
https://www.turf.bzh/api/v1/journees/2026-08-01/partants?api_key=VOTRE_CLE&format=csv

# Les cotes en direct de toutes les courses du jour, une ligne par course
https://www.turf.bzh/api/v1/journees/2026-11-05/cotes?vue=courses&courses=a_venir&api_key=VOTRE_CLE&format=csv

# Le statut D4 des chevaux de trot du jour
https://www.turf.bzh/api/v1/journees/2026-11-05/d4?api_key=VOTRE_CLE&format=csv

# Le dictionnaire des champs, avec leur taux de remplissage reel
https://www.turf.bzh/api/v1/schema?api_key=VOTRE_CLE&format=csv

# Les dernieres courses d'un cheval (l'idcheval vient de /v1/chevaux?recherche=)
https://www.turf.bzh/api/v1/chevaux/VOTRE_IDCHEVAL/historique?api_key=VOTRE_CLE&format=csv

# Les rapports definitifs d'une course, les DEUX masses PMU
https://www.turf.bzh/api/v1/courses/2026-07-20/R1C5/rapports?api_key=VOTRE_CLE&format=csv

# Tous les rapports d'une journee entiere
https://www.turf.bzh/api/v1/rapports?date=2026-07-20&api_key=VOTRE_CLE&format=csv

# Le taux de couverture de nos selections, sur trente jours
https://www.turf.bzh/api/v1/performances?fenetre=30j&api_key=VOTRE_CLE&format=csv

# Les Stats Premium des favoris d'une journee, une ligne par course
https://www.turf.bzh/api/v1/journees/2026-11-05/stats-premium?api_key=VOTRE_CLE&format=csv

# Les memes sur un mois (31 jours au plus)
https://www.turf.bzh/api/v1/stats-premium?du=2026-09-01&au=2026-09-30&api_key=VOTRE_CLE&format=csv

# Les courses de reference d'une course
https://www.turf.bzh/api/v1/courses/2026-11-05/R1C4/stats-premium?api_key=VOTRE_CLE&format=csv

Quelles dates chacune accepte

C'est la question qui revient le plus souvent, alors autant l'écrire une fois pour toutes.

AdresseDates acceptées
/programmeToute date présente en base, paramètre date=AAAA-MM-JJ ; sans lui, le jour même. Depuis le 21/08/2026 : cette adresse ne refuse plus les journées passées. Une date absente de la base renvoie 404 en donnant les bornes réelles. filter=upcoming n'a de sens que le jour même et renvoie 422 sur une journée passée.
/courses/{date}/{rc}Toute date présente en base, y compris des années en arrière.
/courses/{date}/{rc}/indicateursComme la course : toute date présente en base.
/courses/{date}/{rc}/dossierToute date présente en base. Les sections servies dépendent de la date : les cotes en direct le jour même, les rapports définitifs pour une course passée.
/cotesLe jour même. Ce sont les cotes PMU en direct, elles n'existent pas pour une course passée.
/cotes/historiqueToute journée depuis le 27/07/2026, date de début de la collecte. Avant, rien n'existe et rien ne peut être reconstruit.
/topsLe jour même par défaut, paramètre date optionnel sur les journées conservées.
/value-betsIdem : date optionnel, aujourd'hui par défaut.
/journees/{date}/partantsToute date présente en base. Les plages sont refusées en 422 : bouclez sur les dates, ou prenez les exports mensuels.
/journees/{date}/cotesLe jour même, en direct, et toute journée passée depuis le 27/07/2026 (collecte des cotes) : la cote est alors la dernière relevée avant le départ, avec l'arrivée et le rapport simple gagnant.
/journees/{date}/d4Du 01/02/2025 au lendemain. Une date antérieure ou plus lointaine est refusée en 422. Le jour même et le lendemain, le programme du PMU est lu à l'appel, comme sur la page Sélection Trot D4 ; une liste vide avec un meta.source vide veut dire que rien n'a pu être calculé.
/schemaSans objet : le dictionnaire décrit la base, pas une journée.
/chevaux/{id}/historiqueToute la carrière connue. Bornes debut et fin optionnelles ; sans elles, fenêtre glissante de 90 jours.
/courses/{date}/{rc}/rapportsToute journée depuis le 01/01/2026, début de la collecte des rapports. Avant, rien n'a été collecté et rien ne peut être reconstruit. Le meta.collecte donne les bornes exactes. Le jour même aussi, dès que le PMU publie les rapports de la course (meta.source vaut alors direct).
/rapportsLes journées archivées. date optionnel, aujourd'hui par défaut ; une date future renvoie 422, les rapports définitifs n'existant qu'après la course. La journée en cours, et la veille avant 05:00, renvoient 404 journee_pas_encore_archivee.
/journees/{date}/stats-premiumToute journée passée, le jour même et le lendemain (le programme du PMU est publié la veille). Avant février 2025, début de la base, aucune course de référence n'existe.
/stats-premiumdu et au requis, 31 jours au plus, au plus tard le lendemain.
/courses/{date}/{rc}/stats-premiumComme la journée.
/performancesSans objet : cinq fenêtres glissantes (7j, 14j, 30j, 90j, annee), arrêtées à la dernière journée dépouillée. Le meta.arrete_au la donne.

Le piège du séparateur décimal

Les nombres sortent avec un point décimal : 4.8 et non 4,8. Un Excel configuré en français les prendra pour du texte, et vos calculs ne suivront pas. Deux façons de s'en sortir, selon la méthode d'import :
  • Import manuel : ruban Données > Convertir, et à la dernière étape, bouton Avancé, indiquez le point comme séparateur décimal.
  • Power Query : au moment de typer les colonnes, précisez la culture "en-US" dans Table.TransformColumnTypes.
Votre clé voyage dans l'adresse. Une de ces URL complète vaut donc mot de passe : ne la collez pas dans un message public, un forum ou une capture d'écran. Si elle vous échappe, régénérez la clé depuis Ma clé API, l'ancienne est révoquée aussitôt. Pour un script, préférez l'en-tête Authorization: Bearer décrit plus haut : la clé n'apparaît alors ni dans l'URL, ni dans les journaux du serveur.

Étudier 21 mois d'archives

C'est la demande qui revient le plus chez les utilisateurs avancés : tester une hypothèse sur l'ensemble de la base plutôt que sur la journée du jour. Trois chemins existent, et le bon dépend uniquement du volume que vous visez. Prendre le mauvais coûte des heures.

Ce que vous voulezPar ou passerCe que ça coûte
Une course précise/v1/courses/{date}/{rc}1 appel
Une journée entière/v1/journees/{date}/partants1 appel, moins de 300 ms
Quelques semainesBoucle sur /v1/journees/{date}/partants1 appel par journée, largement sous le plafond
Toute la base/v1/exports22 téléchargements, 68 Mo environ

Ne parcourez pas la base course par course. Il y a 32 954 courses : au plafond de 10 000 appels par jour, cela représente trois jours calendaires, pour un résultat que les exports donnent en quelques minutes et sans solliciter la base.

La recette, en quatre commandes

K="Authorization: Bearer VOTRE_CLE"
B=https://www.turf.bzh/api/v1

# 1. La liste des mois, avec taille, nombre de lignes et empreinte
curl -s -H "$K" "$B/exports" | jq '.data.fichiers[] | {nom, lignes, partiel, scelle}'

# 2. Tout télécharger, sauf le mois en cours
curl -s -H "$K" "$B/exports" \
  | jq -r '.data.fichiers[] | select(.partiel == false) | .nom' \
  | while read f; do curl -s -H "$K" "$B/exports/$f" -o "$f"; done

# 3. Vérifier que rien n'est arrivé tronqué
for f in partants-*.csv.gz; do gzip -t "$f" || echo "CORROMPU : $f"; done

# 4. Empiler, en ne gardant l'en-tête qu'une fois
zcat partants-2025-*.csv.gz partants-2026-*.csv.gz \
  | awk 'NR==1 || !/^date;/' > base.csv

Trois pièges, mesurés et pas supposés

La dernière journée n'a pas encore sa cote de départ. Elle arrive au rafraîchissement du lendemain matin. Si vous mesurez un rendement en incluant cette journée, vous comptez des lignes sans prix. /v1/statut donne la date exacte où s'arrêter, dans cotes.derniere_journee_avec_cote_de_depart, et l'index des exports la porte aussi.

Un mois clos n'est pas forcément figé. Quand un mois se termine, sa dernière journée entre en base sans ses cotes : le mois est clos mais son contenu bouge encore une nuit. Ces mois-là portent scelle: false et seront régénérés. Ne bâtissez pas une mesure dessus sans revenir la chercher.

Toutes les colonnes ne sont pas remplies. Au 30/09/2026, 14 des 87 colonnes sont remplies à moins de 65 %, et Rapport_SG n'est présent que sur 8,6 % des lignes. Interrogez /v1/schema avant de choisir vos variables : c'est la différence entre un échantillon de 355 000 lignes et un échantillon de 30 000.

La cote servie dans les archives est la cote de départ, celle qui a payé. Ce n'est pas la cote du matin ni une moyenne. Pour la trajectoire d'une cote à l'intérieur d'une journée, il faut /v1/courses/{date}/{rc}/cotes/historique, et cette collecte ne remonte pas avant le 27/07/2026 : le flux PMU ne publie que l'instantané, une courbe passée ne se reconstruit pas après coup.

Tutoriel - alerte "baisse de cote" sur Telegram en 3 étapes

  1. Créez un bot Telegram. Dans Telegram, écrivez à @BotFather, envoyez /newbot : il vous donne un jeton. Récupérez aussi votre identifiant de discussion (chat_id) en écrivant à @userinfobot.
  2. Lisez les mouvements de cote d'une course avec l'endpoint /cotes/mouvements.
  3. Envoyez un message quand une cote baisse au-delà de votre seuil, via l'API Telegram.

Repérez d'abord les champs exacts de la réponse (le nom des colonnes peut évoluer) :

curl -H "Authorization: Bearer tbz_live_VOTRE_CLE" \
  "https://www.turf.bzh/api/v1/courses/2026-11-05/R1C4/cotes/mouvements?fenetre=5&limit=8"

Puis, un script à lancer à intervalle régulier (adaptez les noms de champs à ce que vous voyez) :

import requests

CLE   = "tbz_live_VOTRE_CLE"
BOT   = "JETON_BOTFATHER"      # de @BotFather
CHAT  = "VOTRE_CHAT_ID"        # de @userinfobot
DATE, COURSE = "2026-11-05", "R1C4"
SEUIL = 0.8                    # alerter si une cote perd au moins 0,8 point

api = requests.Session()
api.headers["Authorization"] = f"Bearer {CLE}"
url = f"https://www.turf.bzh/api/v1/courses/{DATE}/{COURSE}/cotes/mouvements"
data = api.get(url, params={"fenetre": 5, "limit": 8}, timeout=15).json().get("data", {})

for m in data.get("mouvements", []):
    evo = m.get("evolution_5min", m.get("evolution", 0)) or 0
    if evo <= -SEUIL:
        txt = f"Baisse {COURSE} : {m.get('cheval')} -> {m.get('cote')} ({evo})"
        requests.get(f"https://api.telegram.org/bot{BOT}/sendMessage",
                     params={"chat_id": CHAT, "text": txt}, timeout=15)

Pour lancer ce script automatiquement, utilisez le Planificateur de tâches (Windows) ou une tâche cron (Mac/Linux), ou passez par n8n/Make (voir plus bas). Pour un bot qui répond à des commandes, voir le tutoriel suivant.

Tutoriel - votre bot turf privé

Nous fournissons un bot Telegram prêt à héberger, en Python, qui tourne sur votre ordinateur et répond à vos commandes :

  • /course R1C4 - la fiche et les partants d'une course
  • /cotes R1C4 - les cotes PMU en direct
  • /tops ELO_Cheval - le top du jour d'un indicateur
  • /value - les value bets du jour
  • /analyse R1C4 - le classement multi-features d'une course

Il n'a besoin d'aucun serveur ni adresse publique (il utilise le "long polling" Telegram). Téléchargez-le, il est livré avec un mode d'emploi :

Télécharger le bot Telegram

En résumé : créez le bot avec @BotFather, mettez votre jeton et votre clé API dans config.py, lancez python bot.py. La même logique se transpose à Discord (bibliothèque discord.py) : une commande, un appel à l'API, une réponse.

Tutoriel - brancher ChatBZH à votre outil

Une question, une réponse complète en un seul appel. L'agent choisit ses outils (programme, cotes, indicateurs, vos méthodes), analyse et renvoie un texte. Cet endpoint consomme vos crédits IA (comme sur le site).

import requests

r = requests.post("https://www.turf.bzh/api/v1/chat",
    headers={"Authorization": "Bearer tbz_live_VOTRE_CLE"},
    json={"question": "Quels sont les 3 meilleurs ELO du Quinté du jour ?",
          "mode": "standard", "max_credits": 100},
    timeout=240)
j = r.json()
if "error" in j:
    print("Refus:", j["error"]["message"])   # ex : plafond de crédits dépassé, avant tout débit
else:
    d = j["data"]
    print(d["reponse"])                       # le texte de l'agent, à afficher dans votre outil
    print("Outils:", d.get("tools_utilises"))
    print("Crédits facturés:", d["credits"]["factures"], "- reste:", d["credits"]["solde_restant"])

Le JSON de réponse contient : reponse (le texte), tools_utilises (les outils appelés), credits.factures et credits.solde_restant, et forfait_applique (60 cr pour appliquer une méthode, 100 cr pour un backtest). Prévoyez un délai : un tour peut durer 30 à 180 secondes. Chaque appel est autonome (pas de mémoire de conversation), mettez tout le contexte utile dans la question.

Tous les champs, et comment les trouver

89 colonnes, celles que l'API sert réellement. Cette liste est construite depuis le code de l'API, pas recopiée à la main : elle ne peut pas décrire un champ qui n'existe plus, ni oublier celui d'hier.

Le nom d'un champ n'est pas toujours celui qu'on a en tête. La forme d'un cheval, par exemple, se lit dans Taux_Place. Cherchez en français : tapez forme, incident ou chrono dans la barre en haut de page, le champ correspondant remonte.

ChampJeu TypeCe que c'est
date base date YYYY-MM-DD Jour de la course.
code_course base texte RxCy Code reunion et course, par exemple R1C3. Attention : dix courses de l'historique, reparties sur trois journees (2025-11-15, 2025-11-16, 2026-07-17), portent le meme code sous deux hippodromes differents, soit 0,036 % des codes. Sur celles-la, la reponse porte ambigu: true et un des deux pelotons est incomplet en base. Utilisez le parametre hippodrome pour les separer.
hippodrome base texte Nom de l'hippodrome.
num base entier Numero du partant, celui qu'on coche sur un ticket.
idcheval base entier Identifiant stable du cheval. A preferer au nom : deux chevaux peuvent porter le meme nom.
name base texte Nom du cheval.
Sexe base texte H/F/M Sexe du cheval.
age base entier annees Age du cheval le jour de la course.
jockey_driver base texte Jockey au plat et a l'obstacle, driver au trot.
trainer base texte Entraineur.
idjockey base entier Identifiant stable du jockey ou driver.
identraineur base entier Identifiant stable de l'entraineur.
Cote base decimal pour 1 euro LA COTE DE DEPART, pas celle du matin. Verification sur la base : la somme des inverses des cotes d'une course a une mediane de 1,189, soit un prelevement de 18,9 %, ce qui est la signature d'un pool ferme au depart ; et sur 27 198 gagnants ayant aussi un rapport, l'ecart median entre les deux est nul. Vide pour la journee en cours (valeur non rafraichie) et pour la toute derniere journee de la base (la cote de depart arrive au rafraichissement du lendemain).
Cote_BZH base decimal pour 1 euro Cote estimee par turf.bzh a partir des chances du cheval. Ce n'est pas une cote de marche.
Note_IA base texte Note de synthese, forme courte.
Note_IA_Decimale base decimal sur 20 Note de synthese sur vingt.
ELO_Cheval base decimal points Classement ELO du cheval.
ELO_Jockey base decimal points Classement ELO du jockey ou driver.
ELO_Entraineur base decimal points Classement ELO de l'entraineur.
IA_Gagnant base decimal probabilite Chance estimee de gagner. C'est le bouton IA GAGNANT du tableau des partants.
IA_Couple base decimal probabilite Chance estimee de figurer dans les deux premiers. C'est le bouton IA COUPLE du tableau des partants. Servie par l'API depuis le 09/09/2026.
IA_Trio base decimal probabilite Chance estimee de figurer dans les trois premiers. C'est le bouton IA TRIO du tableau des partants. Servie par l'API depuis le 09/09/2026.
IA_Multi base decimal probabilite Chance estimee de figurer dans les quatre premiers. C'est le bouton IA MULTI du tableau des partants. Servie par l'API depuis le 09/09/2026.
IA_Quinte base decimal probabilite Chance estimee de figurer dans les cinq premiers. C'est le bouton IA QUINTE du tableau des partants.
ferrure base texte Ferrure annoncee (D4 deferre des quatre, DA deferre anterieurs, DP deferre posterieurs...).
Musique base texte Musique du cheval, ses dernieres performances.
Rank base texte Rang d'arrivee. Valeurs numeriques 1, 2, 3... pour les chevaux classes. Codes non numeriques REELLEMENT presents en base, avec leurs effectifs mesures au 03/08/2026 : D 31 174, NP 12 405, A 3 209, T 1 474, R 35, N 2. Vide sur 3 144 lignes, quand la source ne publie pas la place (gros pelotons etrangers, ou course pas encore depouillee). Le sens exact de D et de R n'est pas tranche dans nos sources : ne les interpretez pas, traitez-les comme non classe. NP est le seul code qui signale un cheval qui N'A PAS pris le depart : D, A, T, R et N designent des chevaux qui sont partis. Depuis le 12/09/2026 vous n'avez plus a le deduire, le champ partant le dit, et sur une course du jour il tient compte des forfaits de derniere minute releves en direct sur le flux PMU, que la base ne connait pas encore.
supplemente base texte Cheval engage en supplement.
ExFav base texte Etait favori lors d'une sortie precedente.
Poids base decimal kg Poids porte. Sans objet au trot attele.
Valeur base decimal Valeur handicap officielle.
Place_Corde base entier Place a la corde. Sans objet dans les disciplines sans corde.
avis_entraineur base texte Avis publie par l'entraineur, quand il y en a un.
Taux_Victoire base decimal fraction de 0 a 1 Taux de victoire du cheval. CE N'EST PAS UN POURCENTAGE : c'est une fraction entre 0 et 1, et 0,21 se lit 21 %. Le champ etait documente en pourcentage jusqu'au 11/09/2026, a tort. Formule verifiee sur la base entiere ce jour-la : nombre_victoire divise par Courses_courues, exacte sur 266 483 lignes sur 266 483. Les deux termes sortent avec le jeu tout : un taux sans son effectif ne se lit pas.
Taux_Place base decimal fraction de 0 a 1 Taux de place du cheval, LA VICTOIRE COMPRISE, comme le turfiste l'entend. Fraction entre 0 et 1, pas un pourcentage. Formule verifiee sur la base entiere le 11/09/2026 : nombre_victoire plus nombre_place, divise par Courses_courues, exacte sur 265 589 lignes sur 266 483, soit 99,7 %. Attention en consequence : nombre_place ne compte QUE les places hors victoire.
Popularite base decimal Popularite du cheval aupres des parieurs : nombre de turfistes qui l'ont mis a suivre depuis sa derniere course.
prec_popularite base decimal Popularite lors de la sortie precedente.
Evo_Popul base decimal Evolution de la popularite depuis la sortie precedente.
Rapport_SG base decimal euros pour 1 euro Rapport du Simple gagnant. Renseigne uniquement pour le gagnant, et seulement quand la source le publie : c'est la colonne la moins remplie de la base.
Rapport_SP base decimal euros pour 1 euro Rapport du Simple place, pour les chevaux places.
discipline base texte code P plat, A trot attele, M trot monte, H haies, S steeple, C cross.
distance base entier metres Distance de la course.
allocation base entier euros Allocation totale de la course. Sert a mesurer le niveau : un cheval qui passe de 100 000 a 20 000 euros descend de categorie.
heure base texte HH:MM Heure de depart, heure de Paris.
nombre_partants base entier Nombre de partants annonce par la source. C'est le peloton ENGAGE, forfaits compris : sur les 2 741 courses a forfait mesurees de janvier a juin 2026, il vaut le total des lignes 2 345 fois (85,6 %) et le total moins les non partants 275 fois seulement (10,0 %). Ne vous en servez donc pas pour compter les chevaux au depart : depuis le 12/09/2026 la reponse porte nombre_partants_initial et nombre_partants_reel, comptes ligne a ligne, et ce chiffre-ci est servi tel quel sous le nom nombre_partants_declare. Il peut aussi differer du nombre de lignes rendues quand la source ne publie pas tout le peloton : 398 courses sur les 8 530 mesurees (4,7 %).
owner indicateurs texte Proprietaire du cheval. Colonne ajoutee retroactivement le 25/08/2026 (jointure sur l'historique) : elle n'est pas renseignee a 100 %, voyez le taux de remplissage sur /v1/schema avant d'en tirer une conclusion.
breeder indicateurs texte Eleveur, ou naisseur, du cheval. Meme ajout retroactif que le proprietaire, meme reserve sur le taux de remplissage.
idproprio indicateurs entier Identifiant stable du proprietaire, calcule comme idjockey (empreinte du nom). A preferer au nom pour appeler /v1/personnes.
ideleveur indicateurs entier Identifiant stable de l'eleveur, calcule comme idjockey. A preferer au nom pour appeler /v1/personnes.
ELO_Proprio indicateurs decimal points Classement ELO du proprietaire, sur la meme echelle que les autres ELO. Il figurait deja au catalogue de /v1/tops sans etre lisible sur le partant : servi ici depuis le 11/09/2026.
ELO_Eleveur indicateurs decimal points Classement ELO de l'eleveur, sur la meme echelle que les autres ELO. Meme correction que l'ELO proprietaire, meme date.
Sigma_Horse indicateurs decimal Fiabilite de l'estimation ELO du cheval : plus elle est haute, plus le classement repose sur un historique fourni. Le composite ELO_Cheval_fiable de /v1/tops ne retient que les chevaux au-dessus de 60.
IMDC indicateurs decimal points Montee (+) ou descente (-) de categorie depuis la sortie precedente, cinq points par echelon (Course F vers Course E = +5, Course F vers Course D = +10). LISEZ CECI AVANT DE VOUS EN SERVIR : la base stocke 0 aussi bien quand le cheval reste dans sa categorie que quand l'indice n'est pas calculable (maiden, handicap, a reclamer, internationale, course a rating, ou cheval debutant). Sur 202 732 partants mesures, 51,6 % des IMDC a zero ne sont PAS calculables. Croisez avec Classe_Groupe avant de conclure.
Turf_Points indicateurs entier points Points turf du cheval, indicateur de forme du catalogue /v1/tops.
TPch_90 indicateurs entier points Points turf du cheval sur les 90 derniers jours.
Moy_TPch_90 indicateurs entier points Moyenne des points turf du cheval sur 90 jours.
Rang_J indicateurs entier rang Rang du jockey ou driver au classement. Plus le nombre est petit, mieux c'est : /v1/tops trie cet indicateur en ordre croissant.
TPJ_90 indicateurs entier points Points turf du jockey ou driver sur les 90 derniers jours.
Synergie_JCh indicateurs decimal pourcentage Synergie entre le driver et le cheval. Renseignee sur un peu plus de quatre lignes sur dix : lisez le taux sur /v1/schema avant d'en faire un critere eliminatoire.
distanceRecord_sec indicateurs decimal secondes Record chronometrique du cheval ramene a la distance. Plus le chiffre est bas, mieux c'est.
Taux_Incident indicateurs decimal fraction de 0 a 1 Taux d'incident du cheval. Fraction, pas un pourcentage, et arrondie a deux decimales : incident divise par Courses_courues, verifie sur la base le 11/09/2026. Plus le chiffre est bas, mieux c'est. Dix lignes sur 286 850 depassent 1, un cheval pouvant cumuler plusieurs incidents sur une meme course.
nombre_victoire indicateurs entier Nombre de victoires du cheval sur son historique. C'est le numerateur de Taux_Victoire.
nombre_place indicateurs entier Nombre de places du cheval HORS victoires. Ce n'est donc pas a lui seul le numerateur de Taux_Place : celui-ci vaut nombre_victoire plus nombre_place. Verifie sur la base le 11/09/2026.
Gains_Totaux indicateurs entier euros Gains cumules du cheval sur toute sa carriere.
Gains_Course indicateurs entier euros Gains moyens du cheval par course. Formule verifiee sur la base entiere le 11/09/2026 : Gains_Totaux divise par Courses_courues, exacte sur 266 483 lignes sur 266 483. Vous pouvez donc le recalculer vous-meme si vous preferez une autre fenetre.
Courses_courues complement entier Nombre de courses courues par le cheval. C'est le DENOMINATEUR commun de Taux_Victoire, Taux_Place, Taux_Incident et Gains_Course, les quatre formules ayant ete verifiees sur la base entiere le 11/09/2026. Un taux de 0,50 sur deux courses et un taux de 0,50 sur quarante ne disent pas la meme chose : c'est ce champ qui fait la difference.
incident complement entier Nombre d'incidents releves sur l'historique du cheval. C'est le numerateur de Taux_Incident, dont le denominateur est Courses_courues.
Repos complement entier jours Nombre de jours ecoules depuis la sortie precedente.
Classe_Groupe complement texte Classe ou groupe de la course (Classe 2, Classe 3, A reclamer, Internationale...). A lire avec IMDC : c'est elle qui dit si la categorie est comparable d'une sortie a l'autre.
Moy_Alloc complement entier euros Allocation moyenne des dernieres courses du cheval. Se croise avec allocation pour mesurer une montee ou une descente de niveau en valeur, la ou IMDC la mesure en echelons.
Moy_TPch_365 complement entier points Moyenne des points turf du cheval sur 365 jours.
TPJ_365 complement entier points Points turf du jockey ou driver sur 365 jours.
Moy_TPJ_365 complement decimal points Moyenne des points turf du jockey ou driver sur 365 jours.
Moy_TPJ_90 complement decimal points Moyenne des points turf du jockey ou driver sur 90 jours.
PC complement decimal Colonne PC de la base, servie telle quelle. Nous ne publions pas de definition pour celle-ci tant qu'elle n'est pas etablie de source sure : si vous vous en servez, mesurez-la vous-meme avant de lui faire confiance.
ferrure_prec complement texte Ferrure de la sortie precedente (FERRE, DEFERRE_ANTERIEURS, DEFERRE_POSTERIEURS, PROTEGE_ANTERIEURS...). A lire avec changement_ferrure.
changement_ferrure complement texte Oui/Non La ferrure change-t-elle par rapport a la sortie precedente.
changement_driver complement texte Oui/Non Le driver ou jockey change-t-il par rapport a la sortie precedente.
Evo_Distance complement texte Hausse/Baisse/Identique Evolution de la distance par rapport a la sortie precedente.
Evo_Poids complement texte Hausse/Baisse/Identique Evolution du poids porte par rapport a la sortie precedente. Sans objet au trot attele.
Alloc_Precedente complement entier euros Allocation de la derniere course du cheval, en euros. Vide quand aucune sortie precedente n'est connue. Connue avant le depart : elle ne dit rien de la course du jour. A lire avec allocation (celle du jour) et Rapport_Alloc.
Place_Precedente complement texte Place obtenue par le cheval a sa derniere course : un rang (1, 2, 3...) ou une lettre, D pour disqualifie, A ou T pour un arret ou une chute. Vide quand aucune sortie precedente n'est connue. C'est la place de la course d'avant, jamais celle du jour.
Rapport_Alloc complement decimal ratio Allocation du jour divisee par Alloc_Precedente : sous 0,90 le cheval descend de categorie, au-dessus de 1,10 il monte. Verifie sur 16 876 lignes de septembre 2026 : c'est exactement allocation / Alloc_Precedente. Vide quand Alloc_Precedente est vide.
delta_poids complement decimal kg Ecart de poids depuis la derniere sortie : poids du jour moins celui de la derniere course non NP du cheval, en kilos, arrondi a 0,1 (negatif : il porte moins). Toutes disciplines confondues pour la sortie precedente. Galop seulement. Null si l'une des deux valeurs manque. Calcule par turf.bzh, connu avant le depart.
delta_valeur complement decimal points Ecart de valeur de handicap depuis la derniere sortie, arrondi a 0,1 (positif : le handicapeur juge le cheval meilleur). Calcule seulement si la derniere course est dans la meme discipline (P, H, S ou C) ; null sinon, et hors handicap. Galop seulement. Calcule par turf.bzh, connu avant le depart.
poids_prec complement decimal kg Poids porte a la derniere sortie non NP du cheval, en kilos. Galop seulement. Null si inconnu. Calcule par turf.bzh.
valeur_prec complement decimal points Valeur de handicap de la derniere sortie non NP, publiee seulement si cette course est dans la meme discipline que celle du jour. Galop seulement. Null sinon. Calcule par turf.bzh.
date_prec complement date YYYY-MM-DD Date (AAAA-MM-JJ) de la derniere sortie non NP du cheval avant la course. Galop seulement. Null si inconnue. Calcule par turf.bzh.
disc_prec complement texte code Discipline de la derniere sortie non NP (P, H, S, C, A ou M). Galop seulement. Null si inconnue. Calcule par turf.bzh.

base sort par défaut sur tous les endpoints de partants. indicateurs et complement demandent champs=indicateurs ou champs=tout.

Endpoints V1

Tout est en GET (sauf /chat). Les dates sont au format YYYY-MM-DD, les courses au format turfiste R1C4. Réponse : {"data": ..., "meta": {...}} en succès, {"error": {...}} sinon.

EndpointCe que ça renvoie
/v1/meÉtat de votre compte : licence, abonnement, clé, crédits IA, usage API du jour, limites.
/v1/statutFraîcheur de la base (healthcheck pour vos scripts) : fresh / stale / empty, nombre de courses chargées.
/v1/programmeLe programme d'une journée, une ligne par course : code_course, hippodrome, discipline, heure, distance en mètres, allocation en euros, nombre_partants, is_quinte, is_completed et le lien vers la page de la course. C'est l'adresse à utiliser pour connaître les distances de la journée. Paramètre date : AAAA-MM-JJ, le jour même par défaut, toute journée présente en base depuis le 21/08/2026. Paramètre filter : all (défaut), upcoming, completed. is_completed vaut vrai quand aucun partant n'attend son classement et qu'au moins un partant est classé : un non partant ne fait plus passer une course courue pour « à venir ». Le champ et le filtre completed disent désormais la même chose, vérifié sur les 28 809 courses de la base, zéro désaccord.
/v1/quinteLa course du Quinté+ d'une journée (source officielle turf.bzh) avec sa fiche complète et ses partants. Paramètre date : le jour même par défaut, ou toute journée couverte par le fichier de référence. Si le Quinté+ d'une journée passée n'y figure pas, la réponse le dit et renvoie vers /v1/programme?date=.
/v1/resultatsArrivées et rapports (SG/SP) des courses terminées d'une journée, aujourd'hui ou n'importe quelle date passée présente en base. L'ordre d'arrivée est complet par défaut, pas tronqué. Paramètres optionnels : date (YYYY-MM-DD, défaut aujourd'hui), hippodrome (recherche partielle, insensible à la casse, ex. enghien), code_course, arrivee (complete par défaut, top5 pour les cinq premiers). Chaque course porte aussi non_partants et non_classes. Ce qui a changé le 02/08/2026.
/v1/courses/{date}/{rc}Fiche d'une course + partants EXACTS : numéro, cheval, driver/jockey, entraîneur, propriétaire (owner), éleveur (breeder), ferrure, musique, Cote BZH, ELO, Note IA, popularité... Fonctionne aussi sur tout l'historique (depuis le 01/02/2025, soit 376 873 partants sur 32 954 courses au 31/10/2026). Paramètre hippodrome : sur dix journées de l'historique, deux réunions différentes portent le même code RxCy ; sans ce paramètre la réponse mélange les deux pelotons et porte alors ambigu: true. Le jour J, la colonne Cote est volontairement absente : utilisez /cotes (temps réel) ou Cote_BZH. Depuis le 11/09/2026, paramètre champs (base | indicateurs | tout, défaut tout) : tous les indicateurs de la base sortent, ELO propriétaire et éleveur compris. Depuis le 12/09/2026, chaque partant porte partant et la course sépare nombre_partants_initial de nombre_partants_reel ; pour une course du jour non encore courue, les forfaits de dernière minute sont relevés en direct sur le flux PMU. Paramètres non_partants (inclus | exclus | seuls) et non_partants_direct.
/v1/courses/{date}/{rc}/dossierNouveau (06/09/2026), CSV et Excel depuis le 07/09. Toute la course en un seul appel. La fiche et ses partants, les indicateurs, le classement, LigneBZH, les écarts, le carnet du Renifleur, ÉcurieBZH, les cotes en direct et leurs mouvements le jour J, les rapports définitifs sur une course passée, et l'historique récent de chaque partant. Remplace 7 appels de course plus un appel par cheval : 21 appels pour un peloton de 14 deviennent un seul, et il ne compte que pour un appel de quota. Paramètres : inclure, historique, hippodrome. Comment s'en servir.
/v1/journees/{date}/partantsNouveau (03/08/2026). Tous les partants d'une journée en un seul appel, avec la cote de départ. C'est l'endpoint à utiliser pour une étude sur plusieurs jours : reconstituer la base course par course demande 32 954 appels, soit trois jours compte tenu du plafond quotidien ; par journée, 638 appels et une dizaine de minutes. Paramètres : discipline (plat, trot attelé, trot monté, obstacle, ou le code d'une lettre), hippodrome, format=csv, depuis le 11/09/2026 champs (base | indicateurs | tout, défaut tout), et depuis le 12/09/2026 non_partants (inclus | exclus | seuls) et non_partants_direct (relevé PMU en direct sur la journée en cours, inactif par défaut). Une liste courses donne le peloton engagé et le peloton réel course par course. Les plages de dates sont refusées : bouclez sur les dates, ou prenez les exports mensuels.
/v1/journees/{date}/stats-premiumNouveau (12/10/2026). Les Stats Premium des favoris de toutes les courses d'une journée, une ligne par course : réussite au gagnant et au placé des 3 favoris, rapports moyens SG et SP, nombre de courses de référence et de favoris observés, niveau et périmètre de la comparaison. Les chiffres du programme du jour. Paramètres : hippodrome, discipline, format=csv. Ce qu'ils mesurent.
/v1/journees/{date}/cotesNouveau (01/11/2026). Les cotes en direct de toutes les courses d'une journée, en un seul appel, pour piloter la journée. Par partant : cote du dernier relevé et son heure (cote_heure, source_cote), cote du matin et évolution, probabilité du marché marge du PMU retirée, Cote BZH et probabilité de notre modèle, écart ecart_value_pct entre la cote du moment et la Cote BZH, note IA. À chaque appel, les courses dont le départ approche sont relues au PMU (paramètre direct, 6 par défaut, 10 au plus). Paramètres : vue (partants | courses | value), courses (toutes | a_venir), filtres ecart_min, cote_bzh_max, cote_min, cote_max, discipline, hippodrome, format=csv. Sur une journée passée, rang_arrivee, cote_finale et rapport_sg permettent de mesurer un seuil. Un écart est une piste de lecture, pas une garantie de gain.
/v1/journees/{date}/d4Nouveau (03/11/2026). Le statut D4 des chevaux de trot de la journée, une ligne par cheval déferré des 4 pieds ou qui ne l'est plus : statut, courses sans D4, rang dans la série, dernier et premier D4, risque de faute et taux de faute attendu pour le profil. Les mêmes calculs que la page Sélection Trot D4. Paramètres : statut (un ou plusieurs de frais, enchaine, habitue, plus_d4, inconnu), format=csv. Le détail.
/v1/stats-premium?du=&au=Nouveau (12/10/2026). Les mêmes lignes sur une période de 31 jours au plus, bornes comprises. Mêmes paramètres. Une période n'est jamais servie incomplète : 503 calcul_en_cours avec Retry-After tant que des journées restent à calculer.
/v1/exports
/v1/exports/{fichier}
Nouveau (03/08/2026). La base entière, en CSV compressé, un fichier par mois, avec taille et empreinte SHA-256. Le mois en cours est marqué partiel. À préférer à toute extraction massive : c'est plus rapide pour vous et cela ne sollicite pas la base.
/v1/schemaNouveau (03/08/2026). Le dictionnaire des champs servis : description, type, unité, jeu champs qui le fait sortir, et surtout taux de remplissage réel. Depuis le 11/09/2026 il décrit 80 champs au lieu de 49. Sur les 84 colonnes de la base, 38 sont renseignées à 100 % et une quinzaine le sont à moins de 65 % : Rapport_SG à 8,5 %, Cote_BZH à 50,3 %, et les colonnes propriétaire/éleveur (ajout rétroactif). Vérifiez-le avant de bâtir une hypothèse sur un champ.
/v1/courses/{date}/{rc}/stats-premiumNouveau (12/10/2026). Les Stats Premium d'une course, celles de l'onglet de sa page, et la liste de ses courses de référence (date, code, hippodrome, distance, lien). En CSV, le tableau rendu est celui des courses de référence.
/v1/courses/{date}/{rc}/cotesCotes PMU Simple Gagnant en direct : cote actuelle, cote de référence du matin, écart depuis le matin, écart sur les dernières minutes, fraîcheur. Le champ masse dit d'où vient la cote : point_de_vente, ou en_ligne quand la masse des points de vente ne publie pas encore de cote exploitable. Cache serveur adaptatif (rafraîchi plus vite à l'approche du départ). Les trois reculs n'ont pas la même durée.
/v1/courses/{date}/{rc}/cotes/mouvementsLes plus gros mouvements de cote sur une fenêtre glissante, écart depuis le matin inclus. Paramètres : fenetre (2-60 min, défaut 5), limit (1-8, défaut 5). Le champ fenetre_reelle_sec donne la durée réellement couverte.
/v1/courses/{date}/{rc}/cotes/historiqueExclusif. Toute la série des cotes de la course, du matin jusqu'au départ : chaque relevé porte son horodatage, la cote du moment et la cote de référence du matin. Le flux PMU ne publie que l'instantané, une courbe ne se reconstruit donc pas après coup : nous l'enregistrons nous-mêmes depuis le 27/07/2026. Ce que couvre la collecte. Depuis début novembre 2026, chaque relevé porte aussi sa masse (point_de_vente ou en_ligne), dernière colonne du CSV.
/v1/courses/{date}/{rc}/arriveeL'arrivée d'une course en quasi temps réel le jour J (source PMU live), rapports inclus quand disponibles.
/v1/courses/{date}/{rc}/rapportsNouveau (05/08/2026). Exclusif. Les rapports définitifs d'une course, les deux masses PMU : tout l'historique collecté depuis le 1er janvier, et depuis le 11/10/2026 le jour même, dès leur publication par le PMU (raisons et Retry-After avant la publication). Chaque ligne porte deux montants : rapport_pour_1_euro, ce que le PMU publie, et rapport_pour_la_mise_de_base, ce qu'un ticket encaisse réellement. Paramètres : masse (en_ligne | point_de_vente), pari, payants, format=csv. Ce que sont les deux masses.
/v1/rapportsNouveau (05/08/2026). Les mêmes rapports sur une journée entière, environ 340 lignes sur 40 courses. Paramètres : date (défaut aujourd'hui), code_course, masse, pari, payants, format=csv.
/v1/performancesNouveau (05/08/2026). Exclusif. Le taux de couverture de nos sélections, mesuré sur ces mêmes rapports : la part des courses où la combinaison gagnante était entièrement contenue dans nos N premiers chevaux. Mesuré sur toutes les courses de la période, jamais sur une sélection des meilleures, et chaque ligne porte son nombre de combinaisons et son coût. Paramètres : fenetre (7j | 14j | 30j | 90j | annee), famille, palmares=1, n, format=csv. Ce que ce taux est, et ce qu'il n'est pas.
/v1/courses/{date}/{rc}/indicateursFeatures calculées par partant : tendances ELO 30/90 j, forme récente, écarts, affinité distance/hippodrome, synergie jockey, signal IMDC, incidents... Depuis le 11/09/2026, les colonnes brutes du partant sortent aussi (ELO propriétaire et éleveur, Sigma, IMDC, palmarès complet) : paramètres champs et format=csv.
/v1/courses/{date}/{rc}/analyseLe classement multi-features de la course (avec explication des rangs) + la LigneBZH du jour quand elle est disponible.
/v1/courses/{date}/{rc}/ecartsÉcarts de victoires/places : ceux du CHEVAL (vrai signal de forme) et ceux du numéro (folklore, présenté comme tel).
/v1/courses/{date}/{rc}/renifleurExclusif. Le carnet de notes du Renifleur : le résumé rédigé après l'arrivée de cette course, le cheval retenu pour la prochaine fois, et surtout les partants qui avaient déjà été repérés à leur dernière sortie. Contenu éditorial, lisez ce que ça vaut.
/v1/courses/{date}/{rc}/ecuriebzhExclusif. ÉcurieBZH : les écuries engagées dans cette course qui sortent de leurs habitudes du jour (trajet inhabituel, plusieurs chevaux dans la même course, première venue sur un hippodrome...). Lecture descriptive du comportement des écuries, jamais un pronostic - le champ avertissement le rappelle. Ce que ça dit, ce que ça ne dit pas.
/v1/ecuriebzhExclusif. Le panorama du jour : toutes les écuries hors habitude, toutes courses confondues. Paramètre ecurie pour n'en interroger qu'une (ex : ?ecurie=S. Roger).
/v1/journees/{date}/ecuriebzhExclusif. Le même panorama pour une journée passée archivée : ce jour-là, ces écuries sortaient de leurs habitudes. 404 si la date n'est pas archivée.
/v1/topsTop N du jour par indicateur (34 indicateurs : ELO_Cheval, Cote_BZH, Note_IA, IMDC, Popularite...). Paramètres : indicateur (requis), n (1-50), discipline, date.
/v1/value-betsLes value bets du jour (composite maison Cote_BZH_fiable). Paramètre n (1-30).
/v1/chevaux?recherche=Recherche d'un cheval par nom -> renvoie les idcheval à utiliser ensuite.
/v1/chevaux/{id}/historiqueLes dernières courses du cheval (max 50 par appel, filtre discipline).
/v1/chevaux/{id}/statsStats agrégées du cheval (periode_jours optionnel).
/v1/chevaux/{id}/lecturesLes lectures expertes actives du jour pour ce cheval.
/v1/chevaux/{id}/renifleurExclusif. Toutes les notes que Le Renifleur a écrites sur ce cheval, de la plus récente à la plus ancienne. Paramètre n_last (1-40, défaut 10).
/v1/personnes?recherche=&type=Recherche d'une personne du turf (moteur sémantique + repli) -> renvoie les ids. type : jockey, driver, entraineur, et depuis le 25/08/2026 proprietaire et eleveur.
/v1/personnes/{type}/{id}/statsStats d'un jockey, entraîneur, propriétaire ou éleveur : periode_jours, discipline, hippodrome (jockey uniquement). Pour propriétaire et éleveur : partants, chevaux distincts, victoires, placés, taux V/P, ROI placé, répartition par discipline. type = jockey | entraineur | proprietaire | eleveur.
/v1/methodesLa liste de vos méthodes BZH enregistrées (créées dans ChatBZH). Pour les appliquer/backtester par API : voir /v1/chat ci-dessous.

Sortie tableur : ajoutez ?format=csv sur /programme, /courses/{date}/{rc}, /cotes, /cotes/historique, /tops, /value-bets, /journees/{date}/partants, /journees/{date}/cotes, /journees/{date}/d4, /schema, /chevaux/{id}/historique, /courses/{date}/{rc}/rapports, /courses/{date}/{rc}/indicateurs, /courses/{date}/{rc}/dossier (qui accepte aussi xlsx), /rapports, /performances, /journees/{date}/stats-premium, /stats-premium et /courses/{date}/{rc}/stats-premium pour recevoir un CSV UTF-8 (Excel FR, séparateur point-virgule). Les 19 adresses en entier, prêtes à copier, avec les dates que chacune accepte.

Cette référence en un fichier : les adresses, leurs paramètres et les 89 champs du dictionnaire se téléchargent en Markdown, en JSON ou en PDF depuis le bloc Télécharger cette documentation, en haut de page. C'est ce qu'il faut donner à un assistant plutôt que l'adresse de cette page.

Spec machine : openapi.json (OpenAPI 3, importable dans Postman, Swagger, ou un générateur de client).

Exemple de réponse (extrait de /v1/courses/{date}/{rc}/cotes) :

{
  "data": {
    "date": "2026-11-05",
    "code_course": "R1C4",
    "source": "pmu",
    "updated_at": "2026-11-05T05:33:26+01:00",
    "partants": [
      { "num": 1, "cheval": "EXEMPLE DU BOIS", "cote": 4.8,
        "cote_ref": 7.5, "evolution_ouverture": -2.7,
        "evolution_ouverture_pct": -36.0, "sens_ouverture": "baisse",
        "cote_5min_ago": 5.2, "evolution_5min": -0.4,
        "comparaison_age_sec": 312,
        "tendance": "-", "tendance_pct": 4.1,
        "favori": false, "fraicheur_label": "< 2 min", "np": false }
    ]
  },
  "meta": { "generated_at": "2026-11-05T05:33:26+01:00", "temps_reel": true }
}

Le dossier d'une course : tout en un appel

L'API est granulaire par choix : une ressource, une adresse. C'est le bon découpage quand on veut une chose précise, et c'est le mauvais quand on veut tout. Reconstituer une course complète demandait 7 appels de course, plus un appel par partant pour son historique : 21 appels pour un peloton de quatorze, plus de mille pour une journée entière.

/dossier fait l'assemblage côté serveur. Il compte pour un appel, comme n'importe quelle autre adresse : pas de pondération, pas de plafond à part.

# tout ce que l'on sait de la course, historique des partants compris
curl -H "Authorization: Bearer VOTRE_CLE" \
  "https://www.turf.bzh/api/v1/courses/2026-11-05/R1C4/dossier"

# juste ce dont vous avez besoin, et sans l'historique (reponse bien plus legere)
curl -H "Authorization: Bearer VOTRE_CLE" \
  "https://www.turf.bzh/api/v1/courses/2026-11-05/R1C4/dossier?inclure=classement,cotes&historique=0"
ParamètreCe qu'il fait
inclureLes sections voulues, séparées par des virgules : indicateurs, classement, ligne_bzh, ecarts, renifleur, ecuriebzh, cotes, mouvements, arrivee, rapports, historique. Omis, le dossier sert ce qui a du sens pour la date : les cotes en direct pour aujourd'hui, les rapports définitifs pour une course passée. Avec inclure=rapports, une course du jour déjà arrivée a aussi ses rapports. tout force les onze.
historiqueNombre de dernières sorties par partant, de 0 à 10 (défaut 3). C'est la seule partie qui coûte une requête par cheval : historique=0 la supprime et allège nettement la réponse.
hippodromeComme sur /v1/courses/{date}/{rc} : dix courses de l'historique partagent leur code RxCy avec une autre réunion le même jour.
formatNouveau (07/09/2026). csv ou xlsx. Omis, vous recevez le JSON complet.

Le même dossier dans un tableur

Un dossier est imbriqué, un tableur ne l'est pas. ?format=csv prend donc les partants comme colonne vertébrale : une ligne par cheval, les champs de course répétés sur chaque ligne (préfixés course_), et chaque section rabattue sur le cheval qu'elle décrit, préfixée par son nom (classement_score, ecarts_ecart_victoire, historique_sorties…). C'est la seule forme qu'un tableur sait filtrer, trier et croiser.

# une ligne par partant, pret pour Power Query
curl -H "Authorization: Bearer VOTRE_CLE" \
  "https://www.turf.bzh/api/v1/courses/2026-11-05/R1C4/dossier?format=csv&historique=0"

# un classeur : un onglet par section, plus Partants et Course
curl -H "Authorization: Bearer VOTRE_CLE" -OJ \
  "https://www.turf.bzh/api/v1/courses/2026-11-05/R1C4/dossier?format=xlsx"

xlsx rend un classeur avec un onglet Partants (la même mise à plat), un onglet par section pour qui veut le détail, et un onglet Course qui rappelle ce que contient le fichier.

Ce que le tableur ne porte pas. L'historique détaillé de chaque cheval devient un simple compteur historique_sorties : quatorze chevaux fois trois sorties, c'est une deuxième table, pas des colonnes. Les valeurs imbriquées (comme top_features) sont jointes en texte lisible. Le JSON reste la forme complète et fait foi ; ces deux sorties sont des commodités.

Aucune section ne peut faire tomber le dossier. Les autres adresses traduisent une donnée manquante en erreur HTTP et s'arrêtent là. Ici, une section indisponible revient en disponible: false avec sa raison, et tout le reste est servi. Seule la course elle-même est bloquante : si elle n'existe pas, il n'y a pas de dossier. meta.appels_economises ne compte que les sections qui ont réellement abouti : c'est un décompte, pas un argument commercial.

{
  "data": {
    "course":   { "date": "2026-11-05", "code_course": "R1C4", "hippodrome": "Vincennes",
                  "nombre_partants_initial": 14, "nombre_partants_reel": 13,
                  "nombre_non_partants": 1, "numeros_non_partants": [ 7 ],
                  "nombre_partants": 14, "partants": [ ... ] },
    "sections": {
      "classement":  { "disponible": true,  "donnees": { ... } },
      "ligne_bzh":   { "disponible": false, "raison": "daily_data_missing",
                       "message": "Pas de lecture LigneBZH pour cette journee." },
      "historique":  { "disponible": true, "sorties_par_cheval": 3,
                       "donnees": { "1347094367": { "disponible": true, "donnees": { ... } } } }
    }
  },
  "meta": {
    "sections_servies": [ "indicateurs", "classement", "..." ],
    "sections_indisponibles": { "ligne_bzh": "daily_data_missing" },
    "appels_economises": 21,
    "cout_quota": 1,
    "cache": "miss", "cache_ttl_s": 15
  }
}

Cache. Une course passée ne bouge plus : son dossier est gardé six heures. Une course du jour porte des cotes vivantes : quinze secondes seulement. meta.cache vous dit lequel des deux cas vous avez touché. Pour une cote à la seconde près, /cotes reste l'adresse.

Trois reculs, trois durées différentes

C'est le point sur lequel on se trompe le plus facilement, alors autant l'écrire noir sur blanc. La réponse contient trois indicateurs de mouvement. Ils ne mesurent pas la même chose et ils ne portent pas sur la même durée.

ChampsSur quelle duréeCe qu'on peut en dire
tendance, tendance_pct Depuis le dernier rapport PMU. Le PMU ne publie pas quand ce rapport a été pris. « La cote est en baisse de 4 % sur le dernier rapport. » N'annoncez aucune durée chiffrée.
cote_5min_ago, evolution_5min, comparaison_age_sec La durée exacte est dans comparaison_age_sec. Le nom du champ dit 5 minutes, la réalité est « au moins 5 minutes » : sur une course peu consultée l'écart peut porter sur bien plus. Lisez comparaison_age_sec avant d'écrire une durée. Les deux champs valent null tant qu'aucune mesure assez ancienne n'existe pour cette course, c'est normal et ce n'est pas une erreur.
cote_ref, evolution_ouverture, evolution_ouverture_pct, sens_ouverture Depuis la cote de référence de la matinée, donc la journée entière. Le recul le plus large et le plus parlant : « passé de 7,5 ce matin à 4,8 maintenant, soit 36 % de moins ».

Comment le lire. Une cote qui baisse veut dire que l'argent rentre sur le cheval, une cote qui monte qu'il est délaissé. C'est une photographie de ce que le public a joué, pas une prévision de l'arrivée. Un cheval massivement joué perd souvent, un cheval délaissé gagne parfois : le mouvement de marché est un élément de contexte parmi d'autres, jamais un signal à suivre seul.

La série complète : /cotes/historique

Les trois reculs ci-dessus sont des écarts entre deux points. Si vous voulez la courbe, c'est /v1/courses/{date}/{rc}/cotes/historique. Elle existe parce que nous l'enregistrons : le flux PMU ne publie que le rapport du moment et celui du matin, jamais la série. Une journée non collectée est perdue définitivement, et c'est pour cela que la collecte tourne depuis le 27/07/2026 alors qu'aucun écran ne l'exploitait encore.

Temps restant avant le départUn relevé toutes les
Plus de 2 h30 minutes
Entre 2 h et 30 min10 minutes
Moins de 30 min5 minutes

Ce que la collecte ne couvre pas, dit franchement. La finesse ne peut jamais dépasser la fréquence à laquelle notre collecteur est appelé, et elle est aujourd'hui de 5 minutes. Les toutes dernières minutes avant le départ sont donc couvertes par un ou deux relevés, pas davantage, même si le tableau annonce un palier plus serré. Aucune course antérieure au 27/07/2026 n'a de série, et un jour sans relevé renvoie un 404 : c'est une absence de donnée, pas une panne.

Un relevé ressemble à ceci. Le champ cotes donne la cote du moment par numéro, cotes_reference celle du matin. Un numéro absent d'un relevé n'avait pas de cote à cet instant : soit c'est un non-partant, soit le marché n'était pas encore ouvert. Le PMU renvoie 0 dans ce second cas ; nous ne le publions pas comme une cote, parce qu'un 0 fausse toute moyenne et fait exploser tout calcul qui divise par la cote. En CSV, la cellule est simplement vide. Un relevé où aucun numéro n'avait de cote n'apparaît pas du tout.

{
  "data": {
    "date": "2026-11-05", "code_course": "R1C4",
    "hippodrome": "Vichy", "heure_depart": "13:47", "nb_mesures": 24,
    "premiere_mesure": "07:12:03", "derniere_mesure": "13:46:10",
    "mesures": [
      { "t": 1785138324, "heure": "12:25:24",
        "cotes":           { "1": 4.8, "2": 12.3, "3": 3.1 },
        "cotes_reference": { "1": 7.5, "2": 11.0, "3": 3.4 } }
    ]
  }
}

En ?format=csv, la même série arrive en forme longue : une ligne par relevé et par partant, sept colonnes qui ne changent jamais (date;code_course;t;heure;num;cote;cote_ref). C'est volontaire. Une colonne par numéro se lirait mieux à l'oeil, mais le nombre de colonnes changerait d'une course à l'autre et votre requête Power Query casserait au premier changement de partants. Là, la même requête marche sur toutes les courses, et vous pouvez empiler plusieurs courses dans un seul tableau puisque chaque ligne porte sa date et son code course.

Les rapports PMU : deux masses, deux montants

Le PMU ne publie pas un rapport par pari, il en publie deux. Le flux nu rend la masse point de vente, celle des bureaux et des cafés. Le même flux appelé avec ?specialisation=INTERNET rend la masse en ligne, dont les types de pari portent le préfixe E_ et les libellés la minuscule (« e-Trio »). Elles ne paient pas la même somme, et l'écart n'est pas un détail : le 20 juillet 2026 sur la R1C5, le couplé placé 15-7 valait 17,80 € au point de vente et 30,10 € en ligne, soit 69 % de plus. Un script qui ignore la distinction compare des nombres qui ne se comparent pas. C'est pourquoi masse est un champ nommé et jamais un préfixe à deviner.

Chaque ligne porte de même deux montants, et pour la même raison. Le PMU publie rapport_pour_1_euro. Ce qu'un parieur encaisse pour un ticket vaut ce nombre multiplié par la mise de base du pari, qui n'est pas la même partout : 1 € au simple gagnant en ligne, 2 € au point de vente, 1,50 € au quarté, 3 € au Multi et au 2 sur 4. Nous servons donc aussi rapport_pour_la_mise_de_base et mise_de_base : personne n'a à refaire la multiplication, ni à se tromper dessus.

ChampCe qu'il vaut
masseen_ligne ou point_de_vente.
pariLe type sans la marque de masse : E_TRIO et TRIO donnent tous deux TRIO. Le filtre ?pari= accepte les deux écritures.
libelleLe libellé du PMU, et il compte : au Multi, « Multi en 4 » et « Multi en 7 » sont deux rapports différents sur la même combinaison.
nb_gagnantsFractionnaire (Flexi, mises partielles). null quand le PMU ne le publie pas : ce n'est pas zéro, qui voudrait dire « personne ».
payefalse quand le rapport vaut zéro. Ces lignes sont servies par défaut : elles prouvent que la formule était proposée et que personne ne l'a trouvée, ce qui n'est pas une absence. ?payants=1 ne garde que celles qui ont payé.
Ce que la collecte ne couvre pas, dit franchement. Elle commence au 1er janvier 2026 : aucune course antérieure n'a de rapport ici. Sur les journées collectées, environ 2 500 courses n'ont aucun rapport définitif publié par le PMU, essentiellement des courses internationales et du PMH : le flux y répond 204, et c'est une réponse, pas une panne. Le meta.collecte de chaque réponse donne les bornes exactes et le nombre de courses couvertes, pour que vous n'ayez pas à les deviner.

Le taux de couverture : ce qu'il est, ce qu'il n'est pas

/v1/performances sert une mesure et une seule : la part des courses où la combinaison gagnante d'un pari était entièrement contenue dans nos N premiers chevaux. Elle est calculée sur toutes les courses de la période où le PMU a publié cette formule, jamais sur une sélection des meilleures, et le dénominateur voyage avec le taux (courses, couvert) pour que vous puissiez le recompter.

Chaque ligne porte tickets, le nombre de combinaisons à jouer, et engage_eur, leur coût. Un taux sans son prix ne veut rien dire : couvrir un trio avec six chevaux, ce sont vingt combinaisons, et ça n'a pas le même sens que de le couvrir avec quatre.

Ce n'est ni un rendement ni une promesse. Ces nombres disent ce qu'une combinaison a payé, pas ce qu'un joueur a gagné : personne n'a joué toutes les courses. Le champ meta.avertissement le porte dans chaque réponse, et il n'est pas décoratif. Turf.bzh ne prend aucun pari et ne promet aucun gain.
GET /api/v1/performances?fenetre=30j&famille=DEUX_SUR_QUATRE&api_key=VOTRE_CLE
GET /api/v1/courses/2026-07-20/R1C5/rapports?masse=en_ligne&payants=1
GET /api/v1/rapports?date=2026-07-20&pari=TRIO&format=csv

Les arrivées et les réunions passées

Les arrivées : ce qui a changé le 2 août 2026

Trois défauts affectaient la restitution des arrivées. Ils ont été corrigés le 2 août 2026 et sont documentés ici parce qu'ils changent ce que reçoivent les scripts déjà écrits.

DéfautPortée mesurée, sur 30 jours et 1 609 courses terminées
Une course dont un seul partant était déclaré non-partant disparaissait entièrement de la réponse555 courses absentes, soit 34,5 %
Les chevaux disqualifiés étaient triés avant le gagnant, avec un rang 0776 arrivées polluées, soit 48,2 %, dont 94 qui ne contenaient aucun cheval classé
L'ordre d'arrivée était tronqué aux cinq premiersune course compte 9,2 chevaux classés en moyenne

Depuis le correctif, 97,6 % des courses terminées sont servies contre 65,5 % auparavant. Les 2,4 % restantes sont des courses dont la source n'a classé qu'une partie du champ : nous préférons ne rien publier plutôt qu'une arrivée à moitié vraie.

Ce que cela change pour vous. L'ordre d'arrivée est désormais complet par défaut. Si votre script s'appuyait sur exactement cinq lignes, ajoutez ?arrivee=top5 et rien ne bouge. Les rangs sont tous chiffrés : les disqualifiés et les arrêtés ne figurent plus dans arrivee, ils sont comptés dans le champ non_classes, à côté de non_partants.

Une réunion passée en un seul appel

/v1/resultats ne servait que la journée en cours. Reconstituer une réunion passée obligeait à appeler /v1/courses/{date}/{rc}/arrivee course par course. Ce n'est plus le cas : l'endpoint lit maintenant l'historique de la base.

GET /api/v1/resultats?date=2026-08-01&hippodrome=enghien

{
  "data": {
    "date": "2026-08-01", "est_aujourdhui": false,
    "hippodrome": "enghien", "nb_courses": 8,
    "results": [
      { "code_course": "R3C3", "hippodrome": "Enghien",
        "discipline": "Trot attelé", "heure": "15h47",
        "nombre_partants": 10, "non_partants": 1, "non_classes": 1,
        "arrivee_complete": true, "rapport_sg": 10.7,
        "arrivee": [
          { "rank": 1, "numero": 4,  "cheval": "MISTHOS CHRISTAL", "rapport_sp": 2.0 },
          { "rank": 2, "numero": 3,  "cheval": "MONTJOIE",         "rapport_sp": 1.4 }
        ] }
    ]
  },
  "meta": { "date": "2026-08-01", "arrivee": "complete", "hippodrome": "enghien" }
}

Le filtre hippodrome est une recherche partielle insensible à la casse : enghien, Enghien et ENGH donnent le même résultat. Une date future renvoie une 422 : cet endpoint sert les arrivées, pas le programme. Une journée absente de la base renvoie nb_courses: 0 et une note qui le dit, jamais une erreur.

Sur le jour en cours, la base n'est alimentée que le lendemain matin pour certaines réunions. Le champ pending_db_update liste alors les courses déjà courues dont l'arrivée n'est pas encore ingérée. Pour une arrivée immédiate le jour J, /v1/courses/{date}/{rc}/arrivee reste la bonne porte : il interroge le flux PMU en direct. Sur une date passée, pending_db_update est toujours vide : ce qui manque manquera.

Le carnet de notes du Renifleur

Après chaque réunion, Le Renifleur relit les commentaires officiels de fin de course, raconte l'épreuve en deux ou trois phrases et retient un cheval qui mérite un coup d'oeil la prochaine fois. C'est du contenu écrit, produit chaque jour, que vous ne trouverez nulle part ailleurs. Deux endpoints l'exposent.

Sur une course

curl -H "X-API-Key: VOTRE_CLE" \
  "https://www.turf.bzh/api/v1/courses/2026-07-25/R1C4/renifleur"
{
  "data": {
    "date": "2026-07-25",
    "code_course": "R1C4",
    "carnet_de_course": {
      "resume": "Comme une pâtissière qui sort son gâteau du four une seconde trop tard, ...",
      "cheval_retenu": { "numero": 8, "nom": "LORIGRE",
                         "raison": "A pointé en retard à l'usine, mais a fait les heures sup'." },
      "url": "/pronostics-pmu-25072026-R1C4.html"
    },
    "deja_reperes": [
      { "num": 3, "cheval": "PASSE COMPOSE", "repere_le": "2026-07-20", "repere_a": "Vichy",
        "portait_le_numero": 9, "jours_depuis": 6, "course_origine": "R2C1",
        "note": "Bloquée aux six cents, elle n'a jamais pu sortir." }
    ],
    "nb_deja_reperes": 1,
    "avertissement": "Note de course, pas un pronostic. ..."
  },
  "meta": { "date": "2026-07-25", "code_course": "R1C4" }
}

carnet_de_course vaut null tant que la course n'a pas été courue et débriefée. deja_reperes est le champ le plus intéressant : ce sont les partants du jour qui étaient passés sous le nez du Renifleur à leur sortie précédente. La note n'apparaît que sur leur course de rentrée, et pendant 60 jours au maximum.

Sur un cheval

curl -H "X-API-Key: VOTRE_CLE" \
  "https://www.turf.bzh/api/v1/chevaux/1347094367/renifleur?n_last=5"

Renvoie nb_notes et la liste des notes. nb_notes: 0 signifie que ce cheval n'a jamais été repéré sur la période conservée, soit 400 jours. Environ un cheval sur seize parmi les partants du jour porte une note active.

Ce que ça vaut, honnêtement

Nous mesurons ce carnet et nous publions le résultat, parce qu'un chiffre non vérifié ne vaut rien. Sur 2 411 rentrées analysées :

Arrivée à la rentréeChevaux repérésTous les partantsAttendu par la cote
Dans les trois premiers35,9 %27,8 %34,5 %
Dans les quatre premiers46,7 %37,0 %44,7 %
Gagnant11,9 %9,3 %12,0 %

Lecture : Le Renifleur repère des chevaux nettement meilleurs que la moyenne du peloton. Mais la troisième colonne dit l'essentiel : à cote égale, l'écart n'est pas significatif. Ces chevaux partent déjà à une cote qui tient compte de leur valeur. Un euro joué au gagnant sur chacun d'eux en rendrait 0,72.

Ne construisez pas un modèle de pari sur ce champ. Utilisez-le pour ce qu'il est : du contenu à afficher, un fil narratif pour vos utilisateurs, une mémoire de course. Chaque réponse porte un champ avertissement qui reprend ces chiffres, de sorte qu'un agent qui consomme l'API ne puisse pas les ignorer.

Droits d'usage

Ces textes sont produits et financés par turf.bzh. Leur réutilisation publique suppose une attribution visible à turf.bzh et un lien vers la course d'origine, fourni dans le champ url. La republication en masse sans attribution n'est pas couverte par la Licence API : voir les conditions.

ÉcurieBZH : ce que fait l'écurie aujourd'hui

Chaque matin, ÉcurieBZH compare la journée de chaque écurie engagée au programme à ses habitudes des 18 derniers mois, et signale ce qui sort de l'ordinaire : un trajet bien plus long que sa zone habituelle (« trajet inhabituel », « plus loin que jamais »), trois chevaux dans la même course, une première venue sur un hippodrome, une monte confiée ou reprise, le retour d'un cheval, une première sortie chez un entraîneur. C'est la même donnée qui alimente le pictogramme et l'onglet ÉcurieBZH du tableau des partants.

Trois façons d'interroger

curl -H "X-API-Key: VOTRE_CLE" \
  "https://www.turf.bzh/api/v1/ecuriebzh"                         # panorama du jour
curl -H "X-API-Key: VOTRE_CLE" \
  "https://www.turf.bzh/api/v1/ecuriebzh?ecurie=S.%20Roger"       # une écurie précise
curl -H "X-API-Key: VOTRE_CLE" \
  "https://www.turf.bzh/api/v1/courses/2026-08-19/R2C1/ecuriebzh" # les écuries d'une course
curl -H "X-API-Key: VOTRE_CLE" \
  "https://www.turf.bzh/api/v1/journees/2026-08-19/ecuriebzh"     # une journée passée

Chaque écurie renvoyée porte ses deplacements du jour (hippodrome, heure) et, pour chacun, ses faits : le libelle du fait, la phrase rédigée qui l'explique, et le kilométrage (km, km_habituel). Une journée passée est servie tant qu'elle est archivée ; une date non archivée répond 404. Le champ archive vaut true sur une journée passée.

Ce que ça dit, ce que ça ne dit pas

C'est une lecture descriptive du comportement des écuries, pas un pronostic. Une écurie qui se déplace loin, qui aligne plusieurs chevaux ou qui découvre un hippodrome ne gagne pas plus souvent pour autant. Le fait raconte quelque chose de la journée de l'écurie - une contrainte, un choix, un déplacement rare - il ne dit rien de la probabilité qu'un de ses chevaux gagne. Chaque réponse porte un champ avertissement qui le rappelle, de sorte qu'un agent qui consomme l'API ne puisse pas le présenter comme une raison de jouer.

Droits d'usage

Donnée produite et financée par turf.bzh. Sa réutilisation publique suppose une attribution visible à turf.bzh. La republication en masse sans attribution n'est pas couverte par la Licence API : voir les conditions.

Exemples copier-coller (Python, Node.js, PHP)

Le même appel dans trois langages : on lit le programme du jour, on affiche le contenu. Explorez la structure renvoyée, puis ciblez ce qui vous intéresse.

Python (requests)

import requests

r = requests.get("https://www.turf.bzh/api/v1/programme",
                 headers={"Authorization": "Bearer tbz_live_VOTRE_CLE"},
                 timeout=15)
j = r.json()
if "error" in j:
    print("Erreur:", j["error"]["message"])
else:
    print(j["data"])   # explorez, puis ciblez j["data"][...] selon vos besoins

Node.js (18+ : fetch intégré)

const KEY = "tbz_live_VOTRE_CLE";

const res = await fetch("https://www.turf.bzh/api/v1/programme", {
  headers: { Authorization: `Bearer ${KEY}` }
});
const j = await res.json();
if (j.error) {
  console.error("Erreur:", j.error.message);
} else {
  console.log(j.data);
}

PHP (cURL)

<?php
$ch = curl_init("https://www.turf.bzh/api/v1/programme");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ["Authorization: Bearer tbz_live_VOTRE_CLE"],
    CURLOPT_TIMEOUT        => 15,
]);
$j = json_decode(curl_exec($ch), true);
curl_close($ch);
if (isset($j["error"])) {
    echo "Erreur: " . $j["error"]["message"];
} else {
    print_r($j["data"]);
}

Postman, n8n et Make

Postman

Deux façons de démarrer : importez notre collection Postman prête (tous les endpoints rangés par thème), ou importez directement la spec openapi.json (Postman > Import > Link ou File).

  1. Importez la collection dans Postman.
  2. Ouvrez l'onglet Variables de la collection, remplacez api_key par votre clé, ajustez date et rc.
  3. L'authentification Bearer est déjà posée au niveau de la collection : lancez n'importe quelle requête.

n8n

Ajoutez un nœud HTTP Request : méthode GET, URL https://www.turf.bzh/api/v1/programme. Dans Authentication, choisissez Generic Credential Type > Header Auth, avec le nom Authorization et la valeur Bearer tbz_live_VOTRE_CLE. Enchaînez ensuite un nœud de traitement ou une notification (Telegram, e-mail...).

Make (ex-Integromat)

Module HTTP > Make a request : URL de l'endpoint, méthode GET, et un en-tête Authorization = Bearer tbz_live_VOTRE_CLE. Activez Parse response pour manipuler le JSON directement.

Depuis un navigateur (fetch cross-origin), l'API accepte les requêtes : elle renvoie les en-têtes CORS nécessaires et répond aux pré-vérifications. Mais ne mettez jamais votre clé en dur dans une page web publique (elle serait visible dans le code) : pour un outil public, faites transiter l'appel par votre propre serveur (relais), qui seul détient la clé.

POSTChatBZH par API - /v1/chat

Posez une question à ChatBZH comme sur le site : l'agent choisit ses outils (programme, cotes live, indicateurs, vos méthodes...), analyse, et renvoie une réponse complète en un seul JSON. Cet endpoint consomme les crédits IA de votre compte (mêmes règles que le site : plafond 300 cr par question, forfait 60 cr pour appliquer une méthode, 100 cr pour un backtest).

curl -X POST "https://www.turf.bzh/api/v1/chat" \
  -H "Authorization: Bearer tbz_live_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{"question": "Applique ma méthode Borda V2 sur R1C4", "mode": "standard", "max_credits": 100}'
  • question (requis) : votre question, comme dans le chat.
  • mode : standard (défaut) ou expert (analyse approfondie, coûte ~3x plus).
  • max_credits (5-300, défaut 300) : votre plafond d'acceptation. Si l'estimation dépasse, refus 402 cost_exceeds_max_credits AVANT tout débit - l'équivalent API de la modale de confirmation du site.
  • Réponse : reponse (texte), tools_utilises, credits.factures, credits.solde_restant, forfait_applique.
  • Patience : un tour peut durer 30 à 180 secondes (l'agent enchaîne plusieurs outils). Prévoyez un timeout client de 240 s. Limite : 10 questions/minute, 1 question à la fois par compte.
  • Chaque appel est autonome (pas de mémoire de conversation) : mettez tout le contexte dans la question.

Erreurs

Toute erreur renvoie un JSON uniforme :

{ "error": { "code": "subscription_required", "message": "...", "status": 403, "doc": "..." } }
HTTPcodeSignification
401missing_key / invalid_keyClé absente, inconnue ou révoquée. Générez / régénérez sur Ma clé API.
403api_lockedLicence API non débloquée sur ce compte.
403subscription_requiredAbonnement inactif. La licence reste acquise : réabonnez-vous et ça repart.
402insufficient_credits(/chat) Solde crédits IA insuffisant. Rechargez sur chatbzh-boutique.php.
402cost_exceeds_max_credits(/chat) L'estimation dépasse votre max_credits : augmentez-le ou simplifiez la question. Rien n'a été débité.
404not_foundRoute ou course inconnue (la réponse liste parfois les courses disponibles). Le champ reason précise le cas. Quand la donnée existera plus tard, comme les rapports du jour, la réponse dit quand revenir : retry_after en secondes, disponible_vers et l'en-tête Retry-After.
405method_not_allowedGET partout, sauf /v1/chat qui attend un POST.
422invalid_paramsParamètre mal formé (date, code course, bornes).
429rate_limitedLimite technique atteinte. Respectez l'en-tête Retry-After. La réponse porte aussi scope (minute, day ou ip) et le bloc quota, qui dit où vous en êtes sur les deux fenêtres.
500 / 503internal_error / upstream_unavailableIncident côté serveur ou source amont : réessayez un peu plus tard. Sur les rapports du jour, quand le flux du PMU ne répond pas, la réponse porte Retry-After.

Limites et bonnes pratiques

  • 60 requêtes/minute et 10 000/jour par clé (limite technique anti-abus, pas un compteur commercial). Réponse 429 + Retry-After au-delà.
  • Ne devinez plus vos appels restants, lisez-les. Chaque réponse porte meta.quota (limite_minute, restant_minute, reset_minute_s, limite_jour, restant_jour, reset_jour_s) et les en-têtes X-RateLimit-*. Un programme qui boucle lit restant_minute et dort reset_minute_s secondes plutôt que d'attendre le 429. Le détail et un exemple.
  • Un troisième plafond, 120 requêtes par minute et par IP, protège contre une clé partagée ou volée. Il n'apparaît pas dans quota : ce n'est pas votre compteur. On le reconnaît à scope: "ip" sur un 429.
  • /v1/chat : 10 questions/minute et une seule question à la fois par compte (le solde de crédits IA borne naturellement le reste).
  • Les cotes live sont rafraîchies côté serveur avec un cache adaptatif : interroger plus d'une fois toutes les 30 s n'apporte rien.
  • Respectez Retry-After sur les 404 et 503 qui le portent. Sur les rapports du jour, il dit quand revenir : interroger avant n'apporte rien et compte dans votre quota.
  • Ne mettez JAMAIS votre clé dans du code visible (page web publique, dépôt GitHub public, capture d'écran). En cas de fuite : régénérez.
  • Usage personnel uniquement : la revente, la redistribution des données ou le partage de clé sont interdits (CGV turf.bzh) et détectés par l'audit.
  • Contrat de version : sur /v1, des champs peuvent être AJOUTÉS, jamais retirés ni renommés. Codez tolérant aux champs inconnus.

Jeu responsable

Les données et indicateurs turf.bzh sont des outils d'aide à la décision pour un loisir. Aucune API, aucun indicateur, aucun modèle ne garantit un gain : les courses comportent une part d'incertitude irréductible. Automatiser ses analyses ne doit jamais devenir automatiser ses mises. Fixez-vous des limites de budget et de temps, et ne jouez que ce que vous pouvez vous permettre de perdre.

Changelog

03/11/2026 Version 1.6.0 : le statut D4 de la journée, et la sortie précédente. Nouvelle adresse /v1/journees/{date}/d4 (paramètres statut et format=csv) : statut de ferrure, courses sans D4, rang dans la série, dernier et premier D4, risque de faute et taux de faute attendu pour le profil, comme la page Sélection Trot D4. Trois champs de plus avec champs=tout sur /v1/courses/{date}/{rc} et /v1/journees/{date}/partants : Alloc_Precedente, Place_Precedente et Rapport_Alloc, en fin de liste des colonnes de la base. Version 1.6.0 de la spécification, qui compte 41 adresses. Aucun champ retiré ni renommé.

02/11/2026 Version 1.5.0 : la masse des cotes, et le motif des rapports complets. Champ masse (point_de_vente ou en_ligne) sur /cotes, sur chaque partant et en dernière colonne du CSV, et sur chaque mesure de /cotes/historique (dernière colonne du CSV) ; masse_cote sur /journees/{date}/cotes. Vide pour les relevés antérieurs. Sur les rapports d'une course du jour, meta.complet passe à true quand les 2 masses sont lues et que le PMU a déclaré l'arrivée complète (meta.complet_motif : statut_pmu), ou, le PMU pouvant laisser une course au statut FIN_COURSE après son arrivée définitive, quand les 2 masses sont lues 90 minutes après le départ (delai). Version 1.5.0 de la spécification, qui compte 40 adresses avec celle du 01/11. Aucun champ retiré ni renommé.

01/11/2026 Cotes de la journée en un appel, pack Excel version 2.1. Nouvelle adresse /v1/journees/{date}/cotes (vues partants, courses, value, filtres ecart_min, cote_bzh_max, cote_min, cote_max), le jour même en direct et toute journée passée depuis le 27/07/2026. Le pack Excel passe à 41 requêtes : J Cotes courses, J Cotes et J Value direct, actualisées toutes les 5 minutes.

20/10/2026 Pack Excel version 2 : tous les points d'accès dans un classeur. 38 requêtes Power Query prêtes à l'emploi, une par point d'accès (journée, période de dates, course, cheval, personne, compte, schéma, exports, méthodes, performances, ChatBZH), un onglet Réglages pour la clé, la date et la période, un mode d'emploi, un catalogue des adresses et un suivi de bankroll. Le classeur attend et reprend de lui-même quand le quota de 60 appels par minute est atteint, et affiche les erreurs de l'API en clair dans l'onglet concerné. Télécharger le pack.

16/10/2026 L'évolution du poids et de la valeur d'un cheval de galop. Six champs calculés sur chaque partant, avec champs=tout (le défaut) sur /v1/courses/{date}/{rc} et /v1/journees/{date}/partants : delta_poids (poids du jour moins celui de la dernière course, en kg, arrondi à 0,1), delta_valeur (valeur de handicap du jour moins celle de la dernière course, seulement si les deux courses sont dans la même discipline), poids_prec, valeur_prec, date_prec et disc_prec. Ce sont les chiffres des colonnes « Évol. poids » et « Évol. valeur » du tableau des partants. Galop seulement : null au trot, pour un cheval sans course précédente connue et, pour la valeur, hors handicap. Connus avant le départ : la dernière course est cherchée strictement avant le jour de la course. Calculés à partir de la base : pour la journée en cours, le poids et la valeur du jour viennent de la base et peuvent différer de ceux que le tableau affiche (il lit le programme du PMU). champs=base et champs=indicateurs sont inchangés. Version 1.4.0 de la spécification. Aucun champ retiré ni renommé. Les exports mensuels gardent leurs colonnes actuelles. Le détail.

12/10/2026 Les Stats Premium des favoris par l'API. Trois adresses nouvelles : /v1/journees/{date}/stats-premium (une journée, une ligne par course), /v1/stats-premium?du=&au= (31 jours au plus) et /v1/courses/{date}/{rc}/stats-premium (une course et ses courses de référence), toutes en CSV. Ce sont les chiffres du programme du jour et de la page de chaque course, sans calcul à part. Demande d'un abonné. Version 1.3.0 de la spécification. Aucun champ retiré ni renommé. Le détail.

11/10/2026 Les rapports définitifs le jour même. /v1/courses/{date}/{rc}/rapports ne servait que notre collecte de nuit : une course de 14 h n'avait ses rapports que le lendemain à 05:00, et la route répondait 404 toute la journée. Elle lit désormais le flux du PMU en direct pour une course du jour, ou de la veille avant 05:00, les deux masses, dès leur publication, quelques minutes après l'arrivée ; les lignes sont celles que la collecte archive ensuite. meta.source dit collecte ou direct. Avant la publication, le 404 dit pourquoi (error.reason) et quand revenir (retry_after, disponible_vers, en-tête Retry-After) ; une réponse servie en direct porte meta.complet. Le dossier en profite avec inclure=rapports. Aucun champ retiré ni renommé, et error.code reste not_found ; mais pour une course du jour, de la veille ou à venir, error.reason vaut désormais la raison précise au lieu de not_found, et le jour même un 503 upstream_unavailable est possible si le PMU ne répond pas. Un programme qui testait reason doit en tenir compte. Le détail.

01/10/2026 La Licence API passe à 49,90 €. La hausse annoncée le 08/09/2026 prend effet : le tarif de lancement de 19,90 € a pris fin le 30/09/2026. La licence reste un paiement unique, valable à vie et réservée aux abonnés actifs. Les licences achetées avant le 01/10/2026 restent acquises, sans rien à payer de plus. Rien ne change côté API : mêmes adresses, mêmes quotas, même contrat v1.

12/09/2026 Les non partants ne sont plus servis comme des partants. Un cheval déclaré forfait figure dans notre base avec Rank = NP, et /v1/courses/{date}/{rc} comme /v1/journees/{date}/partants ne le distinguaient pas : 3 768 lignes et 2 741 courses sur les 8 530 mesurées de janvier à juin 2026. Chaque partant porte désormais partant, statut_partant et, sur un forfait, source_statut. Chaque course sépare nombre_partants_initial (le peloton engagé) de nombre_partants_reel, avec nombre_non_partants et leurs numéros. Ces deux nombres sont comptés ligne à ligne et non déduits de la colonne annoncée, servie sous le nom nombre_partants_declare : sur les courses avec forfait, elle vaut le peloton engagé 85,6 % du temps et le peloton réel 10,0 % seulement. Pour une course du jour non encore courue, les forfaits de dernière minute sont relevés en direct sur le flux PMU, seule source qui les connaisse avant l'arrivée. Nouveaux paramètres non_partants et non_partants_direct. Rien n'est retiré ni renommé.

09/09/2026 Les cinq probabilités IA, au lieu de deux. IA_Couple, IA_Trio et IA_Multi rejoignent IA_Gagnant et IA_Quinte sur /v1/courses/{date}/{rc}, /dossier, /indicateurs, /journees/{date}/partants et /quinte. Elles existaient en base et s'affichaient dans le tableau des partants (boutons IA COUPLE, IA TRIO, IA MULTI), mais l'API n'en exposait aucune. ia_quinte apparaît par ailleurs dans /indicateurs, qui la lisait depuis sa mise en service sans jamais la rendre. Rien n'est retiré ni renommé.

V1 03/07/2026 - Lancement : clé API self-service, licence à vie au tarif de lancement de 19,90 €, endpoints me, statut, programme, quinte, resultats, courses/{date}/{rc} (+ /cotes, /cotes/mouvements, /arrivee).

V2-V4 03/07/2026 - Données premium (indicateurs, analyse + LigneBZH, écarts, tops, value-bets, lectures, chevaux, personnes), format=csv, POST /v1/chat (crédits IA), /v1/methodes, spec openapi.json.

V5 26/07/2026 - Carnet de notes du Renifleur : /v1/courses/{date}/{rc}/renifleur et /v1/chevaux/{id}/renifleur. Contenu éditorial exclusif, avec la mesure de ce qu'il vaut publiée dans la section dédiée.

08/09/2026 Vos appels restants, dans chaque réponse. Toute réponse 200 porte un bloc meta.quota et six en-têtes X-RateLimit-* : limite et restant sur la minute, limite et restant sur la journée, et les secondes avant chaque remise à zéro. Les mêmes valeurs partent sur un 429, où quota rejoint retry_after et scope dans error. Les en-têtes sont exposés par Access-Control-Expose-Headers, donc lisibles par un fetch() de navigateur. Quand le compteur est indisponible, rien n'est émis plutôt qu'un chiffre faux. Aucun coût de quota supplémentaire, aucun champ retiré : le contrat v1 tient. Livré en même temps, le pack IA qui forme Claude et Codex au turf, compris dans la Licence API.

06/09/2026 Le dossier complet d'une course, en un appel. /v1/courses/{date}/{rc}/dossier assemble côté serveur ce qu'il fallait aller chercher en sept appels de course plus un par partant. Paramètres inclure, historique et hippodrome. Il compte pour un appel de quota, sans pondération ni plafond dédié, et les limites existantes (60/minute, 10 000/jour) sont inchangées. Aucune adresse existante n'est modifiée : le contrat v1 tient, des champs sont ajoutés, aucun n'est retiré ni renommé.

25/08/2026 Le propriétaire et l'éleveur entrent dans l'API. Chaque partant porte désormais owner (propriétaire), breeder (éleveur) et leurs identifiants stables idproprio / ideleveur, sur /v1/courses/{date}/{rc} et /v1/chevaux/{id}/historique. La recherche /v1/personnes?type= et les stats /v1/personnes/{type}/{id}/stats acceptent deux types de plus, proprietaire et eleveur, avec les mêmes mesures qu'un jockey ou un entraîneur (partants, chevaux distincts, victoires, placés, taux V/P, ROI placé, répartition par discipline). Ces colonnes sont remplies rétroactivement sur l'historique : elles ne sont pas renseignées à 100 %, et /v1/schema en publie le taux réel. Le contrat v1 ne change pas : des champs sont ajoutés, aucun n'est retiré ni renommé.

22/08/2026 ÉcurieBZH ouvre à l'API : ce que fait chaque écurie aujourd'hui. Trois adresses nouvelles - /v1/ecuriebzh (le panorama du jour, paramètre ecurie pour n'en interroger qu'une), /v1/courses/{date}/{rc}/ecuriebzh (les écuries engagées dans une course) et /v1/journees/{date}/ecuriebzh (une journée passée archivée). La même donnée que l'onglet ÉcurieBZH du tableau des partants : lecture descriptive du comportement des écuries, jamais un pronostic (champ avertissement). La section dédiée.

30/07/2026 Pack Excel refait, et un correctif sur /value-bets. Le classeur contient maintenant les trois requêtes Power Query prêtes à l'emploi : coller la clé et cliquer sur Actualiser tout suffit. Séparément, /v1/value-bets?format=csv renvoyait du JSON au lieu du CSV, ce qui rendait la troisième requête inopérante même correctement saisie ; c'est corrigé. Retéléchargez le pack si vous l'aviez pris avant cette date.

21/08/2026 Le programme s'ouvre aux journées passées. /v1/programme et /v1/quinte ne servaient que le jour même : toute autre date renvoyait une erreur 422 dont le message conseillait /v1/courses/{date}/{rc}, qui sert une course. Celui qui demandait le programme d'hier ne trouvait donc pas la bonne adresse et concluait à une panne. C'est devenu notre premier motif de support sur l'API. Les deux adresses acceptent désormais date=AAAA-MM-JJ. /programme rend les huit mêmes clés que pour le jour même, dans le même ordre, y compris quand aucune course ne sort : un client qui lit déjà la réponse du jour n'a rien à changer. Deux réserves, écrites plutôt que tues : filter=upcoming est refusé sur une journée passée, parce que le filtre du jour est posé sur le WHERE et fausserait le nombre de partants (mesure du 1er août : 17 au lieu de 28 sur Goodwood R6C4) ; et la couverture de /quinte est celle du fichier de référence Quinté+, pas celle de la base. Pour reconstituer une journée entière avec tous les partants et leur cote de départ, /v1/journees/{date}/partants reste plus direct, en un seul appel.

21/08/2026 is_completed disait « à venir » d'une course sur trois, courue depuis des mois. Une course était réputée terminée quand tous ses partants avaient un classement, et un non partant comptait comme un classement manquant. Mesure sur les 28 809 courses de la base : 9 716, soit 33,7 %, ressortaient is_completed: false alors qu'elles étaient courues. Le filtre completed, lui, appliquait déjà la bonne règle : le champ et le filtre se contredisaient sur la même course. La règle est harmonisée partout, y compris pour /v1/programme au jour même et pour l'agent : une course est terminée quand aucun partant n'attend son classement et qu'au moins un partant est classé. Le jour même, le changement est neutre : la base ne reçoit les classements que le lendemain à 06h, donc les deux règles y répondaient déjà la même chose. Corrigé au même endroit : une course entièrement dépouillée mais comptant un non partant était signalée « en attente d'ingestion ».

21/08/2026 Les liens de course rendus par l'API pointaient sur la mauvaise course. Le champ link_url de /v1/programme était construit en minuscules. Or la page de course découpe son adresse avec une expression sensible à la casse et retombe en silence sur R1C1 quand elle échoue, sans jamais renvoyer 404. Mesure du 21/08 : /pronostics-pmu-20082026-r5c3.html répondait 200 en affichant R1C1 à Deauville, au lieu de R5C3 à York. Tous les link_url sont désormais en majuscules et mènent à la bonne course.

05/08/2026 Les rapports PMU s'ouvrent, les deux masses, et la mesure de ce qu'ils ont couvert. Trois adresses nouvelles. /v1/courses/{date}/{rc}/rapports et /v1/rapports servent les rapports definitifs COLLECTES, 306 088 sur 9 038 courses depuis le 1er janvier, point de vente ET en ligne : /arrivee interrogeait le PMU en direct et ne servait donc que la journee en cours, sur une seule masse. Chaque ligne porte les DEUX montants, rapport_pour_1_euro et rapport_pour_la_mise_de_base, parce que la mise de base va de 1 a 3 EUR selon le pari et que servir un seul des deux obligeait chacun a refaire la multiplication. /v1/performances publie le taux de couverture mesure de nos selections, avec son denominateur et le cout de chaque formule. Les trois acceptent format=csv, ce qui porte a douze le nombre d'adresses qui rendent un tableau. Ce que sont les deux masses, et ce que le taux n'est pas.

03/08/2026 L'historique profond s'ouvre : une journee entiere, un dictionnaire, et la base en telechargement. Trois adresses nouvelles. /v1/journees/{date}/partants rend tous les partants d'une journee en un seul appel, cote de depart comprise : reconstituer la base demandait un appel par course, elle en demande un par journee. /v1/schema publie le taux de remplissage reel de chaque champ, qui n'etait ecrit nulle part. /v1/exports sert la base entiere en CSV compresse, un fichier par mois, avec son empreinte SHA-256. Les deux premieres acceptent format=csv, et /v1/chevaux/{id}/historique l'acceptait deja sans que ce soit ecrit : cela porte a neuf le nombre d'adresses qui rendent un tableau. Les bornes debut et fin arrivent aussi sur l'historique d'un cheval et sur les statistiques d'un jockey ou d'un entraineur.

31/07/2026 L'historique des cotes s'ouvre : /v1/courses/{date}/{rc}/cotes/historique. Toute la série d'une course, du matin au départ, là où /cotes ne donnait que l'instantané et /cotes/mouvements qu'une fenêtre de deux heures. La donnée était collectée depuis le 27/07/2026 sans que personne puisse la lire ; c'est réparé. Sortie format=csv en forme longue, ce qui porte à six le nombre d'adresses qui rendent un tableau. Ce que la collecte couvre, et ce qu'elle ne couvre pas.

31/07/2026 Une page pour les six adresses en CSV. Elles étaient annoncées en une phrase, sans jamais être écrites en entier. Elles le sont désormais, prêtes à copier, avec les dates que chacune accepte et le piège du séparateur décimal sous Excel français. Aller à la section.

Docs Documentation orientée cas d'usage : premiers pas en 5 min, galerie, tutoriels (Excel/Power Query, alerte Telegram, bot, ChatBZH), exemples Python/Node/PHP, outils n8n/Make, et modèles téléchargeables (pack Excel, collection Postman, bot).

Le détail des évolutions précédentes

Version 1.2.0 (12/09/2026) : les non partants et le peloton réel

Jusqu'au 12/09/2026, /v1/courses/{date}/{rc} et /v1/journees/{date}/partants servaient les non partants au même titre que les autres chevaux, sans distinction. Un cheval déclaré forfait n'est pas absent de la base : il y figure avec Rank = NP, et aucun des deux endpoints ne filtrait cette valeur. Portée mesurée sur les 8 530 courses courues du 1er janvier au 8 juin 2026 : 3 768 lignes portent ce code, réparties sur 2 741 courses, soit 32,1 % des courses de la période. La production en compte davantage depuis ; la proportion, elle, ne dépend pas de la fenêtre.

Ce que porte désormais chaque partant :

ChampCe que c'est
partantbooléen. false : le cheval ne prend pas le départ. En CSV, 1 ou 0.
statut_partantpartant ou non_partant, pour la lecture humaine.
source_statutsur un forfait seulement : provenance de l'information, base ou pmu_direct.

Les autres codes de Rank ne désignent pas des forfaits. D (disqualifié), A (arrêté), T (tombé), R et N s'appliquent à des chevaux qui ont pris le départ et qui restent donc des partants. NP est le seul code qui signale un forfait. Un filtre écrit côté client sur Rank doit être vérifié sur ce point.

Peloton engagé et peloton réel

Ce sont deux informations distinctes, et la réponse sert les deux. Le peloton engagé est celui sur lequel les conditions de course et les rapports de référence du matin ont été établis ; le peloton réel est celui qui prend le départ.

ChampCe qu'il compte
nombre_partants_initialle peloton engagé, forfaits compris.
nombre_non_partantsles forfaits connus à l'instant de l'appel.
nombre_partants_reelceux qui prennent le départ. initial = réel + non partants, sans exception.
numeros_non_partantsleurs numéros, triés.
nombre_partants_declarele chiffre annoncé par la source, tel quel.

Pourquoi le chiffre annoncé est servi séparément. La colonne nombre_partants de la source ne permet pas de déduire le peloton réel par soustraction. Sur les 2 741 courses à forfait de la période mesurée, elle vaut le peloton engagé 85,6 % du temps, le peloton réel 10,0 %, et ni l'un ni l'autre 4,4 %. nombre_partants_initial et nombre_partants_reel sont donc comptés ligne à ligne sur la même source, et le chiffre annoncé est servi tel quel sous nombre_partants_declare. Quand les deux divergent, note_partants_declare le signale. Sur les 8 530 courses mesurées, l'écart concerne 398 courses, soit 4,7 %.

La journée en cours : relevé PMU en direct

Le code NP entre en base par deux chemins : les forfaits déclarés tôt, présents dès l'ingestion du programme, et les autres, qui n'arrivent qu'avec l'ingestion de l'arrivée, donc après la course. Relevé du 08/06/2026 : 678 lignes, 674 encore sans résultat, et 4 forfaits déjà enregistrés, pour une journée qui en comptera entre 26 et 52. Pendant la journée en cours, la base ne porte donc qu'une partie des forfaits, et jamais ceux de dernière minute.

Le flux PMU les publie dès leur déclaration. /v1/courses/{date}/{rc} l'interroge par défaut pour toute course du jour non encore courue : un seul appel sortant, mutualisé par un cache de 45 secondes. Si le flux ne répond pas, la réponse est servie avec le relevé de la base et non_partants_direct passe à false. Le champ note_non_partants indique dans chaque réponse la provenance du relevé.

Sur /v1/journees/{date}/partants, le relevé direct est en option (non_partants_direct=1) : une journée compte jusqu'à une quarantaine de courses, soit autant d'appels sortants pour une seule requête. Activé, il relève en parallèle les courses non encore courues, dans la limite de 25 appels par requête. Sur une journée passée le paramètre est sans effet : la base porte alors le relevé définitif.

Le paramètre non_partants

ValeurCe que vous recevez
non_partants=inclustous les chevaux, chacun marqué. C'est le défaut : le nombre de lignes rendues est inchangé.
non_partants=exclusseulement ceux qui prennent le départ.
non_partants=seulsseulement les forfaits, pour un rapprochement.

Le filtre ne porte que sur la liste rendue : les 5 nombres ci-dessus et les numéros des forfaits sont identiques dans les 3 cas.

# la fiche d'une course du jour, forfaits de dernière minute compris
curl -H "Authorization: Bearer tbz_live_VOTRE_CLE" \
  "https://www.turf.bzh/api/v1/courses/2026-11-05/R1C1"

# le même peloton, sans les forfaits
curl -H "Authorization: Bearer tbz_live_VOTRE_CLE" \
  "https://www.turf.bzh/api/v1/courses/2026-11-05/R1C1?non_partants=exclus"

# la journée entière, avec le relevé PMU en direct
curl -H "Authorization: Bearer tbz_live_VOTRE_CLE" \
  "https://www.turf.bzh/api/v1/journees/2026-11-05/partants?non_partants_direct=1"

Compatibilité. Le mode par défaut rend les mêmes lignes qu'avant, nombre_partants conserve sa valeur, et les champs ajoutés le sont en plus. Aucun champ n'est retiré ni renommé. Une seule sortie change dans un fichier existant : en CSV, un booléen faux s'écrit désormais 0 et non plus une cellule vide, qui se confondait avec une donnée manquante.

Sur la journée, la liste courses donne les 5 nombres course par course, et nb_courses_ecart_declare le nombre de courses présentant un écart avec le chiffre annoncé.

Tous les indicateurs, et le paramètre champs

Jusqu'au 11/09/2026, 34 colonnes de la base ne sortaient sur aucun endpoint, dont 16 figuraient au catalogue de /v1/tops : le top 10 du jour par ELO_Proprio était disponible, la lecture de ce même ELO sur le partant ne l'était pas. Ces colonnes sortent désormais, l'ELO du propriétaire et celui de l'éleveur compris.

Les 16 indicateurs qui manquaient sur le partant :

ChampCe que c'est
ELO_ProprioELO du propriétaire, même échelle que les autres ELO
ELO_EleveurELO de l'éleveur
Sigma_Horsefiabilité de l'ELO cheval : plus c'est haut, plus l'historique est fourni
IMDCmontée (+) ou descente (-) de catégorie, 5 points par échelon
Synergie_JChsynergie entre le driver et le cheval
Turf_Points, TPch_90, Moy_TPch_90points turf du cheval, brut et moyenné
Rang_J, TPJ_90classement et points turf du driver ou jockey
distanceRecord_secrecord chronométrique ramené à la distance
Taux_Incidenttaux d'incident du cheval
nombre_victoire, nombre_placele palmarès en clair, pas seulement en taux
Gains_Totaux, Gains_Coursegains de carrière, et gains moyens par course

Et 15 de plus qui ne figuraient dans aucun catalogue mais qui servent tous les jours : Courses_courues (l'effectif derrière les taux), incident, Repos (jours depuis la dernière sortie), Classe_Groupe, Moy_Alloc, ferrure_prec, changement_ferrure, changement_driver, Evo_Distance, Evo_Poids, et les points turf sur 365 jours. Depuis le 16/10/2026, six champs calculés s'y ajoutent pour le galop : delta_poids, delta_valeur, poids_prec, valeur_prec, date_prec et disc_prec.

Le paramètre champs

Depuis le 12/09/2026, ces mêmes adresses acceptent aussi non_partants : voyez le volet ci-dessus.

Il s'applique à /v1/courses/{date}/{rc}, /v1/journees/{date}/partants et /v1/courses/{date}/{rc}/indicateurs.

ValeurCe que vous recevez
champs=baseexactement les colonnes d'avant le 11/09/2026, dans le même ordre. Pour un script déjà écrit qui veut un fichier identique à celui d'hier.
champs=indicateursbase plus le propriétaire, l'éleveur et les 16 indicateurs du tableau ci-dessus.
champs=touttout ce que la base contient d'exploitable. C'est le défaut, vous n'avez rien à écrire.
curl -H "Authorization: Bearer tbz_live_VOTRE_CLE" \
  "https://www.turf.bzh/api/v1/courses/2026-09-10/R1C3"

# le jeu d'hier, si votre script lit les colonnes par position
curl -H "Authorization: Bearer tbz_live_VOTRE_CLE" \
  "https://www.turf.bzh/api/v1/journees/2026-09-10/partants?champs=base&format=csv"

Vos scripts ne cassent pas. Les colonnes ajoutées le sont à la fin : en CSV, les positions 1 à 45 ne bougent pas, et aucune colonne n'a été retirée ni renommée. Les exports mensuels suivent la même liste et sont régénérés sur tout l'historique.

Deux corrections de documentation

La vérification de chaque formule sur la base entière, préalable à leur documentation, a mis au jour deux erreurs dans le dictionnaire :

  • Taux_Victoire, Taux_Place et Taux_Incident sont des fractions entre 0 et 1, pas des pourcentages : 0.21 se lit 21 %. /v1/schema les annonçait en « pourcentage » depuis sa mise en service. Si vous affichiez la valeur telle quelle, vous lisiez 0,21 % au lieu de 21 %.
  • Taux_Place compte la victoire comme une place - (nombre_victoire + nombre_place) / Courses_courues, vérifié sur 265 589 lignes sur 266 483 - alors que nombre_place ne compte que les places hors victoire. Les deux ne se lisent donc pas de la même façon.

Deux autres formules sont désormais vérifiées et publiées : Taux_Victoire = nombre_victoire / Courses_courues et Gains_Course = Gains_Totaux / Courses_courues, exactes l'une et l'autre sur 266 483 lignes sur 266 483.

Le dictionnaire complet est sur /v1/schema : il décrit maintenant 80 champs au lieu de 49, chacun avec son type, son unité, son taux de remplissage réel et le jeu champs qui le fait sortir.

Les 5 probabilités IA, au lieu de 2

Le tableau des partants affiche 5 probabilités par cheval, une par forme de pari. L'API n'en servait que 2. Les 3 autres existaient en base depuis l'origine sans avoir jamais été exposées.

ChampBouton du tableau des partantsCe qu'il vaut
IA_GagnantIA GAGNANTprobabilité de finir 1er
IA_CoupleIA COUPLEnouveau : probabilité de finir dans les 2 premiers
IA_TrioIA TRIOnouveau : probabilité de finir dans les 3 premiers
IA_MultiIA MULTInouveau : probabilité de finir dans les 4 premiers
IA_QuinteIA QUINTEprobabilité de finir dans les 5 premiers

Elles sortent sur /v1/courses/{date}/{rc}, son /dossier, /indicateurs, /journees/{date}/partants et /quinte. Sur /indicateurs les noms sont en minuscules, comme le reste de cet endpoint : ia_gagnant, ia_couple, ia_trio, ia_multi, ia_quinte. Ce dernier y apparaît d'ailleurs pour la première fois : l'endpoint le lisait depuis sa mise en service sans jamais le rendre.

Pour classer une journée entière sur l'une d'elles, /v1/tops les acceptait déjà toutes les cinq :

curl -H "Authorization: Bearer tbz_live_VOTRE_CLE" \
  "https://www.turf.bzh/api/v1/tops?indicateur=IA_Couple&n=20"

Le contrat ne bouge pas : ce sont des champs ajoutés, aucun retiré ni renommé. Vos scripts existants continuent de fonctionner sans retouche. Les taux de remplissage de chaque champ sont dans /v1/schema, à consulter avant de bâtir une méthode dessus.

Vos appels restants, dans chaque réponse

Jusqu'ici, on découvrait la limite en la dépassant : le 429 arrivait sans prévenir. Désormais chaque réponse porte l'état du compteur, sans appel supplémentaire et sans coût de quota, à deux endroits au choix.

Dans le corps, sous meta.quota :

{
  "data": { ... },
  "meta": {
    "generated_at": "2026-11-05T05:33:26+01:00",
    "quota": {
      "limite_minute": 60,   "restant_minute": 57,   "reset_minute_s": 34,
      "limite_jour": 10000,  "restant_jour": 9873,   "reset_jour_s": 51234
    }
  }
}

Et en en-têtes HTTP, pour ceux qui préfèrent ne pas lire le corps :

En-têteCe qu'il vaut
X-RateLimit-Limitappels autorisés par minute (60)
X-RateLimit-Remainingappels restants sur la minute en cours, celui-ci déduit
X-RateLimit-Resetnombre de secondes avant remise à zéro de la minute. Ce n'est pas un horodatage
X-RateLimit-Limit-Dayappels autorisés par jour (10 000)
X-RateLimit-Remaining-Dayappels restants sur la journée
X-RateLimit-Reset-Daysecondes avant minuit Europe/Paris
curl -sSD - -o /dev/null \
  -H "Authorization: Bearer VOTRE_CLE" "https://www.turf.bzh/api/v1/statut"

Trois points à connaître. Les mêmes valeurs partent sur un 429, où le bloc quota rejoint retry_after et scope dans error. Les en-têtes sont exposés par Access-Control-Expose-Headers, donc lisibles depuis un fetch() de navigateur. Et si le compteur est momentanément indisponible, il n'y a ni bloc ni en-tête : mieux vaut ne rien afficher qu'afficher un chiffre faux, alors traitez l'absence plutôt que de supposer zéro.

Les limites et les bonnes pratiques associées.

Le pack IA : Claude et Codex formés au turf

Un assistant généraliste sait écrire et calculer, il ne sait rien des courses : il invente une adresse, confond un ordre et une probabilité, et cite un taux sans son échantillon. Le pack IA lui apprend le métier : les 41 adresses et leurs pièges mesurés, le dictionnaire des champs, la grammaire du turf, les mathématiques du pari mutuel, et ce que l'API permet vraiment en composant les appels.

Deux archives, une par outil : un CLAUDE.md avec 17 skills et 10 agents pour Claude Code, un AGENTS.md au standard ouvert avec 17 méthodes et 11 prompts de rôle pour Codex. Rien à installer au-delà de votre assistant : ni Python, ni bash. Vous décompressez, vous ouvrez votre assistant dans le dossier, vous collez une phrase, il fait le reste.

Compris dans la Licence API, sans supplément. Ce qu'il y a dedans et comment le prendre en main.

Le dossier complet d'une course, en un appel

La demande revenait des intégrateurs : reconstituer une course complète imposait 7 appels de course plus un appel par partant, soit 21 pour un peloton de quatorze, et plus de mille pour une journée entière. C'est beaucoup de latence et beaucoup de code pour une seule course.

GET /v1/courses/{date}/{rc}/dossier assemble le tout côté serveur : fiche et partants, indicateurs, classement, LigneBZH, écarts, carnet du Renifleur, ÉcurieBZH, cotes en direct et mouvements le jour J, rapports définitifs sur une course passée, et l'historique récent de chaque partant.

Il ne compte que pour un appel de quota, comme n'importe quelle autre adresse. Ni pondération, ni plafond dédié : les 60 requêtes par minute et les 10 000 par jour ne changent pas.

curl -H "Authorization: Bearer VOTRE_CLE" \
  "https://www.turf.bzh/api/v1/courses/2026-11-05/R1C4/dossier"

# plus leger : seulement ce dont vous avez besoin
curl -H "Authorization: Bearer VOTRE_CLE" \
  "https://www.turf.bzh/api/v1/courses/2026-11-05/R1C4/dossier?inclure=classement,cotes&historique=0"

Une section indisponible ne fait pas tomber le dossier : elle revient en disponible: false avec sa raison, et le reste est servi. Les paramètres et un exemple de réponse.

Propriétaire et éleveur, dans les partants et en statistiques

Deux demandes revenaient souvent : savoir à qui appartient un cheval et qui l'a élevé. Ces deux informations sont désormais servies partout où figurent les partants.

GET /v1/courses/{date}/{rc} et GET /v1/chevaux/{id}/historique portent quatre champs de plus par partant : owner (le propriétaire), breeder (l'éleveur ou naisseur), et leurs identifiants stables idproprio et ideleveur - construits comme l'idjockey, une empreinte du nom, à préférer au nom pour enchaîner les appels.

On peut aussi les chercher et lire leurs statistiques, exactement comme un jockey ou un entraîneur :

# trouver un proprietaire, puis ses stats
curl -H "Authorization: Bearer VOTRE_CLE" \
  "https://www.turf.bzh/api/v1/personnes?recherche=ecurie&type=proprietaire"
curl -H "Authorization: Bearer VOTRE_CLE" \
  "https://www.turf.bzh/api/v1/personnes/proprietaire/{idproprio}/stats?periode_jours=365"

type= accepte maintenant proprietaire et eleveur en plus de jockey, driver et entraineur, sur la recherche comme sur les stats (partants, chevaux distincts, victoires, placés, taux V/P, ROI placé, répartition par discipline).

Une réserve, écrite plutôt que tue. Ces colonnes sont remplies rétroactivement sur l'historique : elles ne sont pas renseignées à 100 %. Un partant sans propriétaire connu rend owner: null. Le taux de remplissage réel est publié, champ par champ, par GET /v1/schema - vérifiez-le avant d'en tirer une conclusion.

Le programme d'une journée passée, enfin

GET /v1/programme ne servait que la journée du jour. Toute autre date renvoyait une erreur 422 dont le message conseillait /v1/courses/{date}/{rc}, qui sert une course, pas une journée : personne ne pouvait deviner la bonne adresse. C'était notre premier motif de support sur l'API.

https://www.turf.bzh/api/v1/programme?date=2026-08-20&api_key=VOTRE_CLE

La réponse porte les huit mêmes clés que pour le jour même, dans le même ordre, y compris quand aucune course ne sort : un client qui lit déjà la réponse du jour n'a rien à changer, il ajoute date=. format=csv fonctionne aussi. /v1/quinte accepte le même paramètre.

Deux réserves, écrites plutôt que tues. filter=upcoming est refusé sur une journée passée : le filtre du jour est posé sur le WHERE et fausserait le nombre de partants. Et la couverture de /v1/quinte est celle du fichier de référence Quinté+, pas celle de la base.

Pour tous les partants d'une journée, cote de départ comprise, en un seul appel, /v1/journees/{date}/partants reste plus direct : une requête par journée au lieu d'une par course.

Les liens de course renvoyés par l'API menaient à la mauvaise course

Le champ link_url était construit avec le code de course en minuscules. Or la page de course découpe son adresse avec une expression sensible à la casse et retombe en silence sur R1C1 quand elle échoue, pendant que la réécriture d'URL, elle, accepte les minuscules : aucune 404 ne signalait quoi que ce soit. Mesure du 21/08 :

/pronostics-pmu-20082026-r5c3.html  →  R1C1 à Deauville   (200, faux)
/pronostics-pmu-20082026-R5C3.html  →  R5C3 à York        (200, juste)

Tous les link_url sont désormais en majuscules et mènent à la course annoncée. Si vous aviez contourné le problème en reconstruisant l'URL vous-même, vous pouvez revenir au champ.

« is_completed » disait « à venir » d'une course sur trois

Une course était réputée terminée quand tous ses partants avaient un classement, et un non partant comptait comme un classement manquant. Sur les 28 809 courses de la base, 9 716, soit 33,7 %, ressortaient is_completed: false alors qu'elles étaient courues depuis des mois. Le filtre completed, lui, appliquait déjà la bonne règle : le champ et le filtre se contredisaient sur la même course.

La règle est désormais unique, partout : une course est terminée quand aucun partant n'attend son classement et qu'au moins un partant est classé. Vérifié sur les 28 809 courses, le champ et le filtre concordent, zéro désaccord.

Le jour même, rien ne change : la base ne reçoit les classements que le lendemain à 06h, donc les deux règles y répondaient déjà la même chose.

Les rapports définitifs d'une course, les deux masses PMU

GET /v1/courses/{date}/{rc}/rapports rend les rapports définitifs d'une course, point de vente et en ligne. Pour une course passée, ce sont ceux de notre collecte de nuit, tout l'historique : meta.collecte en donne les bornes exactes. Depuis le 11/10/2026, le jour même aussi : une course arrivée est servie dès que le PMU publie ses rapports, quelques minutes après l'arrivée. meta.source dit d'où vient la réponse : collecte ou direct. Avant la publication, la réponse dit quand revenir.

Le PMU sert deux masses pour le même pari, et elles ne paient pas pareil. Le 20 juillet sur la R1C5, le couplé placé 15-7 valait 17,80 € au point de vente et 30,10 € en ligne, soit 69 % de plus. La masse est donc un champ nommé, en_ligne ou point_de_vente, jamais un préfixe à deviner.

Chaque ligne porte deux montants, et pour la même raison. Le PMU publie rapport_pour_1_euro. Ce qu'un ticket encaisse vaut ce nombre multiplié par la mise de base du pari, qui n'est pas la même partout : 1 € au simple gagnant en ligne, 2 € au point de vente, 1,50 € au quarté, 3 € au Multi et au 2 sur 4. Nous servons donc aussi rapport_pour_la_mise_de_base : personne n'a à refaire la multiplication, ni à se tromper dessus.

Paramètres : masse, pari, payants, format=csv. Ce que chaque champ signifie.

curl -H "Authorization: Bearer VOTRE_CLE" \
  "https://www.turf.bzh/api/v1/courses/2026-07-20/R1C5/rapports?masse=en_ligne"
Une journée entière de rapports en un seul appel

GET /v1/rapports?date= sert les mêmes lignes sur toute une journée, soit environ 340 rapports sur 40 courses. Mêmes champs, mêmes filtres, plus code_course pour se restreindre à une course.

Le meta porte nb_courses, paris_presents et surtout collecte, qui donne les bornes exactes de ce qui existe : vous n'avez pas à deviner jusqu'où remonte l'historique.

Cette route ne sert que les journées archivées. Pour la journée en cours, et pour la veille avant 05:00, elle répond 404 avec la raison journee_pas_encore_archivee et l'heure de l'archivage ; demandez alors les courses une par une, servies en direct.

Le taux de couverture mesuré de nos sélections

GET /v1/performances?fenetre=30j sert une mesure et une seule : la part des courses où la combinaison gagnante d'un pari était entièrement contenue dans nos N premiers chevaux. Elle est calculée sur toutes les courses de la période où le PMU a publié cette formule, jamais sur une sélection des meilleures, et le dénominateur voyage avec le taux pour que vous puissiez le recompter.

Chaque ligne porte tickets, le nombre de combinaisons à jouer, et engage_eur, leur coût. Un taux sans son prix ne veut rien dire : couvrir un trio avec six chevaux, ce sont vingt combinaisons, et ça n'a pas le même sens que de le couvrir avec quatre.

Fenêtres : 7j, 14j, 30j, 90j, annee. Le palmarès des plus gros rapports couverts n'est joint qu'avec palmares=1 : la mesure pèse moins que la vitrine, et c'est la mesure qu'on sert par défaut.

Ce n'est ni un rendement ni une promesse. Ces nombres disent ce qu'une combinaison a payé, pas ce qu'un joueur a gagné : personne n'a joué toutes les courses. Le champ meta.avertissement le porte dans chaque réponse.

Douze adresses rendent un tableau, contre neuf auparavant

Les trois nouvelles acceptent format=csv : /v1/courses/{date}/{rc}/rapports, /v1/rapports et /v1/performances. Les douze adresses sont écrites en entier, prêtes à copier, dans la section dédiée.

La spécification openapi.json déclare le paramètre format sur les trois, ce qui vaut pour Postman et pour tout client généré depuis la spec.