# Unofficial ToDoCanada Event Scraper & Canadian Data API (`thescrapelab/todocanada-event-scraper`) Actor

Unofficial independent scraper for public ToDoCanada event pages. Export schedules, venues, coordinates, prices, ticket links, organizers, contacts, and URLs.

- **URL**: https://apify.com/thescrapelab/todocanada-event-scraper.md
- **Developed by:** [Inus Grobler](https://apify.com/thescrapelab) (community)
- **Categories:** Travel, Automation, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 canadian event results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Unofficial ToDoCanada Event Scraper – Canadian Events Data API

Scrape upcoming Canadian events from ToDoCanada and export them as structured data. Collect event names, normalized dates and times, schedules, venues, coordinates, structured price ranges, ticket links, organizers, contacts, accessibility information, images, and source URLs.

> **Unofficial independent tool:** This Actor is not affiliated with, sponsored by, or endorsed by ToDoCanada. ToDoCanada is a third-party website and retains all rights to its name, website, and content.

Use this Canadian events scraper to build local event calendars, monitor festivals and activities, research tourism markets, discover free events, or supply current event data to apps and AI workflows. Search one or several supported city sections, filter by event category and date, and choose between fast listing data or enriched event details.

### Why use this Canadian event scraper?

- **Multi-city collection:** Search ToDoCanada city sections such as Toronto, Ottawa, Calgary, Edmonton, Vancouver, and Halifax in one run.
- **Category targeting:** Collect all events or focus on category paths such as free events, family activities, food festivals, concerts, and other available ToDoCanada categories.
- **Normalized dates:** Receive `YYYY-MM-DD` dates, local timezone information, and the original source date text for verification.
- **Detailed event data:** Enrich listings with descriptions, schedules, ticket information, audience guidance, contact fields, and wheelchair accessibility when available.
- **Planning-ready fields:** Get parsed minimum and maximum prices, currency, direct ticket links, organizer details, postal codes, and venue coordinates when published.
- **Comparison-ready output:** Stable event IDs, canonical source URLs, and content hashes make deduplication and change monitoring easier.
- **Controlled usage:** Limit locations, pages, and dataset rows to keep runs predictable.
- **Integration-ready results:** Export the Apify dataset as JSON, CSV, Excel, or XML, or retrieve it through the Apify API.

### Use cases

- Build a Canadian events calendar, newsletter, directory, or discovery app.
- Collect Toronto events, Ottawa events, Calgary events, Vancouver events, and other regional listings.
- Find upcoming festivals, concerts, family activities, food events, and free events.
- Monitor changes to event dates, venues, prices, descriptions, or ticket information.
- Research local tourism, entertainment, hospitality, and community activity.
- Create location-aware event recommendations for websites, mobile apps, or AI assistants.
- Supply structured event data to CRM, analytics, data warehouse, and automation workflows.

### Start with this input

Run the Actor from Apify Console with a small Toronto search:

```json
{
  "locationSlugs": ["toronto"],
  "categories": [],
  "maxListingPagesPerLocation": 1,
  "maxItems": 10,
  "includeDetails": true
}
```

For a faster multi-city feed without visiting every event page:

```json
{
  "locationSlugs": ["toronto", "ottawa", "calgary"],
  "categories": ["free-events"],
  "maxListingPagesPerLocation": 1,
  "maxItems": 50,
  "includeDetails": false
}
```

### Input options

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `locationSlugs` | array | `["toronto"]` | Select up to 10 Canadian cities from the Console dropdown, or send valid ToDoCanada city slugs through the API. |
| `categories` | array | `[]` | Select optional event categories from the Console dropdown, or send valid category paths through the API. Leave empty to collect all available categories. |
| `maxListingPagesPerLocation` | integer | `1` | Maximum listing pages for each location and category combination, from 1 to 25. The combined crawl is capped at 250 listing pages. |
| `maxItems` | integer | `10` | Global maximum number of event rows written to the dataset, from 1 to 500. |
| `includeDetails` | boolean | `true` | Visit event pages for descriptions, schedules, structured prices, ticket links, organizer data, contacts, categories, and accessibility. |
| `dateFrom` | string | current date | Include events ending on or after this date. Select it in the Console calendar or send `YYYY-MM-DD` through the API. |
| `dateTo` | string | none | Include events starting on or before this inclusive date. Select it in the Console calendar or send `YYYY-MM-DD` through the API. |

The Canadian cities and event categories appear as searchable multi-select dropdowns in Apify Console. Common choices are suggested while custom ToDoCanada slugs remain supported. Category values follow ToDoCanada URL paths; examples include `free-events`, `familyevents`, and `food-drink-events/food-festivals`.

To keep runs predictable, the number of cities × selected categories (or 1 when categories are empty) × listing pages per combination must be 250 or fewer. Split wider collection jobs into multiple runs.

Connection handling is automatic. The Actor uses direct access, so clients do not need to select, configure, or pay for a proxy. Deprecated proxy inputs from older integrations are accepted for compatibility but ignored.

### What the Actor returns

Each default dataset row represents one canonical Canadian event:

```json
{
  "eventId": "91734ec6da17977fdf1778e0",
  "title": "Taste of Mississauga – Summer Festival 2026",
  "sourceUrl": "https://www.todocanada.ca/city/toronto/event/taste-of-mississauga-summer-festival-2026/",
  "listingLocation": "toronto",
  "categories": ["Family & Kids' Events", "Food Events"],
  "description": "A two-day summer festival with food, entertainment and family activities.",
  "startDate": "2026-09-05",
  "endDate": "2026-09-06",
  "startDateTime": "2026-09-05T12:00:00-04:00",
  "endDateTime": "2026-09-06T20:00:00-04:00",
  "timezone": "America/Toronto",
  "scheduleText": "Saturday September 5, 2026 to Sunday September 6, 2026",
  "venueName": "Mississauga",
  "venueAddress": "3520 Odyssey Dr, Mississauga",
  "postalCode": "L5L 0B9",
  "latitude": 43.5427,
  "longitude": -79.7227,
  "province": "ON",
  "country": "Canada",
  "priceText": "$15 - $35",
  "priceMin": 15,
  "priceMax": 35,
  "currency": "CAD",
  "isFree": false,
  "audience": "All ages",
  "ticketUrl": "https://tickets.example.com/summer-festival",
  "organizerName": "Mississauga Summer Events",
  "contactEmail": "events@example.com",
  "websiteUrl": "https://events.example/summer-festival",
  "isWheelchairAccessible": true,
  "extractionStatus": "full",
  "contentHash": "0538a71009bf4359b3b6526e1a5d4dd07dffe5731e636598169076ae7ca6a59c",
  "scrapedAt": "2026-08-28T16:30:09.848Z"
}
```

Important output fields include:

- `eventId`, `sourceUrl`, and `contentHash` for matching, deduplication, and change detection.
- `startDate`, `endDate`, `startDateTime`, `endDateTime`, `timezone`, `scheduleText`, and raw source dates for scheduling workflows.
- `venueName`, `venueAddress`, `sourceLocality`, `province`, `postalCode`, `latitude`, `longitude`, and `country` for location analysis.
- `priceText`, `priceMin`, `priceMax`, `currency`, `isFree`, `ticketUrl`, `ticketInfo`, `audience`, and `isWheelchairAccessible` for event qualification.
- `description`, `categories`, `organizerName`, `organizerUrl`, `organizerEmail`, `contactEmail`, `phone`, `websiteUrl`, and `imageUrl` for enriched event profiles.
- `extractionStatus` and `warnings` for data-quality checks. A value of `listing-only` means detail enrichment was disabled or unavailable.

Output fields are source-dependent. Many current ToDoCanada pages publish calendar dates but not exact start/end times, organizer metadata, postal codes, or coordinates. When a value is not published, the corresponding field is `null`; the Actor does not invent missing event details.

The run's default key-value store also contains `RUN_SUMMARY`, with the normalized input, pages visited, events discovered, rows produced, duplicates skipped, date-filter counts, challenge observations, warnings, errors, and final status.

### Run with the Python API

Install `apify-client`, provide your Apify API token, and call the Actor by ID:

```python
from decimal import Decimal

from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
actor_client = client.actor("thescrapelab/todocanada-event-scraper")

run_input = {
    "locationSlugs": ["toronto", "ottawa"],
    "categories": ["free-events"],
    "maxListingPagesPerLocation": 1,
    "maxItems": 10,
    "includeDetails": True,
}

run = actor_client.call(
    run_input=run_input,
    max_total_charge_usd=Decimal("0.03"),
)
if run is None:
    raise RuntimeError("The Actor run did not finish successfully")

dataset_client = client.dataset(run.default_dataset_id)
items = dataset_client.list_items().items

for event in items:
    print(event["title"], event["startDate"], event["sourceUrl"])
```

The example allows up to 10 rows and caps pay-per-event charges at $0.03. Adjust both `maxItems` and `max_total_charge_usd` for larger runs.

You can also use Apify schedules for recurring collection and webhooks to notify downstream systems when a run finishes.

### Cost and performance

The Actor uses simple pay-per-event pricing: **$0.003 for each Canadian event row saved to the dataset**. There is no run-start fee, and a run that finds no matching events has no event-result charge. Diagnostic probe rows are not charged.

Example event-result charges:

| Results | Price |
| ---: | ---: |
| 10 | $0.03 |
| 100 | $0.30 |
| 500 | $1.50 |

Apify platform usage is included in the result price. You can set a maximum total charge when starting a run; the Actor charges only rows written to the dataset and stops producing additional event results when the limit is reached. For example, a $0.003 limit permits one result. The run summary then sets `billingLimitReached` to `true` and includes a clear warning. The pricing displayed on the Actor page remains the final source of truth.

Use these controls to manage usage:

- Start with 10–25 events before increasing `maxItems`.
- Set `includeDetails` to `false` when listing-level data is sufficient; this avoids one additional page visit per event.
- Keep `maxListingPagesPerLocation` at `1` for routine monitoring unless deeper pagination is required.
- Connection mode is managed by the Actor and always uses direct access, keeping the client input and expected cost simple.
- Narrow `dateFrom`, `dateTo`, locations, and categories to reduce irrelevant work.

### Reliability and limitations

- Event information is collected from publicly accessible ToDoCanada pages and may change after a run.
- Some events do not publish every field. Missing source values are returned as `null` or an empty collection where appropriate.
- ToDoCanada occasionally publishes inconsistent structured locality or country metadata. The Actor preserves source locality separately and identifies the country as Canada based on the source scope.
- Source date formats are normalized to `YYYY-MM-DD`; original date text and `dateParsingStatus` remain available for verification.
- Event URLs are deduplicated before detail requests, but separate source URLs can still describe related or recurring events.
- Failed detail requests fall back to available listing data and use `extractionStatus: "listing-only"`.
- `startDateTime`, `endDateTime`, organizer, status, postal code, and coordinate fields remain `null` when the source publishes only date-level or basic venue information.
- Persistent security verification can cause bounded retries or a failed request. The Actor does not solve CAPTCHAs or visit external ticket websites.
- Always verify time-sensitive attendance, cancellation, price, and ticket information using `sourceUrl` before acting on it.

### Troubleshooting and FAQ

#### Why did the run return no events?

Check that each location and category matches a valid ToDoCanada URL path. Also review `dateFrom` and `dateTo`; a narrow date window can filter every discovered event. Try one location, no category filter, one listing page, and a small `maxItems` value first.

#### Why are some fields empty?

Publishers do not always provide prices, contact details, audience guidance, accessibility, or complete venue information. Use `extractionStatus`, `warnings`, and the source URL to distinguish missing source data from a failed detail request.

#### Why is a detailed run slower than a listing-only run?

With `includeDetails: true`, the Actor visits each event page individually. Disable enrichment when you only need titles, dates, venues, prices, images, and source URLs available on listing pages.

#### What should I include in a support request?

Open the Actor's **Issues** tab and include the Apify run ID, the affected source URL, the unexpected field or behavior, and a redacted copy of the input. Do not include API tokens or other secrets.

### Responsible use

Use the Actor in accordance with applicable laws, privacy requirements, and website terms. Do not republish personal or copyrighted material without the necessary rights. This Actor is an independent tool and is not affiliated with or endorsed by ToDoCanada.

# Actor input Schema

## `locationSlugs` (type: `array`):

Select one or more Canadian cities. You can also enter another valid ToDoCanada city slug.

## `categories` (type: `array`):

Leave empty for all events, or select one or more categories. You can also enter another valid ToDoCanada category path.

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

Include events ending on or after this date. Choose it in the calendar; API clients send YYYY-MM-DD. The current date is used when left empty.

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

Include events starting on or before this inclusive date. Choose it in the calendar; API clients send YYYY-MM-DD.

## `maxItems` (type: `integer`):

Global maximum number of event rows written to the dataset. Each saved row is one billed event result.

## `includeDetails` (type: `boolean`):

Visit each event page for descriptions, schedules, structured prices, ticket links, organizer data, contacts, and accessibility when published by the source.

## `maxListingPagesPerLocation` (type: `integer`):

Maximum listing pages visited for every city and category combination. Cities × selected categories (or 1 when empty) × this value must not exceed 250.

## `maxSessionRotations` (type: `integer`):

Compatibility input for bounded retry behavior.

## `connectionMode` (type: `string`):

Deprecated compatibility input. Proxy requests are ignored because this Actor always uses direct access.

## `useResidentialProxy` (type: `boolean`):

Deprecated compatibility input. This value is ignored because this Actor always uses direct access.

## `probeOnly` (type: `boolean`):

Development diagnostic that checks one listing and one detail page without returning event rows.

## Actor input object example

```json
{
  "locationSlugs": [
    "toronto"
  ],
  "categories": [],
  "maxItems": 10,
  "includeDetails": true,
  "maxListingPagesPerLocation": 1,
  "maxSessionRotations": 3,
  "connectionMode": "direct",
  "useResidentialProxy": false,
  "probeOnly": false
}
```

# Actor output Schema

## `datasetItems` (type: `string`):

Structured event rows from the requested locations, categories, pages, and date window. Each saved event row is one billable event-result; hidden probe diagnostics are free.

## `runSummary` (type: `string`):

Normalized run input, counters, compatibility and charge-limit warnings, failures, and completion status.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("thescrapelab/todocanada-event-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("thescrapelab/todocanada-event-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 '{}' |
apify call thescrapelab/todocanada-event-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,thescrapelab/todocanada-event-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/gbfZqkBqSx2lP4nyr/builds/3bnL3fienP2tVOzYz/openapi.json
