# BestFightOdds Scraper - MMA Odds, UFC & Boxing Odds, Line Moves (`neverempty/bestfightodds-scraper`) Actor

MMA and UFC odds plus boxing odds from BestFightOdds: one row per fight with every sportsbook's moneyline side by side, the best price and which book has it, implied probability, the opening line, the line range and the movement chart. Optional prop bets (rounds, method).

- **URL**: https://apify.com/neverempty/bestfightodds-scraper.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** Sports, Developer tools, Automation
- **Features:** Limited permissions
- **Stats:** 5 total users, 3 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.60 / 1,000 fight returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## BestFightOdds Scraper - MMA Odds, UFC & Boxing Odds, Line Moves

**MMA and UFC odds, plus boxing odds, from BestFightOdds** - one row per fight with **every sportsbook's moneyline side by side**, the **best price and which book has it**, implied probability, the **opening line**, the line range and the **odds-movement chart**.

Built for line shoppers, betting-model builders, fight-preview writers and anyone tracking how the market moves before fight night. Pick UFC, PFL, ONE or any other promotion by name, or paste the event pages you want. Prop bets (over/under rounds, method of victory, goes the distance) are one switch away.

No login, no API key. Export as JSON, CSV or Excel, or pull the rows into a spreadsheet on a schedule.

### What you get

One row per fight, with:

- **The card**: event name, the event date with its year, the fight time (boxing), the event page address
- **Both fighters**: name, id and their BestFightOdds page
- **Every book's line for each side**: `{"Caesars": -110, "FanDuel": -113, "Polymarket": 108, ...}` in American odds, exactly as the site shows them
- **The best price for each side**: the American odds, decimal odds, implied probability and **every book offering that price**
- **`bestLinesImpliedTotal`**: the two best prices' implied probabilities added up - below 1 means the best lines across books leave no margin
- **The opening line** for each fighter, the **range** (worst and best line seen) and the **movement %** from the open to now
- **The movement chart** as a list of decimal odds, oldest first, ready to plot
- **Prop bets** when you turn them on: each prop with every book's price
- **When the odds last changed** on the site, in UTC

### Example output (one fight, shortened)

```json
{
  "sport": "mma",
  "eventName": "UFC 333",
  "eventDate": "2026-10-24",
  "eventDateText": "October 24th",
  "eventUrl": "https://www.bestfightodds.com/events/ufc-333-4321",
  "matchupId": "44524",
  "fighter1Name": "Alexander Volkanovski",
  "fighter2Name": "Movsar Evloev",
  "bookCount": 7,
  "fighter1Odds": { "Polymarket": -113, "Kalshi": -112, "FanDuel": -113, "Caesars": -110, "BetRivers": -112, "BetWay": -110, "Unibet": -112 },
  "fighter2Odds": { "Polymarket": 108, "Kalshi": -112, "FanDuel": -113, "Caesars": -110, "BetRivers": -112, "BetWay": -110, "Unibet": -112 },
  "fighter1BestOdds": -110,
  "fighter1BestOddsDecimal": 1.9091,
  "fighter1BestImpliedProbability": 0.5238,
  "fighter1BestBooks": ["Caesars", "BetWay"],
  "fighter2BestOdds": 108,
  "fighter2BestOddsDecimal": 2.08,
  "fighter2BestImpliedProbability": 0.4808,
  "fighter2BestBooks": ["Polymarket"],
  "bestLinesImpliedTotal": 1.0046,
  "fighter1OpeningOdds": -120,
  "fighter2OpeningOdds": -110,
  "fighter1RangeWorstOdds": -113,
  "fighter1RangeBestOdds": -110,
  "fighter1MovementPct": 3.6,
  "fighter1TrendDecimal": [1.8333, 1.868, 1.8677, 1.8832, 1.9005, 1.9395, 1.8997],
  "lineHistoryStatus": "ok",
  "props": [{ "label": "Over 1½ rounds", "odds": { "BetRivers": -2000, "BetWay": -1205, "Unibet": -2000 } }],
  "oddsLastChangedUtc": "2026-10-21T09:14:00Z",
  "source": "bestfightodds.com"
}
```

### Input

| Field | What it does |
|---|---|
| **Sport** (`sport`) | `mma` (UFC, PFL, ONE and more - the default), `boxing`, or `both` |
| **Only events whose name contains** (`eventKeywords`) | For example `UFC`, `PFL`, `ONE`. Leave empty for every upcoming event |
| **How many events to read** (`maxEvents`) | The nearest upcoming events with lines first, after the name filter (per site when Sport is `both`) |
| **Event URLs** (`eventUrls`) | Paste event pages - upcoming or past - to read exactly those events |
| **Only fights with these fighters** (`fighterNames`) | Part of a name, for example `Volkanovski` |
| **Include opening line and line movement** (`includeLineMovement`) | On by default. Turn off for the fastest run with the current lines only |
| **Include prop bets** (`includeProps`) | Off by default |
| **Include fights without odds** (`includeFightsWithoutOdds`) | Fights announced before any book has a line are left out unless you turn this on |
| **Maximum fights** (`maxRecords`) | The most fight rows this run returns and charges for |
| **Maximum page requests** (`maxRequests`) | A safety limit on pages opened per run |

The quickest start: **Sport** `mma`, **Only events whose name contains** `UFC`, **How many events to read** `1`. That returns the next UFC card with every book's line and the movement since the open.

### How to use it

- **Line shopping**: sort by `fighter1BestOdds` / `fighter2BestOdds` and read `fighter1BestBooks` to see where to place the bet.
- **Spotting value**: `bestLinesImpliedTotal` below 1.0 means the best lines on the two sides, at different books, add up to less than 100%.
- **Tracking the market**: schedule the run every few hours and compare `fighter1Odds` between runs, or plot `fighter1TrendDecimal`.
- **Fight previews**: the opening line and movement % tell the story of who the money has moved toward.

### Good to know

- **Every row is checked against the event you asked for.** If an event address does not exist, the site sends the request to its home page; you get a free row saying the event was not found - never the home page's fights sold as that event.
- **Nothing is guessed.** A line the site shows as unavailable comes back missing for that book, not as 0. The event date carries a year only when the site shows one that matches the date on the page; otherwise `eventDate` is `null` and `eventDateText` keeps what the site wrote.
- **Line movement comes from each fighter's own page.** `lineHistoryStatus` says whether both pages were read (`ok`), only one (`partial` - the opening line and range for the other side still come through), or why not. The chart is the site's own movement chart for that fighter.
- **Boxing events are named by date** on the site (for example "October 24th Boxing"), so leave the event-name filter empty for boxing.
- **Rows that explain a problem are free**: pages that could not be read, events the site does not have, filters that matched nothing, and fights left out by your maximum each come back as their own row with a `status` and a `note`, and are not charged.
- The site's robots.txt is read at the start of every run and respected.

### Frequently asked

**How do I get UFC odds from every sportsbook in one table?**
Run it with Sport `mma` and the event-name filter `UFC`. Each fight comes back with every book's moneyline for both fighters, the best price and the book that has it.

**Can I see the opening line and how the odds moved?**
Yes - `fighter1OpeningOdds`, `fighter1MovementPct` and `fighter1TrendDecimal` (and the same for fighter 2) are on every row by default.

**Does it cover boxing?**
Yes. Choose `boxing`, or `both` for MMA and boxing in one run.

**Can I get odds for a past event?**
Paste the event page under Event URLs. Past events keep their final lines on the site.

**Can I get prop bets like over/under rounds or method of victory?**
Turn on "Include prop bets". Each fight row then carries a `props` list with every book's price for each prop.

**How do I track odds changes over time?**
Schedule the Actor (for example every 3 hours) with the same input and compare the rows between runs, or use the movement chart on each row.

### Pricing

You pay per fight row returned. Rows that only explain a problem (`status` and `note`) are never charged. Set **Maximum fights** to control the cost of a run.

### Thanks for using this Actor

We build these tools for people who run them every day, and we improve them from what users tell us.

- **Missing a field, or need another filter?** Tell us in the **Issues** tab. If the data is there, we add it.
- **Found a bug or a wrong value?** Post the run ID in the **Issues** tab. Wrong data is the thing we fix first.

If this Actor saved you time, a short review helps other people find it.

# Actor input Schema

## `sport` (type: `string`):

mma = UFC, PFL, ONE, Bellator and the other MMA events on bestfightodds.com (the default). boxing = boxing events from proboxingodds.com, the BestFightOdds boxing site. both = MMA and boxing in one run. Ignored when you paste event URLs below.

## `eventKeywords` (type: `array`):

Words to look for in the event name, for example UFC, PFL or ONE. An event is kept when its name contains any of them (not case sensitive). Leave empty to keep every upcoming event. Applied before "How many events to read". Boxing events are named by their date on the site (for example "October 24th Boxing"), so leave this empty for boxing.

## `maxEvents` (type: `integer`):

Upcoming events are taken in the order the site lists them, the nearest first, after the event-name filter above. Events with no betting line yet are skipped unless Include fights without odds is on. With Sport set to both, this many events are read from each site. Ignored when you paste event URLs below.

## `eventUrls` (type: `array`):

Paste event pages to read exactly those events, past or upcoming, for example https://www.bestfightodds.com/events/ufc-333-4321 or https://www.proboxingodds.com/events/2026-10-24-2473. When this list is filled, Sport and the event filters are not used. An address that is not an event page, or an event the site does not have, gets a free row that says so.

## `fighterNames` (type: `array`):

Part of a fighter's name, for example Volkanovski. A fight is kept when either fighter's name contains any of them (not case sensitive). Leave empty to keep every fight.

## `includeLineMovement` (type: `boolean`):

Adds each fighter's opening line, the line range, the movement % and the points of the odds-movement chart. This reads both fighters' pages, so it takes about two more requests per fight. Turn off for the fastest run with the current lines only.

## `includeProps` (type: `boolean`):

Adds the prop bets listed for each fight (over/under rounds, goes the distance, method of victory, round betting) with every book's price. Off by default because a big fight can have dozens of props.

## `includeFightsWithoutOdds` (type: `boolean`):

Fights that are announced but have no betting line from any book yet are left out by default. Turn on to get them too, with empty odds.

## `maxRecords` (type: `integer`):

The most fight rows this run may return and charge for. When the limit cuts the result, fights are taken evenly from every event, so no event disappears completely, and a free row tells you what was left out.

## `maxRequests` (type: `integer`):

A safety limit on how many pages this run may open. When it is reached the run stops early and a free row says so. The default fits about 140 fights with line movement.

## Actor input object example

```json
{
  "sport": "mma",
  "eventKeywords": [
    "UFC"
  ],
  "maxEvents": 3,
  "includeLineMovement": true,
  "includeProps": false,
  "includeFightsWithoutOdds": false,
  "maxRecords": 100,
  "maxRequests": 300
}
```

# Actor output Schema

## `results` (type: `string`):

One row per fight: the event, its date and fight time, both fighters, every sportsbook's moneyline for each side, the best price and the books offering it, decimal odds and implied probability, the opening line, the line range, the movement % and the odds-movement chart points, and the prop bets when you ask for them. Pages that could not be read, events the site does not have, filters that matched nothing and fights left out by your maximum each come back as their own row and are not charged.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "sport": "mma",
    "eventKeywords": [
        "UFC"
    ],
    "maxEvents": 3,
    "maxRecords": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/bestfightodds-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "sport": "mma",
    "eventKeywords": ["UFC"],
    "maxEvents": 3,
    "maxRecords": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("neverempty/bestfightodds-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "sport": "mma",
  "eventKeywords": [
    "UFC"
  ],
  "maxEvents": 3,
  "maxRecords": 100
}' |
apify call neverempty/bestfightodds-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/bestfightodds-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/j5uiGRUJUAnbRukKz/builds/MXDNvYooGmbFO6iOh/openapi.json
