# Amilia Scraper: Summer Camps, Classes & Activities (`firsthand/amilia-activities-scraper`) Actor

Amilia SmartRec scraper: export summer camps, day camps, classes, swim lessons and recreation programs from public Amilia stores of cities, park districts, swim schools and museums. Schedules, prices, age limits, open spots and taxes to Excel, CSV or JSON.

- **URL**: https://apify.com/firsthand/amilia-activities-scraper.md
- **Developed by:** [Firsthand](https://apify.com/firsthand) (community)
- **Categories:** Education, Sports, Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$5.00 / 1,000 activities

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?

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

## Amilia Scraper: Summer Camps, Classes & Activities

Export summer camps, day camps, classes, swim lessons and recreation programs from public Amilia SmartRec stores: city recreation departments, park districts, swim schools and museums in the US and Canada. Paste an organization, program or activity URL and get one row per activity with schedule, dates, prices, age limits, open spots and taxes. No Amilia account, API key or proxy needed.

Good for camp and class directories, parent guides, municipal program research, price comparison between cities, and scheduled monitoring of new sessions and open spots.

### What you get

One row represents an Amilia activity offering. A recurring class can contain several occurrences in its published schedule; this Actor does not expand it into one row per class date.

- Activity ID, name, organization slug and source URL.
- Program URL, category and subcategory, for activities found through a program and for direct activity URLs.
- Published schedule text, parsed start date and end date when identifiable.
- Price text, minimum and maximum published amounts, currency symbol and separate `priceOptions` with their payment periods.
- Tax treatment, discount conditions, pricing notes and availability text when published.
- Location text, age requirement, prerequisites, organizer notes and description with details enabled.
- UTC observation time and a `detailsFetched` flag.

### How to use it

Paste public URLs into `startUrls`:

```json
{
  "startUrls": [
    {"url": "https://app.amilia.com/store/en/playonnyc/shop/programs/136773"}
  ],
  "maxItems": 100,
  "includeDetails": true,
  "maxRequests": 500,
  "requestDelayMs": 250
}
```

Accepted paths are `/store/en/ORGANIZATION`, `/shop/programs`, `/shop/programs/ID` and `/shop/activities/ID` beneath that store. Program and activity IDs must be numeric. French store paths are fetched in English. URL query parameters are removed; URL filters are not supported. Old hashed `/api/Activity/Detail` links, search URLs, calendars, memberships, facilities and private registration pages are not supported.

`urls`, `organizationUrls` and `programUrls` are aliases for `startUrls`. The first nonempty field wins. The Python input parser accepts strings and objects containing `url`; Console uses its URL-list editor.

An organization URL such as `https://app.amilia.com/store/en/ville-de-laval-inscriptions` collects every public program of that organization. With details on, a run saves about 45 activities per minute (169 activities in 226 seconds in our test); without details, about 150 per minute (88 activities in 36 seconds).

Defaults are 100 activities, 500 HTTP requests, details enabled and 250 milliseconds between sequential requests. `maxItems: 0` removes the item limit, but the request budget still applies. All limits count across the entire run. Duplicates are removed within the run using organization plus activity ID.

With `includeDetails: false`, list records omit fields only available on detail pages, including tax labels and age requirements. A direct activity URL always fetches its details and also reads its program group, because drop-in prices, the payment period and the end of single-date events appear only in the program list. This costs up to two extra requests, shared by cards from the same group. If the group cannot be read, the row keeps the detail-page values; problems in such a lookup group are logged but not added to `SUMMARY.errors`, because you did not request that program.

One failing page does not stop the rest of the run. A retired program, a removed activity, an unreadable group or a list item without a usable activity link is recorded in `SUMMARY.errors` with its own URL, and the other programs and activities are still collected. Password-protected programs are listed in `SUMMARY.skipped`; they are outside the public scope and do not make the run incomplete. The run fails only when it saves no activities and at least one source error occurred.

### Amilia SmartRec data export (Excel, CSV, JSON)

Every run writes a dataset you can download from the run page as Excel, CSV, JSON, XML or HTML, or send it to Zapier, Make and other tools through Apify integrations. The **Activities** view shows the main columns: name, start date, schedule, price, tax, age and availability.

### Example output

Excerpt from an observed SMLI activity, not a guarantee that the example remains available:

```json
{
  "activityId": "7217158",
  "organization": "SMLI",
  "name": "11/3 - Let's Build It!",
  "url": "https://app.amilia.com/store/en/SMLI/shop/activities/7217158",
  "startDate": "2026-11-03",
  "priceText": "$100.00 / per session",
  "priceMin": 100,
  "priceMax": 100,
  "currency": null,
  "currencySymbol": "$",
  "pricePeriod": "session",
  "taxTreatment": "waived",
  "ageRequirementText": "Over 5 years old as of 12/31/2026",
  "availability": "available",
  "availabilityText": "Available",
  "detailsFetched": true
}
```

`discountsText` preserves source pricing tooltips and notes. These can include discount conditions, prorated prices or installment totals such as "Total amount"; not every entry represents a discount.

All dataset fields: `activityId`, `organization`, `url`, `name`, `programUrl`, `category`, `subCategory`, `scheduleText`, `startDate`, `startDateText`, `endDate`, `priceText`, `priceMin`, `priceMax`, `currency`, `currencySymbol`, `pricePeriod`, `priceOptions`, `taxTreatment`, `discountsText`, `availability`, `availabilityText`, `locationText`, `ageRequirementText`, `prerequisitesText`, `notesText`, `description`, `detailsFetched`, `scrapedAt`.

### Prices, schedules and missing data

Unknown values remain null. Dollar symbols do not establish whether the currency is USD or CAD, so `currency` is currently null. Price ranges and options can depend on residency, membership, drop-in versus course registration or proportional pricing. `priceMin` and `priceMax` summarize published options, not a universally available final price. A published `$0.00` is kept as zero: it can be a free event, a multipass-only drop-in or a prorated price for a session already in progress, with the original amount in `discountsText`. Use `priceOptions` and the original conditions; the Actor does not calculate taxes, discounts or a weekly equivalent. `pricePeriod` can be `session`, `drop_in`, `multiple` or null. A direct detail page may not identify its payment period.

Age text is preserved verbatim, including reference dates. No child eligibility is inferred. Availability can be `available`, `full` or null; the published text is retained, and `Available` does not imply a known seat count. A bare number such as `2` is the published count of open spots. No booking or waitlist action is performed. Schedule times retain their published text, without an inferred timezone. `endDate` is the latest date in the published schedule: the last "until" date of several periods, the last date of a listed series, the end of a date range or the single "On" date. It is null when any part of the schedule is given only as a count, such as "10 times starting September 23, 2026", because the end of that part is not published. Links to other sites are ignored.

The Actor follows public programs and their subcategory lists exposed by the current page templates. It does not discover every organization in a city, guarantee platform-wide coverage or bypass account restrictions. Retired URLs and unrecognized structures produce errors rather than empty successful results. A missing activity is not proof of cancellation.

### Run completeness

The default key-value store contains `SUMMARY` with `items`, `requests`, `bytes`, `complete`, `stopReason`, `errors` and `skipped`. It is written once the input is accepted and updated every 10 activities with `stopReason: "running"`. A run aborted from Console or the API writes a final record with `stopReason: "aborted"`; its `items` can be one lower than the dataset when the stop arrives while a row is being saved. Invalid input fails the run before any request, with the reason in the status message and no `SUMMARY`. A run that hits its timeout is stopped without notice, so `SUMMARY` keeps the last progress record (`stopReason: "running"`, up to 9 activities behind the dataset). The default timeout is one hour. Item or request limits produce an incomplete summary. `complete: true` means the implemented traversal of the supplied scope finished without a recognized error, not that all activities on Amilia were collected. Check this record before comparing snapshots. Incremental updates, persistent deduplication and resumable runs are not implemented.

### Schedule recurring exports

Use [Apify Schedules](https://docs.apify.com/actors/running/schedules) to repeat exports. The Actor does not need its own scheduler.

1. Run the Actor with your URLs and limits, and check the dataset and `SUMMARY`.
2. Save the configuration as an Actor task.
3. In Console, open Schedules, create a schedule and add the saved task.
4. Set the frequency and timezone, then enable the schedule. For example, `0 8 * * *` runs daily at 08:00 in the selected timezone. Enable exclusive execution to avoid overlapping runs.

Each run produces a fresh dataset and may return activities seen in earlier runs. Persistent deduplication, change alerts and an "only new activities" mode are not implemented. Compare snapshots by `organization` plus `activityId`, retaining `scrapedAt`; check `SUMMARY.complete` and errors before interpreting missing rows. Repeatedly returned activities count again toward any applicable per-activity charge. Choose item and request limits that cover the programs you intend to monitor.

### Pricing

**$0.005 per activity ($5 per 1,000), with no start fee.** Platform usage is included.

- You pay only for activities saved to the dataset. Duplicates within a run, failed pages, skipped password-protected programs and empty programs are not charged.
- Set a maximum cost per run in the run options; the Actor stops when the next activity would exceed it.
- Users on the Apify free plan can export up to 100 activities per run.
- A scheduled run charges again for activities it returns again.

There is no other Amilia scraper in Apify Store (October 2026).

### Amilia data API for developers (unofficial)

Amilia's official API needs an account with permissions on the organization or partner access. This Actor reads the public store pages instead and gives you an API over Apify: start a run and get the activities as JSON in the same call.

```bash
curl -X POST "https://api.apify.com/v2/acts/firsthand~amilia-activities-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" -H "Content-Type: application/json" \
  -d '{"startUrls": [{"url": "https://app.amilia.com/store/en/playonnyc/shop/programs/136773"}], "maxItems": 50}'
```

For long runs, start the run asynchronously, then read the default dataset and the `SUMMARY` record from the output links. Python and JavaScript clients are on the API tab of this page. Do not put an Apify token in public URLs or shared scripts.

### Good to know

This Actor is independent of Amilia. Use public program data consistently with the source terms and applicable law, including GDPR where applicable. The implementation does not access participant, child, parent or private account records. Public free-text descriptions may contain organizer-provided contact information; review your intended use and retention. Source layouts and registration availability can change.

# Actor input Schema

## `startUrls` (type: `array`):

Public Amilia store, program or activity URLs. French paths are read in English. Duplicate activities are returned once per run.

## `urls` (type: `array`):

Public Amilia store, program or activity URLs. French paths are read in English. Duplicate activities are returned once per run.

## `organizationUrls` (type: `array`):

Public Amilia store, program or activity URLs. French paths are read in English. Duplicate activities are returned once per run.

## `programUrls` (type: `array`):

Public Amilia store, program or activity URLs. French paths are read in English. Duplicate activities are returned once per run.

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

Total activities across all URLs. Default 100; zero means no item limit. maxRequests still applies.

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

Request budget including retries and detail pages. Default 500. A capped run is marked incomplete in SUMMARY.

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

Minimum interval between HTTP requests. Default 250 ms. Requests are sequential.

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

Default true. Open details for description, address, age condition and tax label. Direct activity URLs always include details.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://app.amilia.com/store/en/playonnyc/shop/programs/136773"
    }
  ],
  "urls": [],
  "organizationUrls": [],
  "programUrls": [],
  "maxItems": 100,
  "maxRequests": 500,
  "requestDelayMs": 250,
  "includeDetails": true
}
```

# Actor output Schema

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

No description

## `summary` (type: `string`):

No description

# 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 = {
    "startUrls": [
        {
            "url": "https://app.amilia.com/store/en/playonnyc/shop/programs/136773"
        }
    ],
    "urls": [],
    "organizationUrls": [],
    "programUrls": [],
    "maxItems": 100,
    "maxRequests": 500,
    "requestDelayMs": 250,
    "includeDetails": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("firsthand/amilia-activities-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 = {
    "startUrls": [{ "url": "https://app.amilia.com/store/en/playonnyc/shop/programs/136773" }],
    "urls": [],
    "organizationUrls": [],
    "programUrls": [],
    "maxItems": 100,
    "maxRequests": 500,
    "requestDelayMs": 250,
    "includeDetails": True,
}

# Run the Actor and wait for it to finish
run = client.actor("firsthand/amilia-activities-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 '{
  "startUrls": [
    {
      "url": "https://app.amilia.com/store/en/playonnyc/shop/programs/136773"
    }
  ],
  "urls": [],
  "organizationUrls": [],
  "programUrls": [],
  "maxItems": 100,
  "maxRequests": 500,
  "requestDelayMs": 250,
  "includeDetails": true
}' |
apify call firsthand/amilia-activities-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,firsthand/amilia-activities-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/aeF8ZGsvae7wz3GlX/builds/9iYGhQbP6Eq6hqmFd/openapi.json
