{
  "schemaVersion": 2,
  "generatedAt": "2026-08-09T03:02:33.057Z",
  "artifact": {
    "name": "@factorywager/bookmakers",
    "version": "0.4.1",
    "checksum": "a7996c7cdb90fdda5c33e532df30ec07fba077aeb95ae5b699fb61eb9736ce3a",
    "source": "artifact-registry"
  },
  "bookmakers": {
    "pinnacle": {
      "id": "pinnacle",
      "slug": "pinnacle",
      "label": "Pinnacle",
      "urls": {
        "web": "https://www.pinnacle.com",
        "api": "https://api.pinnacle.com/v1",
        "limitsPage": null,
        "termsPage": null
      },
      "fetcher": "rest",
      "lifecycle": [
        "pre_match",
        "live"
      ],
      "sports": [
        "tennis",
        "tabletennis",
        "soccer"
      ],
      "regions": [
        {
          "country": "US"
        },
        {
          "country": "GB"
        },
        {
          "country": "CA"
        }
      ],
      "limits": {
        "minBetUsd": null,
        "maxBetUsd": null,
        "liquidityTier": "high"
      },
      "brandGroup": "Pinnacle",
      "color": "#f59e0b"
    },
    "caesars": {
      "id": "caesars",
      "slug": "caesars",
      "label": "Caesars Sportsbook",
      "urls": {
        "web": "https://sportsbook.caesars.com",
        "api": null,
        "limitsPage": null,
        "termsPage": null
      },
      "fetcher": "webview",
      "lifecycle": [
        "pre_match",
        "live"
      ],
      "sports": [
        "tennis",
        "tabletennis",
        "soccer",
        "basketball",
        "american-football"
      ],
      "regions": [
        {
          "country": "US",
          "stateCode": "NY"
        }
      ],
      "limits": {
        "minBetUsd": null,
        "maxBetUsd": null,
        "liquidityTier": "medium"
      },
      "skin": "Caesars Sportsbook",
      "brandGroup": "Caesars Entertainment",
      "color": "#1a1a2e",
      "webViewConfig": {
        "waitForSelector": "[class*=competitor], [class*=market-button]",
        "timeout": 30000,
        "emptyMarker": "MARKETS NOT AVAILABLE"
      },
      "note": "Event content only renders on state-scoped paths (/us/{state}/bet/{sport}); the /{sport}?id={eventId} link shape returns a shell page with no markets."
    },
    "fanduel": {
      "id": "fanduel",
      "slug": "fanduel",
      "label": "FanDuel",
      "urls": {
        "web": "https://sportsbook.fanduel.com",
        "api": null,
        "limitsPage": null,
        "termsPage": null
      },
      "fetcher": "webview",
      "lifecycle": [
        "pre_match",
        "live"
      ],
      "sports": [
        "tennis",
        "american-football",
        "basketball",
        "hockey",
        "mma",
        "boxing",
        "baseball"
      ],
      "regions": [
        {
          "country": "US",
          "stateCode": "NY"
        }
      ],
      "limits": {
        "minBetUsd": null,
        "maxBetUsd": null,
        "liquidityTier": "medium"
      },
      "skin": "FanDuel Sportsbook",
      "brandGroup": "Flutter Entertainment",
      "color": "#0d9488",
      "webViewConfig": {
        "waitForSelector": "main a[href*='-v-'], main a[href*='-@-']",
        "timeout": 30000
      },
      "note": "Event rows are lazy-rendered below the fold — nothing renders without scroll pumping, and all classes are hashed atomic CSS (unusable as selectors). Extraction (see fanDuelScrapeScript) uses deploy-independent hooks: a JSON-LD schema.org SportsEvent[] blob (names, startDate, canonical url) correlated with match-link rows (a[href*='-v-'], slug ends in the numeric event id) whose li leaf texts carry names and the two moneyline prices in order. Event pages (/{sport}/{league}/{slug}-{id}) take a separate branch (fanDuelEventScrapeScript): JSON-LD single SportsEvent + outcome buttons with aria-labels ending in ' Odds' (\"Rafael Jodar to win, -130 Odds\", \"Spread, X, -9.5, -122 Odds\"); ended matchups render no outcome buttons, so they still fail with SelectorTimeoutError."
    },
    "draftkings": {
      "id": "draftkings",
      "slug": "draftkings",
      "label": "DraftKings",
      "urls": {
        "web": "https://sportsbook.draftkings.com",
        "api": null,
        "limitsPage": null,
        "termsPage": null
      },
      "fetcher": "webview",
      "lifecycle": [
        "pre_match",
        "live"
      ],
      "sports": [
        "tennis",
        "american-football",
        "basketball",
        "baseball",
        "mma",
        "boxing"
      ],
      "regions": [
        {
          "country": "US",
          "stateCode": "NY"
        }
      ],
      "limits": {
        "minBetUsd": null,
        "maxBetUsd": null,
        "liquidityTier": "medium"
      },
      "skin": "DraftKings Sportsbook",
      "brandGroup": "DraftKings",
      "color": "#2563eb",
      "webViewConfig": {
        "waitForSelector": ".cb-market__button-odds",
        "timeout": 30000
      },
      "note": "Sport landing pages (/sports/{sport}) are nav-only; odds live on league and event pages. On event pages participant names appear in the h1 (\"A vs B Odds\"), not in cb-market__label-team. Prices use U+2212 minus, not ASCII '-'."
    },
    "betmgm": {
      "id": "betmgm",
      "slug": "betmgm",
      "label": "BetMGM",
      "urls": {
        "web": "https://sports.betmgm.com",
        "api": null,
        "limitsPage": null,
        "termsPage": null
      },
      "fetcher": "webview",
      "lifecycle": [
        "pre_match",
        "live"
      ],
      "sports": [
        "tennis",
        "american-football",
        "basketball",
        "hockey",
        "mma",
        "boxing",
        "baseball"
      ],
      "regions": [
        {
          "country": "US",
          "stateCode": "NY"
        }
      ],
      "limits": {
        "minBetUsd": null,
        "maxBetUsd": null,
        "liquidityTier": "medium"
      },
      "skin": "BetMGM",
      "brandGroup": "Entain / MGM",
      "color": "#b91c1c",
      "webViewConfig": {
        "waitForSelector": ".option-indicator",
        "timeout": 30000
      },
      "note": "Geo-redirects unpredictably (observed www.la.betmgm.com; also an nj geolocator interstitial that hangs scrapes) — discovery pins www.ny.betmgm.com directly (BETMGM_DEFAULT_STATE, verified live 2026-08-01). Plain HTTP clients get a Cloudflare 403, but WKWebView renders fine. .participant text includes a trailing country code in .participant-country."
    },
    "hard-rock-florida": {
      "id": "hard-rock-florida",
      "slug": "hard-rock-florida",
      "label": "Hard Rock Florida",
      "urls": {
        "web": "https://hardrockfl.sportsbook.hardrock.bet",
        "api": null,
        "limitsPage": null,
        "termsPage": null
      },
      "fetcher": "seat",
      "lifecycle": [
        "pre_match"
      ],
      "sports": [
        "basketball"
      ],
      "regions": [
        {
          "country": "US",
          "stateCode": "FL"
        }
      ],
      "limits": {
        "minBetUsd": null,
        "maxBetUsd": 500,
        "liquidityTier": "medium"
      },
      "skin": "HardRockBet Florida",
      "brandGroup": "Hard Rock International",
      "color": "#db2777",
      "note": "Seat-managed PPH desk (partner ASH/NOV) — credentials in partner_vault, not pipeline-fetched. Domain best-effort."
    },
    "parlay21-com": {
      "id": "parlay21-com",
      "slug": "parlay21-com",
      "label": "Parlay21",
      "urls": {
        "web": "https://parlay21.com",
        "api": null,
        "limitsPage": null,
        "termsPage": null
      },
      "fetcher": "seat",
      "lifecycle": [
        "pre_match"
      ],
      "sports": [
        "basketball"
      ],
      "regions": [
        {
          "country": "US"
        }
      ],
      "limits": {
        "minBetUsd": null,
        "maxBetUsd": 500,
        "liquidityTier": "low"
      },
      "skin": "Parlay21",
      "brandGroup": "Parlay21",
      "color": "#7c3aed",
      "note": "Seat-managed PPH desk (partner SPEN) — credentials in partner_vault, not pipeline-fetched."
    },
    "lonestarwagering-com": {
      "id": "lonestarwagering-com",
      "slug": "lonestarwagering-com",
      "label": "LoneStar Wagering",
      "urls": {
        "web": "https://lonestarwagering.com",
        "api": null,
        "limitsPage": null,
        "termsPage": null
      },
      "fetcher": "seat",
      "lifecycle": [
        "pre_match"
      ],
      "sports": [
        "basketball"
      ],
      "regions": [
        {
          "country": "US"
        }
      ],
      "limits": {
        "minBetUsd": null,
        "maxBetUsd": 500,
        "liquidityTier": "low"
      },
      "skin": "LoneStar Wagering",
      "brandGroup": "LoneStar",
      "color": "#0891b2",
      "note": "Seat-managed PPH desk (partner SPEN) — credentials in partner_vault, not pipeline-fetched."
    },
    "action92-com": {
      "id": "action92-com",
      "slug": "action92-com",
      "label": "Action92",
      "urls": {
        "web": "https://action92.com",
        "api": null,
        "limitsPage": null,
        "termsPage": null
      },
      "fetcher": "seat",
      "lifecycle": [
        "pre_match"
      ],
      "sports": [
        "basketball"
      ],
      "regions": [
        {
          "country": "US"
        }
      ],
      "limits": {
        "minBetUsd": null,
        "maxBetUsd": 500,
        "liquidityTier": "low"
      },
      "skin": "Action92",
      "brandGroup": "Action92",
      "color": "#65a30d",
      "note": "Seat-managed PPH desk (partner SPEN) — credentials in partner_vault, not pipeline-fetched."
    },
    "betvegas23-com": {
      "id": "betvegas23-com",
      "slug": "betvegas23-com",
      "label": "BetVegas23",
      "urls": {
        "web": "https://betvegas23.com",
        "api": null,
        "limitsPage": null,
        "termsPage": null
      },
      "fetcher": "seat",
      "lifecycle": [
        "pre_match"
      ],
      "sports": [
        "basketball"
      ],
      "regions": [
        {
          "country": "US"
        }
      ],
      "limits": {
        "minBetUsd": null,
        "maxBetUsd": 500,
        "liquidityTier": "low"
      },
      "skin": "BetVegas23",
      "brandGroup": "BetVegas23",
      "color": "#ea580c",
      "note": "Seat-managed PPH desk (partner SPEN) — credentials in partner_vault, not pipeline-fetched."
    }
  },
  "audit": {
    "ok": true,
    "issues": []
  },
  "summary": {
    "count": 10,
    "webview": 4,
    "rest": 1,
    "seat": 5,
    "sports": [
      "american-football",
      "baseball",
      "basketball",
      "boxing",
      "hockey",
      "mma",
      "soccer",
      "tabletennis",
      "tennis"
    ]
  }
}
