# UFCStats Fight & Round Stats (`danthedataman/ufcstats-fight-round-stats`) Actor

Get UFCStats fight and round statistics as flat rows with fighter IDs, event dates and locations. Supply event or fight URLs; export CSV or JSON.

- **URL**: https://apify.com/danthedataman/ufcstats-fight-round-stats.md
- **Developed by:** [Eli J](https://apify.com/danthedataman) (community)
- **Categories:** Sports, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.86 / 1,000 results

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## UFCStats Fight Round Stats

Turn the public UFCStats `fight-details` pages you name -- directly, or through
the event cards those fights sit on -- into flat typed dataset rows: one row per
fight, fighter and round, plus an optional fight-level total row per fighter.

Nothing is required and nothing is assumed. A run with no `eventUrls` and no
`fightUrls` fetches nothing and writes no rows rather than picking a card for
you, so it incurs zero result-row charges; the platform's automatic
`apify-actor-start` event may still be charged for that run, so it is not free.

### Input

| Field | Type | Default | What it does |
| --- | --- | --- | --- |
| `eventUrls` | array | None; one card is prefilled in the Console | Event cards to take every fight from. An `event-details` URL in either host form, or a bare 16-hex event id. Up to 50 entries. |
| `fightUrls` | array | None | Individual fights: a `fight-details` URL in either host form, or a bare 16-hex fight id. Up to 500 entries. Each one's event card is read once per event to join on the date and location. |
| `includeTotals` | boolean | `true` | Adds one `scope: "total"` row per fight and fighter carrying that fight's own Totals and Significant Strikes cells. Turn it off to store and pay for per-round rows only. |
| `maxFights` | integer | `50` | The ceiling on distinct fights one run reads, applied to the selection order below. Between 1 and 500. |
| `proxyConfiguration` | object | Apify Proxy off | One proxy URL is resolved once per run and used for every request. If a run is refused, the Apify Proxy datacenter group is the first thing to try. |

Copy event URLs from the UFCStats completed-events listing,
`http://www.ufcstats.com/statistics/events/completed?page=all`.

Targets are selected in `eventUrls` order, each card contributing its fights in
the card's own order, then `fightUrls` in order. The first occurrence of a fight
wins, so naming the same fight twice fetches it once, and a page is fetched only
when it will be processed.

A malformed input is reported by **field name only**: the run fails before any
request, and its `ERRORS` record names the field and the rule that failed, never
the value you sent, because an input field can carry a credential.

### Output

One row per (fight, fighter, round) in the default dataset, `recordType`
`fightRoundStats`. A value the source does not carry is `null`; `0` means the
page printed a zero.

**Totals rows repeat the round rows.** With `includeTotals` on, each fighter
also gets one `scope: "total"` row per fight. If you sum the `scope: "round"`
rows and also keep the `scope: "total"` rows, you double count. Filter to
`scope: "round"` to add rows up, or read a `scope: "total"` row on its own.

**Raw counts only.** Every count is the page's own landed or attempted figure.
The percentages the page prints are not emitted: they are yours to compute from
these columns.

| Column | Type | Unit / form |
| --- | --- | --- |
| `recordType` | string | Literal `fightRoundStats` |
| `rowKey` | string | `<fightId>:<fighterId>:<scope>:<round-or-total>`, unique within a fight |
| `fightId` | string | 16 hex |
| `fightUrl` | string | The absolute URL this run fetched |
| `eventId` | string | 16 hex, read from the fight page's own event anchor |
| `eventName` | string | As printed |
| `eventDate` | string | null | `YYYY-MM-DD`, from the event card |
| `eventLocation` | string | null | As printed, from the event card |
| `fighterId` | string | 16 hex |
| `fighterName` | string | As printed, trailing space stripped |
| `opponentId` | string | 16 hex |
| `opponentName` | string | As printed |
| `result` | string | The page's own symbol (`W`, `L`), never coerced |
| `weightClass` | string | As printed, `Bout` suffix kept |
| `method` | string | As printed |
| `methodDetail` | string | null | The raw joined `Details:` text |
| `endRound` | integer | null | The round the fight ended in |
| `endTime` | string | null | `M:SS` as printed |
| `timeFormat` | string | null | As printed |
| `referee` | string | null | As printed |
| `scope` | string | `round` or `total` |
| `round` | integer | null | 1-based; `null` on a `total` row |
| `knockdowns` | integer | null | Count |
| `sigStrikesLanded` | integer | null | Count |
| `sigStrikesAttempted` | integer | null | Count |
| `totalStrikesLanded` | integer | null | Count |
| `totalStrikesAttempted` | integer | null | Count |
| `takedownsLanded` | integer | null | Count |
| `takedownsAttempted` | integer | null | Count |
| `submissionAttempts` | integer | null | Count |
| `reversals` | integer | null | Count |
| `controlTimeSeconds` | integer | null | Seconds, converted from the page's `M:SS` |
| `headLanded` | integer | null | Count |
| `headAttempted` | integer | null | Count |
| `bodyLanded` | integer | null | Count |
| `bodyAttempted` | integer | null | Count |
| `legLanded` | integer | null | Count |
| `legAttempted` | integer | null | Count |
| `distanceLanded` | integer | null | Count |
| `distanceAttempted` | integer | null | Count |
| `clinchLanded` | integer | null | Count |
| `clinchAttempted` | integer | null | Count |
| `groundLanded` | integer | null | Count |
| `groundAttempted` | integer | null | Count |
| `scrapedAt` | string | ISO-8601 UTC, stamped at fetch |

### Pricing

A run is charged per dataset row plus the platform's automatic
`apify-actor-start` event, once per run. A failure is never a dataset row, so it
is never charged as one, and the run's counters report the rows that actually
landed. An input that names no event and no fight writes no rows, so it incurs
zero result-row charges; the start event may still be charged for that run.

Per-fight row subtotals, before the start event: one fight's rows come to a
subtotal such as 4, 8 or 12 dataset rows. Each of those rows is charged at the
applicable row rate in the current Pricing section of this Actor's Apify Store
listing, and the automatic `apify-actor-start` event is charged separately, once
per run. These subtotals only illustrate how one fight's rows add up; they are
not a price for a card or a run.

### Requests per run

Every request to the source is counted before it is sent, requests go out one
at a time at least 1.5 seconds apart, and nothing is retried.

- **The prefilled card, with nothing failing:** `2 + 1 + 14 = 17` requests --
  2 to start the session with the source, 1 for the card and 1 for each of the
  14 fights it lists.
- **The logical upper bound at the defaults, when a run mixes both arrays:**
  `2 + 50 + 50 + 50 = 152` requests -- 2 for the session, 50 named cards that
  contribute no selected fight, 50 further cards for 50 fights named directly
  whose cards all differ, and those 50 fights at the default `maxFights` of 50.
- **The hard ceiling, whatever the input:** 1,052 requests. A run that reaches
  it stops normally, and its `ERRORS` record carries a `request-ceiling` notice
  naming the targets it left unfetched.

### When a target produces no rows

Failures are never dataset rows. Each one is recorded in the run's `ERRORS`
key-value record as an object with four keys -- `target`, `id`, `errorClass`,
`message` -- and the Actor's key-value store schema lists every class it can
carry, including the two run-level notices (`charge-limit`, `request-ceiling`)
that stop a run normally rather than failing it, and the `no-stats` note for a
fight whose page publishes no statistics tables at all.

### Source and terms

Public UFCStats pages, fetched over plain HTTP. This Actor reads no page behind
a login, writes nothing back to the source, and emits professional athletes' and
officials' competition statistics only.

Every UFCStats page footer links the UFC Terms of Use at https://ufc.com/terms.
Those terms say they "apply to all Ultimate Fighting Championship® web sites"
and to "all associated sites linked to" www.ufc.com by Zuffa, LLC; that you may
not use any "deep-link", "page-scrape", "robot", "spider" or other automatic
device to access the site; that commercial reproduction of the site or its
information without Zuffa, LLC's prior written permission is not permitted; and
that "these files may not be used to construct any kind of database". Whether
ufcstats.com is one of those associated sites is an open question, and this
Actor does not settle it. Disclosing the conflict does not cure it: you are
responsible for your downstream use of the data.

# Actor input Schema

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

Event cards to take every fight from. Each entry is a UFCStats <code>event-details</code> URL in either host form (<code>http://www.ufcstats.com/event-details/2144954270be834d</code> or <code>http://ufcstats.com/event-details/2144954270be834d</code>) or a bare 16-hex event id. Every fight the card lists becomes a target, in the card's own order, up to <b>Max fights</b>. Left empty together with <b>Fight pages</b>, the run fetches nothing, writes no result rows and incurs no result-row charge — it is never back-filled with an event you did not ask for.

## `fightUrls` (type: `array`):

Individual fights. Each entry is a UFCStats <code>fight-details</code> URL in either host form or a bare 16-hex fight id. Each is a target in its own right, and the event card it is listed on is read once per event so that <code>eventDate</code> and <code>eventLocation</code> are joined on rather than invented; a fight already taken from a card in <b>Event cards</b> is not fetched twice.

## `includeTotals` (type: `boolean`):

Add, per fight and fighter, one extra row with <code>scope: "total"</code> and <code>round: null</code> carrying the fight's own Totals and Significant Strikes cells. These rows repeat what the per-round rows already carry, so summing a fight's rows without filtering on <code>scope</code> double-counts it. Turn this off to bill and store per-round rows only.

## `maxFights` (type: `integer`):

The ceiling on how many distinct fights one run reads, applied to the selection order: event cards first, in the order you listed them and in each card's own order, then the fights you named directly. A fight is fetched only when it will be processed, so lowering this cap fetches fewer pages rather than discarding pages already paid for.

## `proxyConfiguration` (type: `object`):

Apify Proxy or your own proxy URLs. One proxy URL is resolved once per run and used for every request. The source is served over plain HTTP and answered this Actor's own requests without a proxy when it was measured; if a run is refused, the Apify Proxy datacenter group is the first thing to try.

## Actor input object example

```json
{
  "eventUrls": [
    "http://www.ufcstats.com/event-details/2144954270be834d"
  ],
  "includeTotals": true,
  "maxFights": 50
}
```

# Actor output Schema

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

No description

## `errors` (type: `string`):

Per-target failures, empty answers and run-level notices, when there are any; never result rows.

# 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 = {
    "eventUrls": [
        "http://www.ufcstats.com/event-details/2144954270be834d"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("danthedataman/ufcstats-fight-round-stats").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 = { "eventUrls": ["http://www.ufcstats.com/event-details/2144954270be834d"] }

# Run the Actor and wait for it to finish
run = client.actor("danthedataman/ufcstats-fight-round-stats").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 '{
  "eventUrls": [
    "http://www.ufcstats.com/event-details/2144954270be834d"
  ]
}' |
apify call danthedataman/ufcstats-fight-round-stats --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,danthedataman/ufcstats-fight-round-stats"
        }
    }
}
```

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/vtD3pUy9uSEdKDBDZ/builds/UzhKqkXX2c7GCwyeW/openapi.json
