PMU Data API

PMU API for French Racing, Pre-Match, Live & Results Data

Access the French tote card and the PMU sportsbook through one API — trot attelé and monté, flat, hurdles, steeplechase and cross-country from Vincennes, Chantilly, Cabourg and the rest of the French card, plus pre-match and live odds across 15 sports from Soccer and Ice Hockey to the NFL, UFC and Davis Cup tennis. Market names are English, competitions are French, every market carries a canonical type and every selection a decimal price. REST and WebSocket, one API key.

What a PMU API Response Looks Like

GET /api/pmu/soccer/leagues/Ligue 1/events?limit=3
{
  "total": 12,
  "events": [
    {
      "eventId": "1027973865",
      "away": "Lens",
      "country": "France",
      "home": "AS Monaco",
      "league": "Ligue 1",
      "live": false,
      "markets": [
        {
          "canonicalMarket": "MATCH_RESULT",
          "rawName": "2nd Half",
          "period": "SECOND_HALF",
          "isActive": true,
          "selections": [
            {
              "canonicalOutcome": "HOME",
              "rawName": "1",
              "odds": 2.28,
              "isActive": true,
              "selectionId": "4327432078",
              "moreInfo": {
                "participant": "AS Monaco",
                "participantId": "1000000030"
              }
            },
            {
              "canonicalOutcome": "DRAW",
              "rawName": "X",
              "odds": 2.8,
              "isActive": true,
              "selectionId": "4327432079"
            },
            {
              "canonicalOutcome": "AWAY",
              "rawName": "2",
              "odds": 3.35,
              "isActive": true,
              "selectionId": "4327432080",
              "moreInfo": {
                "participant": "Lens",
                "participantId": "1000000157"
              }
            }
          ],
          "marketId": "1001159826"
        },
        {
          "canonicalMarket": "OVER_UNDER",
          "rawName": "Total Goals - 2nd Half",
          "period": "SECOND_HALF",
          "line": 2.5,
          "isActive": true,
          "selections": [
            {
              "canonicalOutcome": "UNDER",
              "rawName": "Under",
              "odds": 1.18,
              "isActive": true,
              "selectionId": "4327417695",
              "line": 2.5
            },
            {
              "canonicalOutcome": "OVER",
              "rawName": "Over",
              "odds": 3.15,
              "isActive": true,
              "selectionId": "4327417696",
              "line": 2.5
            }
          ],
          "marketId": "1001243173:2.5"
        },
        {
          "canonicalMarket": "PLAYER_TO_SCORE_OR_ASSIST",
          "rawName": "To score or give an assist (Settled using Opta data)",
          "period": "FULL_TIME",
          "line": 1,
          "isActive": true,
          "selections": [
            {
              "canonicalOutcome": "YES",
              "rawName": "Yes",
              "odds": 8.5,
              "isActive": true,
              "selectionId": "4327420748",
              "line": 1,
              "moreInfo": {
                "participant": "Jonathan Gradit",
                "participantId": "1004871441"
              }
            }
          ],
          "marketId": "1003314795:1:Jonathan Gradit",
          "moreInfo": {
            "player": "Jonathan Gradit"
          }
        }
      ],
      "slug": "/sport/football/france/ligue-1/as-monaco---lens/1027973865",
      "sport": "soccer",
      "startTime": "2026-09-18T18:45:00.000Z"
    }
  ]
}

French Racing — Runners, Jockeys & Tote Prices

GET /api/pmu/horse-racing/races?page=1&limit=1
{
  "total": 82,
  "page": 1,
  "limit": 1,
  "totalPages": 82,
  "hasNextPage": true,
  "hasPrevPage": false,
  "races": [
    {
      "id": "18092026-R3-C8",
      "EW": "",
      "active": "true",
      "country": "FR",
      "horses": [
        {
          "id": "18092026-R3-C8-1",
          "number": "1",
          "name": "ICARE FILE",
          "jockey": "G. TUGEND",
          "trainer": "R. MOURICE",
          "active": true,
          "EW": {
            "status": "active",
            "fractional": "",
            "decimal": "50"
          },
          "markets": [
            {
              "rawName": "Simple Gagnant - rapport de référence",
              "decimal": 17
            }
          ]
        },
        {
          "id": "18092026-R3-C8-2",
          "number": "2",
          "name": "JUDOPOCK",
          "jockey": "CH. MARTENS",
          "trainer": "Sté. Ent. Christophe MARTENS",
          "active": true,
          "EW": {
            "status": "active",
            "fractional": "",
            "decimal": "3.5"
          },
          "markets": [
            {
              "rawName": "Simple Gagnant - rapport de référence",
              "decimal": 7
            }
          ]
        },
        {
          "id": "18092026-R3-C8-3",
          "number": "3",
          "name": "JURAE VET",
          "jockey": "J. GUELPA",
          "trainer": "Mlle J. TRIOMPHE",
          "active": true,
          "EW": {
            "status": "active",
            "fractional": "",
            "decimal": "67"
          },
          "markets": [
            {
              "rawName": "Simple Gagnant - rapport de référence",
              "decimal": 39
            }
          ]
        }
      ],
      "isInPlay": "unknown",
      "meetingId": "18092026-R3",
      "name": "15:13 Cavaillon - Grand Prix De Trot De Cavaillon (ATTELE)",
      "region": "France",
      "settled": true,
      "slug": "/turf/18092026/r3/c8/",
      "startDate": "2026-09-18T13:13:00.000Z"
    }
  ]
}

In-Play — Score, Match Clock & Statistics

GET /api/pmu/live-events?sport=soccer&limit=1
{
  "total": 1,
  "page": 1,
  "limit": 1,
  "totalPages": 1,
  "hasNextPage": false,
  "hasPrevPage": false,
  "sport": "soccer",
  "events": [
    {
      "eventId": "1028074535",
      "away": "Hapoel Petach Tikva",
      "country": "Israel",
      "home": "Hapoel Tel Aviv",
      "league": "Ligat Ha'Al",
      "markets": [
        {
          "canonicalMarket": "OVER_UNDER",
          "rawName": "Total Goals",
          "period": "FULL_TIME",
          "line": 3.5,
          "isActive": true,
          "selections": [
            {
              "canonicalOutcome": "OVER",
              "rawName": "Over",
              "odds": 2.07,
              "isActive": true,
              "selectionId": "4339476890",
              "line": 3.5
            },
            {
              "canonicalOutcome": "UNDER",
              "rawName": "Under",
              "odds": 1.53,
              "isActive": true,
              "selectionId": "4339476891",
              "line": 3.5
            }
          ],
          "marketId": "1001159926:3.5"
        },
        {
          "canonicalMarket": "HOME_OVER_UNDER",
          "rawName": "Total Goals by Hapoel Tel Aviv",
          "period": "FULL_TIME",
          "line": 3.5,
          "isActive": true,
          "selections": [
            {
              "canonicalOutcome": "OVER",
              "rawName": "Over",
              "odds": 2.2,
              "isActive": true,
              "selectionId": "4341399688",
              "line": 3.5
            },
            {
              "canonicalOutcome": "UNDER",
              "rawName": "Under",
              "odds": 1.38,
              "isActive": true,
              "selectionId": "4341399689",
              "line": 3.5
            }
          ],
          "marketId": "1001159967:3.5"
        },
        {
          "canonicalMarket": "EUROPEAN_HANDICAP",
          "rawName": "3-Way Handicap",
          "period": "FULL_TIME",
          "line": -3,
          "isActive": true,
          "selections": [
            {
              "canonicalOutcome": "HOME",
              "rawName": "1",
              "odds": 2.28,
              "isActive": true,
              "selectionId": "4341402603",
              "line": -3,
              "moreInfo": {
                "participant": "Hapoel Tel Aviv",
                "participantId": "1000257496"
              }
            },
            {
              "canonicalOutcome": "DRAW",
              "rawName": "X",
              "odds": 1.46,
              "isActive": true,
              "selectionId": "4341402604",
              "line": -3
            },
            {
              "canonicalOutcome": "AWAY",
              "rawName": "2",
              "odds": 0,
              "isActive": false,
              "selectionId": "4341402605",
              "line": -3,
              "moreInfo": {
                "participant": "Hapoel Petach Tikva",
                "participantId": "1000452638"
              }
            }
          ],
          "marketId": "1001224081:-3"
        }
      ],
      "matchClock": {
        "minute": 81,
        "second": 34,
        "period": "2ème mi-temps",
        "periodId": "SECOND_HALF",
        "running": true
      },
      "score": {
        "home": "3",
        "away": "0"
      },
      "settlement": {
        "source": "pmu",
        "state": "in_play",
        "final": false,
        "inPlay": true,
        "lastSeenAt": "2026-09-18T14:25:52.003Z"
      },
      "slug": "/sport/football/israel/ligat-haal/hapoel-tel-aviv---hapoel-petach-tikva/1028074535",
      "sport": "soccer",
      "startTime": "2026-09-18T12:46:00.000Z",
      "statistics": {
        "football": {
          "home": {
            "yellowCards": 2,
            "redCards": 0,
            "corners": 8
          },
          "away": {
            "yellowCards": 3,
            "redCards": 1,
            "corners": 1
          }
        }
      }
    }
  ]
}

Results — 30 Days of In-Play History

GET /api/pmu/results?sport=soccer&limit=20
{
  "total": 3,
  "page": 1,
  "limit": 20,
  "totalPages": 1,
  "hasNextPage": false,
  "hasPrevPage": false,
  "results": [
    {
      "eventId": "1028074535",
      "away": "Hapoel Petach Tikva",
      "country": "Israel",
      "home": "Hapoel Tel Aviv",
      "league": "Ligat Ha'Al",
      "matchClock": {
        "minute": 81,
        "second": 34,
        "period": "2ème mi-temps",
        "periodId": "SECOND_HALF",
        "running": true
      },
      "score": {
        "home": "3",
        "away": "0"
      },
      "settlement": {
        "source": "pmu",
        "state": "in_play",
        "final": false,
        "inPlay": true,
        "lastSeenAt": "2026-09-18T14:25:52.003Z"
      },
      "slug": "/sport/football/israel/ligat-haal/hapoel-tel-aviv---hapoel-petach-tikva/1028074535",
      "sport": "soccer",
      "startTime": "2026-09-18T12:46:00.000Z",
      "statistics": {
        "football": {
          "home": {
            "yellowCards": 2,
            "redCards": 0,
            "corners": 8
          },
          "away": {
            "yellowCards": 3,
            "redCards": 1,
            "corners": 1
          }
        }
      }
    },
    {
      "eventId": "1029063855",
      "away": "Wuhan Three Towns",
      "country": "China",
      "home": "Zhejiang",
      "league": "Super League",
      "matchClock": {
        "minute": 95,
        "second": 15,
        "period": "2ème mi-temps",
        "periodId": "SECOND_HALF",
        "running": true
      },
      "score": {
        "home": "4",
        "away": "1"
      },
      "settlement": {
        "source": "pmu",
        "state": "in_play",
        "final": false,
        "inPlay": true,
        "lastSeenAt": "2026-09-18T13:32:04.379Z"
      },
      "slug": "/sport/football/chine/super-league/zhejiang---wuhan-three-towns/1029063855",
      "sport": "soccer",
      "startTime": "2026-09-18T11:34:00.000Z",
      "statistics": {
        "football": {
          "home": {
            "yellowCards": 2,
            "redCards": 0,
            "corners": 4
          },
          "away": {
            "yellowCards": 2,
            "redCards": 1,
            "corners": 7
          }
        }
      }
    }
  ]
}

Why Use PulseScore for PMU Data

The French Tote Card, Not a Bookmaker's Selection

PMU runs France's pari-mutuel monopoly, so the racing feed is the French card itself — trot attelé and monté, plat, haies, steeplechase and cross-country from Vincennes, Chantilly, Cabourg, La Cépière, Amiens, Vichy, Pornichet, Hyères and Martinique, with the Belgian Mons-Ghlin meeting alongside. Every race carries its meeting ID, off time, discipline and full runner list with jockey or driver, trainer and the rapport de référence for the Simple Gagnant pool. Races priced at tote odds only are flagged onlySP so you never mistake a missing price for a missing runner.

English Market Names on a French Board

Competitions come through in French — Ligue des Champions, Premiership d'Ecosse, Qualif. Championnat International — while every market name is English: Full Time, Total Goals, Double Chance, 3-Way Handicap, Point Spread - Including Overtime. Nothing to translate on the market side, and every market still carries a canonicalMarket with every selection a canonicalOutcome, so a MATCH_RESULT / HOME selection lines up with every other book on PulseScore.

Opta-Settled Player Markets on Soccer

PMU's soccer player board goes past the usual goalscorer set: anytime, first, two-or-more and hat-trick markets sit beside PLAYER_TO_ASSIST and PLAYER_TO_SCORE_OR_ASSIST, both settled on Opta data and both rare on other books — 7,600 assist-related markets across the board on a single snapshot. Each one names the player in markets.moreInfo.player, so you can join props to a squad list without parsing market titles.

Deep NFL, Rugby League and Nordic Hockey Boards

The NFL board averages 250 markets per game and peaks at 555, NRL play-off games run 367, and SHL and Liiga ice hockey sit near 190-225 with separate regular-time, overtime and overtime-plus-shootout boards. Soccer fixtures inside 48 hours average 95 markets and the biggest go past 280, so line-movement tracking has something to track.

Live Boards That Go Deeper Than Prematch

Table tennis and snooker are one- to nine-market boards before the off and 15-20 markets in play; a Davis Cup rubber ran 42 live markets including point-by-point props, deuce markets and per-set correct scores. Live documents carry a score, a match clock with the French period label plus a machine periodId, football statistics or per-set points, and a settlement block on every row. Golf is live-only here — tournament outrights with the whole field in one market.

REST Plus WebSocket Streaming

Poll the REST endpoints for pre-match boards, leagues, racing and results, or subscribe over WebSocket for in-play pushes roughly every second. All 15 sports are subscribable on PRO, MAX and ULTRA plans, and one PulseScore API key works across PMU and every other bookmaker on the platform.

Frequently Asked Questions

What data does the PMU API provide?

PulseScore provides French tote racing plus pre-match and live odds from PMU (pmu.fr), France's ANJ-licensed pari-mutuel operator, across 15 sports, with a 30-day results archive. Racing documents carry the meeting, discipline, off time and every runner with jockey, trainer and price; sports events carry normalized market data with canonical market types and outcomes, decimal odds, the league and country, and the PMU match-page slug.

What makes the PMU racing feed different from other bookmakers' racing?

PMU is the tote that French racing runs on, not a bookmaker taking a position on it, so the feed is the French card end to end — trot attelé and monté, flat, hurdles, steeplechase and cross-country. Runner prices are the rapport de référence for the Simple Gagnant pool rather than fixed odds, and most races are pari-mutuel only (flagged onlySP: true) with no runner prices published, which is normal for a tote and not a gap in the data.

Which sports are covered?

Fifteen: Soccer, Ice Hockey, Basketball, Handball, Tennis, Table Tennis, Volleyball, American Football, Baseball, Rugby Union, Rugby League, Boxing, MMA, Snooker and Australian Rules, plus horse racing. Golf is live-only — it appears on the live feed and in results but has no pre-match board. Call /sport-availability for what is on right now rather than hard-coding the list.

Are the market and selection IDs stable?

marketId is a market-template ID with the line appended, not a per-event key: 1001159858 is the 1X2 market on every soccer fixture and 1001159926:3.5 is Total Goals 3.5 on nearly all of them, with just 61 base IDs covering 42,000 soccer markets. Key a market on eventId plus marketId. selectionId is globally unique — 95,974 distinct across 95,974 soccer selections — so use it when you want one key that never collides.

Why are competition names in French but market names in English?

That is how PMU publishes the board. league, slug and the live match-clock label are French (Ligue des Champions, 2ème mi-temps); market and selection rawName, plus country, are English (Full Time, Total Goals, Spain, Czech Republic). Every market also carries a language-independent canonicalMarket and every selection a canonicalOutcome, and the live clock pairs the French period label with a machine periodId, so you can key on the codes and display the labels.

How do I get only finished matches from the results endpoint?

Filter client-side on settlement.final. PMU's results collection has no settlement.final index, so unlike some books there is no ?final= query parameter here. The archive is a 30-day mirror of the live feed: rows are written from the first in-play sighting and updated in place, so a row can be a mid-match snapshot the feed stopped refreshing. settlement.lastSeenAt tells you when it was last touched.

Is live in-play data available?

Yes — /api/pmu/live-events over HTTP and a WebSocket for streaming. Live documents carry the in-play board, a score with the numbers as strings, a match clock with both the French period label and a machine periodId, football statistics or per-set points, and a settlement block. Live retention is 120 seconds, so documents that stop being refreshed drop out, and a suspended board arrives as an empty markets array.

How do I connect to the PMU WebSocket?

Open wss://api.pulsescore.net/api/pmu/ws/live?key=YOUR_KEY&sport=soccer. The sport parameter is kebab-case (ice-hockey, table-tennis, american-football) and all 15 pre-match sports are valid; golf and racing are not subscribable. The server sends a connected frame with your plan and the valid sport list, then pushes live events roughly every second. WebSocket access requires PRO, MAX or ULTRA.

How often is the data updated?

Live events are pushed roughly every second over WebSocket and expire 120 seconds after their last refresh over HTTP. Pre-match boards, leagues and racing carry a one-hour freshness window, and results are retained for 30 days. Every document carries createdAt and updatedAt timestamps so you can measure freshness yourself.

How do I join PMU events to other bookmakers?

Join on the canonical fields. canonicalMarket, canonicalOutcome and period mean the same thing on every book PulseScore serves, so a soccer MATCH_RESULT / HOME price from PMU lines up with the same market from Betclic, Winamax or Unibet FR. Match the fixture on home, away and startTime — PMU's eventId is its own numeric ID — and use selections' moreInfo.participantId as a stable per-team key inside the PMU feed.

Does PMU have outright or futures markets?

Not on the pre-match board — away is a real opponent on all 15 sports and there are no outright documents. The one outright-shaped board is live golf, where a row carries the tournament as home and a Finishing Position market listing every player in the field.

Can I use this data commercially?

Yes. PulseScore plans cover commercial use — odds comparison sites, trading models, in-play dashboards and internal tooling. Rate limits and WebSocket connection counts scale with the plan, and one API key works across PMU and every other bookmaker on the platform.

Start Using the PMU API Today

Free tier available. No credit card required. Get your API key in 30 seconds.