# Czech Athletics Scraper (`dankovac30/czech-athletics-scraper`) Actor

Scrapes official competition results, athlete profiles, and event calendars from atletika.cz. Returns clean JSON data with auto-calculated World Athletics (Spiriev) scoring points.

- **URL**: https://apify.com/dankovac30/czech-athletics-scraper.md
- **Developed by:** [Dan Kováč](https://apify.com/dankovac30) (community)
- **Categories:** Developer tools, Automation, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## 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

## Czech Athletics Scraper

Apify actor that scrapes public results from [atletika.cz](https://online.atletika.cz), the official website of the Czech Athletics Federation.

The actor exposes three independent scrapers — meet results, athlete profiles, and calendar lookup — and returns raw parsed dictionaries. It performs no ETL, aggregation, or post-processing; downstream consumers are expected to transform the records into whatever shape they need.

### Modes

Set the `mode` input to one of:

| Mode | Inputs used | Returned records |
|------|-------------|------------------|
| `meet` | `meetIds: int[]` | One row per athlete performance (or attempt for field/vertical events). `record_type = "meet_result"`. |
| `athlete` | `athleteIds: int[]` | Athlete profile (`record_type = "athlete_profile"`), plus one row per medal (`athlete_medal`) and one row per achievement entry (`athlete_achievement`). |
| `calendar` | `dateFrom`, `dateTo` (YYYY-MM-DD) | One row per meet ID found in the range. `record_type = "calendar_meet_id"`. |

All records are pushed to a single default dataset. Use the `record_type` field to route them.

### Inputs

| Field | Type | Description |
|-------|------|-------------|
| `mode` | `string` (enum) | `meet`, `athlete`, or `calendar`. |
| `meetIds` | `int[]` | Meet IDs to scrape (for `mode = meet`). |
| `athleteIds` | `int[]` | Athlete CAS IDs to scrape (for `mode = athlete`). |
| `dateFrom` | `string` | ISO date, defaults to today (for `mode = calendar`). |
| `dateTo` | `string` | ISO date, defaults to `dateFrom + 31 days`. |
| `requestDelayMs` | `int` | Delay between HTTP requests. |
| `requestTimeoutSecs` | `int` | Per-request timeout. Defaults to 30. |
| `proxyConfiguration` | `object` | Apify Proxy config. Recommended — atletika.cz rate-limits aggressively. |

### Output Data Structure

All records are pushed to a single default dataset. Use the `record_type` field to route them in your downstream pipelines.

#### 1. Meet Results (`mode = "meet"`)

```json
{
  "meet_id": "Integer: Unique identifier of the athletics meet",
  "meet_name": "String: Official name of the competition",
  "location": "String: Competition location",
  "date": "String: Competition date (YYYY-MM-DD)",
  "time": "String: Event start time or null",
  "is_indoor": "Boolean: Indoor or outdoor competition flag",
  "event_id": "String: Unique identifier of the event",
  "discipline": "String: Discipline name",
  "specification": "String: Event-specific parameters (e.g., hurdle height, javelin weight) or null",
  "main_phase": "String: Main competition phase (e.g., Final, Heat, Qualification)",
  "sub_phase": "String: Subdivision of phase (e.g., Heat 2, Final A)",
  "name": "String: Athlete name",
  "cas_id": "String: Unique federation athlete identifier",
  "category": "String: Competition category",
  "sex": "String: Athlete sex (M/Z)",
  "birth_year": "String: Athlete birth year",
  "age": "Integer: Athlete age at competition date",
  "team": "String: Represented club",
  "lane": "Integer: Assigned lane number or null",
  "order": "Integer: Starting order or null",
  "reaction": "Float: Reaction time or null",
  "performance_raw": "String: Original result text as displayed on the website",
  "numeric_value": "Float: Parsed numerical performance value",
  "wind": "Float: Wind measurement value or null",
  "wa_points": "Integer: World Athletics scoring points",
  "height": "Float: Height value for vertical jump events or null",
  "attempt_in_series": "Integer: Attempt sequence identifier or null",
  "position": "String: Final competition placement",
  "record": "String: Performance status marker (e.g., PB, SB, MR) or null",
  "dq_rule": "String: Disqualification rule code or null",
  "trial_id": "String: Unique UUID of the result record",
  "record_type": "String: Always 'meet_result'"
}
```

#### 2. Athlete Data (`mode = "athlete"`)

##### Athlete Info

```json
{
  "cas_id": "Integer: Athlete official CAS federation ID",
  "name": "String: Athlete full name",
  "birth_year": "Integer: Athlete birth year",
  "club": "String: Current represented club",
  "coach_1": "String: Primary coach or null",
  "coach_2": "String: Secondary coach or null",
  "coach_3": "String: Tertiary coach or null",
  "registration_until": "String: Registration validity date (YYYY-MM-DD)",
  "record_type": "String: Always 'athlete_profile'"
}
```

##### Athlete Medals

```json
{
  "medal_id": "String: Unique UUID of the medal record",
  "cas_id": "Integer: Athlete official CAS federation ID",
  "year": "Integer: Year of the championship",
  "placement": "Integer: Final placement (1, 2, or 3)",
  "category": "String: Championship category (e.g., MČR)",
  "type": "String: Environment (e.g., dráha, hala)",
  "discipline": "String: Discipline name",
  "location": "String: Host city/location",
  "performance_raw": "String: Original result text",
  "numeric_value": "Float: Parsed numerical performance value",
  "record_type": "String: Always 'athlete_medal'"
}
```

##### Athlete Achievements

```json
{
  "achievement_id": "String: Unique UUID of the achievement record",
  "cas_id": "Integer: Athlete official CAS federation ID",
  "year": "Integer: Year of the event",
  "event_name": "String: Name of the major event (e.g., OH, MS)",
  "discipline": "String: Discipline name",
  "result_phase": "String: Reached phase or placement",
  "record_type": "String: Always 'athlete_achievement'"
}
```

#### 3. Calendar (`mode = "calendar"`)

```json
{
  "record_type": "String: Always 'calendar_meet_id'",
  "meet_id": "Integer: Unique identifier of the meet"
}
```

### Scoring

Meet results include a `wa_points` field computed via the quadratic model. Coefficients live in [`src/coefficients-2025.json`](src/coefficients-2025.json); replacing that file is all that's required to migrate to a future edition of the tables. The methodology follows Jeff Chen's [iaaf-scoring-tables](https://github.com/jchen1/iaaf-scoring-tables).

### Development

Local run (without Apify platform):

```bash
pip install -r requirements.txt
$env:APIFY_LOCAL_STORAGE_DIR="apify_storage"; python -m src
```

The actor reads its input via the standard Apify input mechanism — for local runs, place an `apify_storage/key_value_stores/default/INPUT.json` file with the desired input.

### Layout

```
.actor/
    actor.json          # Actor metadata
    input_schema.json   # Input UI schema
Dockerfile              # Build recipe
requirements.txt        # apify, requests, selectolax
src/
    __main__.py         # python -m src entrypoint
    main.py             # Async actor orchestrator
    scraper.py          # HTML fetch + parsing logic
    cas_config.py       # Discipline / category / WA mappings
    calculator.py       # Spiriev points calculator
    coefficients-2025.json
```

# Actor input Schema

## `mode` (type: `string`):

Which scraper to run. 'meet' scrapes competition results, 'athlete' scrapes athlete profiles, 'calendar' returns meet IDs found within the given date range.

## `meetIds` (type: `array`):

List of numeric meet IDs to scrape. Used when mode = 'meet'. Example: \[12345, 67890].

## `athleteIds` (type: `array`):

List of numeric athlete CAS IDs to scrape. Used when mode = 'athlete'. Example: \[10000074944, 10000015944].

## `dateFrom` (type: `string`):

Start date (YYYY-MM-DD). Used when mode = 'calendar'. Defaults to today when omitted.

## `dateTo` (type: `string`):

End date (YYYY-MM-DD). Used when mode = 'calendar'. Defaults to dateFrom + 31 days when omitted.

## `requestDelayMs` (type: `integer`):

Wait between consecutive HTTP requests. Helps avoid rate-limiting on atletika.cz.

## `requestTimeoutSecs` (type: `integer`):

How long to wait for a single HTTP response before giving up.

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

Apify Proxy configuration. Recommended because atletika.cz returns HTTP 403/429 on burst requests.

## Actor input object example

```json
{
  "mode": "meet",
  "meetIds": [],
  "athleteIds": [],
  "requestDelayMs": 0,
  "requestTimeoutSecs": 30,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# 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 = {
    "meetIds": [],
    "athleteIds": [],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("dankovac30/czech-athletics-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 = {
    "meetIds": [],
    "athleteIds": [],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("dankovac30/czech-athletics-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 '{
  "meetIds": [],
  "athleteIds": [],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call dankovac30/czech-athletics-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dankovac30/czech-athletics-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/c9S8bKIDePLUhzMwt/builds/f23FMhrHsfuPaTZFZ/openapi.json
