DEV Community

Cover image for Filtering ACA Camps by Activity is Cheaper Than Post-Processing
Crawler Bros
Crawler Bros

Posted on Fully Autonomous

Filtering ACA Camps by Activity is Cheaper Than Post-Processing

Lowering Payload Costs with Schema-Level Filtering

Building a targeted dataset of summer programs, specialized day camps, or regional youth operations requires sifting through thousands of listings across the United States. Processing raw, unfiltered listings at scale introduces unnecessary platform charges and data cleanup overhead.

The ACA Camps Scraper provides access to the American Camp Association directory of over 3,900 accredited and member camps containing 11,000+ programs. Fetching these records without parameters returns a large volume of generic listings. Setting targeted conditions directly in the input schema narrows the response payload before execution, reducing unnecessary item emits.

Execution Modes and Parameter Scoping

The scraper operates in four distinct execution modes: search, byCampId, byCampName, and byProgramId. Each mode controls how records are returned and determines the format of the output dataset.

{
  "mode": "search",
  "campType": "overnight_camp",
  "locationType": "state",
  "states": ["CO", "WY"],
  "accreditedOnly": true,
  "activities": ["archery", "horseback_riding"],
  "maxItems": 50
}
Enter fullscreen mode Exit fullscreen mode

When running in search mode, passing pre-filtered constraints like activities, singleFocusActivity, or accreditedOnly causes the remote endpoint to filter results during extraction.

If you request a broad search and attempt to filter by activity downstream in Python or SQL, you incur a $0.005 event fee for every extraneous item emitted into the dataset. Pushing criteria directly to the input payload ensures you only ingest listings that match your target requirements.

Search Mode vs Direct Lookups

  • search: Accepts full spatial, activity, and demographic filters. Returns recordType: "camp" objects containing summarized program arrays.
  • byCampName: Searches partial or exact name matches using campNameQuery (minimum 3 characters). Useful when verifying specific organizations like YMCA or Camp Fire.
  • byCampId: Accepts an array of numeric identifiers in campIds to fetch full camp profile details.
  • byProgramId: Takes an array of numeric program IDs in programIds and returns detailed recordType: "program" objects, including session-level pricing and scheduling.
{
  "mode": "byProgramId",
  "programIds": ["16330", "4836"]
}
Enter fullscreen mode Exit fullscreen mode

When targeting session calendars, pricing structures, or specific weekly dates, switching to mode: "byProgramId" yields session granularities that general search results do not include.

Specialized Schema Filters for Precision Targeting

The input schema exposes parameters to isolate specialized operators and niche demographics without dumping the entire directory.

Geographic and Radius Scoping

Setting locationType to zip allows you to define a 5-digit US zip code in postalCode alongside a distanceMiles radius string (e.g., "50"). Alternatively, setting locationType to state allows array-based state filtering using standard two-letter postal abbreviations via the states key.

Special Needs and Population Targeting

For market research on specialized programs, pass specific disability parameters:

{
  "mode": "search",
  "campType": "overnight_camp",
  "disabilities": "autism",
  "disabilitySpecializesIn": true,
  "sessionStartDate": "2026-06-01",
  "sessionEndDate": "2026-06-30",
  "maxItems": 50
}
Enter fullscreen mode Exit fullscreen mode

The booleans disabilitySpecializesIn and disabilityExclusively refine search criteria further, ensuring returned camps either focus primarily on or cater exclusively to the condition passed in disabilities.

Data Cleaning and Field Handling

The scraper omits empty fields from output JSON objects rather than populating them with null or empty string "" values. If a camp does not report a foundedYear, email, or phone, those keys will not exist on the emitted record.

When scraping program descriptions from public search results, truncated text ending in ellipses is automatically sanitized to prevent invalid byte offsets or corrupted special characters from breaking downstream ingest pipelines.

Executing the Scraper Programmatically

You can invoke the scraper using the Apify Python SDK. This script executes a localized search for day camps providing specific amenities and dumps the resulting dataset items.

from apify_client import ApifyClient

client = ApifyClient("YOUR_API_KEY")

run_input = {
    "mode": "search",
    "campType": "day_camp",
    "locationType": "zip",
    "postalCode": "10001",
    "distanceMiles": "25",
    "dayCampAmenities": ["lunch_provided", "transportation_door_to_door"],
    "maxItems": 100,
}

run = client.actor("crawlerbros/aca-camps-scraper").call(run_input=run_input)

dataset_items = client.dataset(run["defaultDatasetId"]).list_items().items

for camp in dataset_items:
    print(f"Camp: {camp.get('campName')} | Phone: {camp.get('phone', 'N/A')}")
    for program in camp.get("programs", []):
        print(f"  - Program: {program.get('programName')} ({program.get('cost')})")
Enter fullscreen mode Exit fullscreen mode

Cost and Event Charging Structure

This actor uses a pay-per-event pricing model rather than traditional compute-time billing. You are billed strictly for starting the run and for the items written to the platform dataset:

  • Actor Start (apify-actor-start): Flat fee of $0.005 per GB of memory allocated to the run, charged once when the run begins.
  • Dataset Item (apify-default-dataset-item): $0.005 per "result" event emitted to the default dataset.

Under the default volume tier (FREE), extracting 100 matched records costs $0.005 for the actor startup (assuming 1 GB memory allocation) plus $0.50 for the 100 dataset items emitted ($0.005 per result), totaling $0.505.

Volume-tier pricing automatically applies to dataset item charges as extraction volume scales:

  • FREE: $0.005 per result
  • BRONZE: $0.00433 per result
  • SILVER: $0.00367 per result
  • GOLD, PLATINUM, DIAMOND: $0.003 per result

Because every emitted item carries a direct per-event charge, broad queries without input constraints generate unnecessary costs. Constraining runs using parameters like costMin, costMax, gender, or activities reduces item count prior to emission, keeping costs directly aligned with relevant records.

Limitations of Directory Extraction

This tool extracts publicly available data hosted on the American Camp Association directory. It does not perform live seat availability checks or process booking forms. Furthermore, session-level arrays inside mode: "byProgramId" rely on camps self-reporting their calendars; if a camp has not yet published its upcoming season schedule to the ACA portal, the scraper returns the root program metadata without a sessions[] array.


The examples here were produced with ACA Camps Scraper. Its README lists the output fields, so you can check a response against the schema before you build on it.

Top comments (0)