Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 

Repository files navigation

UFC & MMA Stats Scraper for espn.com

Extract structured data from espn.com — espn.com for MMA fights, fighters, rankings and events at $0.50 per 1,000 rows — the lowest price of any comparable UFC scraper. 42 measured stats per fighter per bout · reach, stance, gym & referee · career defence & closing sportsbook odds.

UFC & MMA Stats Scraper for espn.com on Apify →


🚀 How to use this actor

💚 $5 free Apify credits — every month

No credit card required. No commitment. Cancel anytime.

  1. Click sign up — pick GitHub, Google, or email; takes ~30 seconds
  2. Open this actor — input is pre-filled with a working example
  3. Click Start — export results as JSON, CSV, or Excel

Your $5 monthly platform credit is enough to run this actor right away — and again every month — scraping typically several hundred to several thousand results per run, depending on your input.

Key features

Search with filters — Search by keyword. Filter by 🚻 gender, 🥋 method, 🏁 result, and more.

Multiple input modes — 🥊 fights (per-fighter statlines) or 👤 fighters (profiles + career rates) or 🏆 rankings or 📅 events. Switch modes without re-scraping.

Detail enrichment — Fetch structured metadata for each MMA.

Incremental mode — Only get new or changed MMA records since your last run. Content hash per MMA — no duplicates, no re-processing.

Change classification — Track unchanged, expired, cross-run duplicate detection across runs. Build audit trails of how MMA records evolve over time.

Compact output — Emit core fields only (AI-agent / MCP-friendly). Keeps response size small for LLM workflows.

Result cap — Stop after N MMA records (up to 10.000). Set to 0 for the full catalog.

Export anywhere — Download as JSON, CSV, or Excel. Stream via Apify API, webhooks, or integrations with Make, Zapier, Airbyte, Keboola.

Structured data — Every MMA returns the same schema with consistent field naming. All fields always present — null when unavailable, never omitted.


Use cases

Data pipeline automation Integrate with your ETL pipeline to collect structured MMA records from espn.com on a schedule. Export to CSV, JSON, or directly to your database. Use compact mode to control output size.

Market research Monitor MMA records, track trends, and analyze market dynamics with structured, deduplicated data from espn.com.

Change monitoring Run daily or hourly in incremental mode to capture only new, updated, or expired ${NOUNS}. Perfect for price-tracking, churn analysis, and alerting pipelines.

AI / LLM training data Structured JSON per MMA is ready for RAG pipelines, embeddings, and agent workflows. Compact mode trims tokens for LLM context windows.


Quick start

{
  "query": "ufc",
  "season": "2024",
  "mode": "fights",
  "maxResults": 50,
  "includeDetails": true
}

Input parameters

Parameter Type Default Description
query string — Promotion slug to scrape: "ufc", "pfl", "bellator", and 45 more that ESPN carries. Use a JSON array for several, e.g. ["ufc","pfl"]. This is NOT a free-text search — to find a fighter or an event by name, leave this as the promotion and use Name Search, or paste an ESPN URL into Start URLs.
mode enum "fights" fights = one row per fighter per bout with the full 42-stat line. fighters = roster profiles with career rates and a W-L-D breakdown. rankings = divisional + pound-for-pound boards. events = one row per card.
season string — Scope to one or more seasons, e.g. "2024". Accepts a JSON array like ["2023","2024"]. Applies to fights and events only — a roster and a ranking board are current, not historical, so fighters and rankings mode ignore it and say so in the log. Leave empty for ESPN’s current season.
startUrls array [] Paste espn.com URLs. What each one returns depends on the Record type: in fights mode a fighter page gives that fighter’s whole bout log and a fightcenter page gives the card, while in fighters mode a fighter page gives that one profile and in events mode a fightcenter page gives that one event. A fightcenter link that names a bout (…/fightId/…) gives exactly that bout. Each URL becomes its own search and results are merged and deduplicated. A URL that does not match the selected mode is skipped with a message. Rankings are selected by promotion and cannot be addressed by URL.
maxResults integer 50 Maximum rows to deliver (0 = unlimited). A row is whatever the selected mode produces: one fighter’s statline in a bout, a fighter, a ranking entry, or an event. In fights mode each bout yields two rows, one per corner.
fromDate string — Only events on or after this date (YYYY-MM-DD). Narrowing by date marks the run coverage incomplete, so incremental mode will not mark unseen records as expired.
toDate string — Only events on or before this date (YYYY-MM-DD).
weightClass string — Substring match, e.g. "Lightweight", "Women's Strawweight". In rankings mode it matches the board name.
gender enum "" Restrict to one gender. Applies to fighters and rankings.
fighterCountry string — Substring match on the fighter's citizenship, e.g. "Brazil".
stance string — Orthodox, Southpaw or Switch.
gym string — Substring match on the fighter’s gym, e.g. "American Top Team", "Pitbull".
minWins integer — Drop fighters whose career record shows fewer wins than this. A fighter with no record on file is dropped, not kept.
minLosses integer — Drop fighters with fewer career losses than this.
titleFightsOnly boolean false Keep only championship-length (five-round) bouts. ESPN ships no title flag, so a five-round main event without a belt is included too.
methodFilter enum "any" Keep only bouts that ended this way.
resultFilter enum "any" Keep only rows where this fighter won, lost, or drew.
searchQuery string — Substring match on fighter name (or event name in events mode).
activeOnly boolean false Fighters mode only: drop fighters ESPN marks inactive.
includeCareerDefense boolean false Fighters mode only. Adds strikes absorbed per minute, striking defence and takedown defence — computed from the opponent side of every bout in the fighter’s log, because the source publishes no defensive career stat. Several times slower per fighter, so it is off by default. Each row reports measuredBouts and measuredMinutes, the basis the figures rest on.
includeOdds boolean false Adds the money line for each fighter (open, close and current), the implied win probability with and without the bookmaker margin, line movement, the best price across the books, and the scheduled-rounds over/under. This is a HISTORICAL archive, not a live feed, and it is not complete: sampled 2026-09-01, 2020-2025 priced 20-24 of every 21-24 bouts checked, 2019 about half, 2018 and 2026 none at all. Closing lines exist only for 2024-2025. Bouts the books did not price, or that fall outside that window, come back with the odds columns empty.
includeDetails boolean true Adds each fighter’s career averages to their fight rows: strikes landed per minute, striking and takedown accuracy, KO/TKO and decision shares. Slower per row. The 42-stat line for the bout itself is always included and is not affected by this.
compact boolean false Core fields only (for AI-agent/MCP workflows).
excludeEmptyFields boolean false Drop null, empty-string, and empty-array fields from each record before push. Smaller payloads for AI agents and dashboards.
incrementalMode boolean false Compare against the previous run and label each row NEW, UPDATED, REAPPEARED, UNCHANGED or EXPIRED. By default you get NEW, UPDATED and REAPPEARED; the other two are opt-in below. stateKey is optional — leave it empty and the actor derives a stable key from the search inputs, so different filter sets never share state.
stateKey string — Optional. Stable identifier for the tracked universe. Leave empty to auto-generate from search inputs.
emitUnchanged boolean false Incremental mode only. Off by default: rows that have not changed since the previous run are skipped, so you are not billed for them again. Turn on to receive the full universe every run.
emitExpired boolean false Incremental mode only. When a record a previous run delivered is gone from the source, emit a row for it marked EXPIRED, carrying its listingId so you can reconcile. Knowing something is gone requires seeing the whole set, so turning this on makes the run read the full universe for your search even when Max Results is small — the extra reading is not charged, and expired rows still count against Max Results. Suppressed automatically when the run used a date window or when part of the source failed to respond, because neither run saw the whole universe.
skipReposts boolean false When incremental, skip records whose content matches an expired record from a prior run (cross-run duplicate detection).
telegramToken string — Telegram bot token (from @BotFather). Required for Telegram notifications.
telegramChatId string — Telegram chat or channel ID (e.g. "-100123456789"). Required when telegramToken is set.
discordWebhookUrl string — Discord incoming webhook URL. Server Settings → Integrations → Webhooks → New Webhook.
slackWebhookUrl string — Slack incoming webhook URL. api.slack.com/messaging/webhooks.
notificationLimit integer 5 Maximum number of records included in each notification message (1–20).
notifyOnlyChanges boolean false When Incremental Mode is on, only send notifications for records marked NEW, UPDATED or REAPPEARED. Has no effect outside incremental mode.
whatsappAccessToken string — WhatsApp Cloud API permanent access token (System User token from Meta Business). Recipient must have messaged the business number within the last 24h (service-conversation window — free since Nov 2024).
whatsappPhoneNumberId string — Your WhatsApp Business phone-number ID (numeric, from Meta dashboard). Required when whatsappAccessToken is set.
whatsappTo string — Recipient phone in E.164 format without + (e.g. "436641234567"). Recipient must have messaged your business number within last 24h.
webhookUrl string — Receives a JSON POST with {metadata, items} after each run. Universal escape hatch for n8n / Make / Zapier / custom backends.
webhookHeaders object — Optional JSON object of custom headers (e.g. {"Authorization":"Bearer ..."}).
appConnector string — Optional. Pick a connected app under Settings → API & Integrations to receive your results. The app receives up to the first 500 rows of the run and only rows that carry data — records marked EXPIRED are not sent, so an app-side copy will keep a record the source has dropped. The full run is always in the dataset. Best-effort across MCP connectors as Apify expands its catalog.
mcpIssueTeam string — Only when the connected app is an issue tracker: the team (name or ID) the summary issue is created under, if that app requires one.

Output fields

Every MMA returns the same 174-field schema. Missing values are null — never omitted.

  • recordType
  • name
  • fightId
  • eventId
  • eventName
  • date
  • promotion
  • weightClass
  • cardSegment
  • matchNumber
  • scheduledRounds
  • roundLengthSeconds
  • venue
  • venueName
  • venueCity
  • venueState
  • venueCountry
  • location
  • referee
  • isCompleted
  • boutCount
  • fighter1Id
  • fighter1Name
  • fighter2Id
  • fighter2Name
  • winnerId
  • winnerName
  • isDraw
  • isNoContest
  • isTitleFight
  • method
  • methodCategory
  • methodDetail
  • methodTarget
  • round
  • time
  • fighterId
  • fighterName
  • nickname
  • corner
  • isWinner
  • result
  • opponentName
  • opponentId
  • opponent
  • country
  • gender
  • age
  • dateOfBirth
  • isActive
  • stance
  • reachInches
  • heightInches
  • weightLbs
  • gym
  • styles
  • accolades
  • careerRecord
  • fighterUrl
  • headshotUrl
  • wins
  • losses
  • draws
  • noContests
  • tkoWins
  • tkoLosses
  • submissionWins
  • submissionLosses
  • titleWins
  • titleLosses
  • titleDraws
  • totalFights
  • careerStrikeLPM
  • careerStrikeAccuracy
  • careerTakedownAvg
  • careerTakedownAccuracy
  • careerSubmissionAvg
  • careerKoTkoRate
  • careerDecisionRate
  • measuredStrikesLandedPerMinute
  • measuredStrikesAbsorbedPerMinute
  • measuredStrikeDifferentialPerMinute
  • measuredStrikeDefense
  • measuredTakedownDefense
  • measuredBouts
  • measuredMinutes
  • oddsProvider
  • oddsProviderCount
  • moneyLine
  • moneyLineOpen
  • moneyLineClose
  • closingLineProvider
  • moneyLineDecimal
  • moneyLineMovement
  • impliedProbabilityMovement
  • bestMoneyLine
  • bestMoneyLineProvider
  • impliedProbabilitySpread
  • isFavorite
  • isPickEm
  • impliedWinProbability
  • impliedWinProbabilityNoVig
  • opponentMoneyLine
  • opponentImpliedWinProbability
  • roundsOverUnder
  • overOdds
  • underOdds
  • rankingBoard
  • rankingType
  • rank
  • rankTrend
  • titleDefenses
  • isChampion
  • knockDowns
  • totalStrikesAttempted
  • totalStrikesLanded
  • sigStrikesAttempted
  • sigStrikesLanded
  • sigDistanceHeadStrikesAttempted
  • sigDistanceHeadStrikesLanded
  • sigDistanceBodyStrikesAttempted
  • sigDistanceBodyStrikesLanded
  • sigDistanceLegStrikesAttempted
  • sigDistanceLegStrikesLanded
  • sigClinchBodyStrikesAttempted
  • sigClinchBodyStrikesLanded
  • sigClinchHeadStrikesAttempted
  • sigClinchHeadStrikesLanded
  • sigClinchLegStrikesAttempted
  • sigClinchLegStrikesLanded
  • sigGroundHeadStrikesAttempted
  • sigGroundHeadStrikesLanded
  • sigGroundBodyStrikesAttempted
  • sigGroundBodyStrikesLanded
  • sigGroundLegStrikesAttempted
  • sigGroundLegStrikesLanded
  • takedownsAttempted
  • takedownsLanded
  • takedownsSlams
  • takedownAccuracy
  • advances
  • reversals
  • submissions
  • timeInControl
  • sigDistanceStrikesAttempted
  • sigDistanceStrikesLanded
  • sigClinchStrikesAttempted
  • sigClinchStrikesLanded
  • sigGroundStrikesAttempted
  • sigGroundStrikesLanded
  • sigStrikesAbsorbed
  • sigStrikeDefense
  • takedownsDefended
  • takedownDefense
  • sigStrikeAccuracy
  • advanceToHalfGuard
  • advanceToSide
  • advanceToMount
  • advanceToBack
  • slamRate
  • url
  • portalUrl
  • listingId
  • searchQuery
  • contentQuality
  • readIncomplete
  • detailFetched
  • scrapedAt
  • source
  • contentHash
  • changeType
  • isRepost
  • repostOfId
  • repostDetectedAt

Sample output

One object per MMA. Here is a real example from a production run:

{
  "recordType": "fight",
  "name": "Felipe Bunes",
  "fightId": "401623977",
  "eventId": "600039893",
  "eventName": "UFC Fight Night: Ankalaev vs. Walker 2",
  "date": "2024-01-13T21:00Z",
  "promotion": "ufc",
  "weightClass": "Flyweight",
  "cardSegment": "Prelims",
  "matchNumber": 11,
  "scheduledRounds": 3,
  "roundLengthSeconds": 300
}

Truncated — full records contain 174 fields. See Output fields for the complete schema.

Try UFC & MMA Stats Scraper for espn.com now — $5 free credit, no credit card →


Pricing

Pay only for what you extract. No subscription required — Apify's free $5 credit covers thousands of results.

Event Price (USD)
Actor Start $0.001
Result $0.0005

See the actor on Apify for current pricing.


FAQ

How do I scrape espn.com? Use this actor on Apify to extract structured data from espn.com. Configure your search query and filters in the input, then click Start — no coding required.

How do I get espn.com data as JSON, CSV, or Excel? The actor writes each MMA to Apify's dataset. Download as JSON, CSV, or Excel from the Console, stream via the API, or push to Make, Zapier, Airbyte, or Keboola.

Is it legal to scrape espn.com? Web scraping of publicly available data is generally legal. This actor only accesses publicly visible information. Always check espn.com's terms of service for your specific use case.

How much does it cost? Pay-per-event pricing — you only pay for ${NOUNS} extracted. Apify's free $5 credit is enough to run thousands of results before you pay anything.

How does incremental mode work? Each MMA gets a content hash. On subsequent runs, only new or changed MMA records are emitted — saving time, compute, and storage. Expired MMA records can be tracked separately.

Do I need an API key or credentials? No. Just sign up for Apify, paste your input, and click Start. No credit card required.


Related products by Black Falcon Data

Browse all Black Falcon Data actors →


Getting started with Apify

New to Apify? Create a free account with $5 credit — no credit card required.

  1. Sign up — $5 platform credit included
  2. Open UFC & MMA Stats Scraper for espn.com and configure your input
  3. Click Start — export results as JSON, CSV, or Excel

Need more later? See Apify pricing.


About Black Falcon Data

Black Falcon Data builds production-grade web scrapers for job boards and marketplace data. Browse our full actor catalog at www.blackfalcondata.com.


Last updated: 2026 09

About

Scrape espn.com for MMA fights, fighters, rankings and events at $0.50 per 1,000 rows — the lowest price of any comparable UFC scraper. 42 measured stats per fighter per bout · reach, stance, gym & referee · career defence & closing sportsbook odds.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors