# ALDI AU Scraper - Groceries, Special Buys & Prices (`abotapi/aldi-com-au-scraper`) Actor

Scrape ALDI Australia (aldi.com.au) products: name, brand, price, was-price, savings, unit price, size, category, images, availability and Special Buys on-sale date, plus full detail (ingredients, allergens, nutrition). Search by keyword or category, filter by brand/theme, or paste any ALDI URL.

- **URL**: https://apify.com/abotapi/aldi-com-au-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** E-commerce, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 product results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## ALDI Australia Scraper

Scrape the ALDI Australia catalogue - groceries, weekly ranges, and Special Buys - with prices and full product detail. Pick a promotional range (Special Buys, Lower Prices, Super Savers) with one input, search by keyword or category, filter by brand and theme, or paste any ALDI product, category, or search link. Every record carries the product name, canonical title, brand, price, unit (comparison) price, numeric was-price with savings amount and percent when a product is on offer, selling size, category and full category path, image gallery, availability, and Special Buys on-sale date. Turn on detail enrichment for the full description, ingredients, allergens, nutritional claims, storage and usage instructions, and country of origin.

### Why this scraper

- Rich product records: 45+ fields per catalogue item, more with detail enrichment
- Specials / offers picker: select the **Special Buys (Limited Time Only)**, **Lower Prices**, or **Super Savers** range directly in search mode, or paste a specials-listing URL in url mode
- Four ways to collect: pick a specials range, search by keyword, list a category, or paste ALDI URLs
- Brand and theme filters, plus price and name ordering
- Numeric was-price plus savings amount and savings percent when a product is discounted, alongside the pre-formatted display strings
- Unit / comparison pricing ("$3.65 per 1 L") on every record
- Optional detail enrichment: description, ingredients, allergens, nutritional claims, storage and usage instructions, country of origin, and the complete image gallery
- Ready-to-download image URLs and a direct product URL on every record
- **Resume & recurring updates**: continue one interrupted crawl (`resumeFromRunId`), or run this actor on a schedule against the same search and get back only what's new/changed (`incrementalMode`)

### Data you get

> Sample shape; values are illustrative placeholders, not from a live listing.

| Field | Example |
|---|---|
| sku | `000000000000398949` (the canonical per-product key - use this to dedupe/join, not `abstractSku`) |
| abstractSku | `000000000000538441` (groups sibling variants that are still DIFFERENT products, e.g. different flavours/sizes at the same base item - deduping on this instead of `sku` silently merges distinct products) |
| name | `Full Cream Milk 2L` |
| title | `SAMPLE BRAND Full Cream Milk 2L` |
| brand | `SAMPLE BRAND` |
| price | `7.29` |
| priceDisplay | `$7.29` |
| wasPrice | `8.99` |
| wasPriceDisplay | `$8.99` |
| savingsAmount | `1.70` |
| savingsPercent | `18.9` |
| savingsDisplay | `Save $1.70` |
| promoLabel | `Save $1.70` |
| onSale | `true` |
| unitPrice | `3.65` |
| unitPriceDisplay | `$3.65 per 1 L` |
| unitOfMeasure | `L` |
| unitMeasure | `1L` |
| currency | `AUD` |
| sellingSize | `2 L` |
| category | `Milk` |
| categoryPath | `Dairy, Eggs & Fridge > Milk` |
| categories | `[{"id":"960000000","name":"Dairy, Eggs & Fridge","path":"dairy-eggs-fridge"}]` |
| isSpecialBuy | `false` (this product's card currently shows an active on-sale date - a different signal from `categoryPath` starting with "Limited Time Only", which is the product's home department and can be populated while `isSpecialBuy` stays `false`) |
| onSaleDateDisplay | `On Sale Sat 5 Jul` |
| images | `["https://.../product/jpg/scaleWidth/600/%3Chash%3E/full-cream-milk-2l"]` |
| imagesCount | `3` |
| notForSale | `false` |
| url | `https://www.aldi.com.au/product/full-cream-milk-2l-000000000000398949` |
| description | `Full product description appears here when fetchDetails is on.` |
| ingredients | `Full-cream milk.` |
| allergens | `["Milk"]` |
| nutritionalClaims | `Source of calcium` |
| storageInstructions | `Keep refrigerated.` |
| countryOrigin | `Australia` |
| detailFetched | `true` |
| changeType | `UPDATED` (incremental mode only) |
| changedFields | `["price", "wasPrice"]` (incremental mode only) |
| firstSeenAt | `2026-08-01T00:00:00Z` (incremental mode only) |
| lastSeenAt | `2026-08-03T00:00:00Z` (incremental mode only) |

### How to use

Search by keyword:

```json
{
  "mode": "search",
  "searchTerms": ["milk", "coffee"],
  "maxItems": 50
}
```

Scrape a specials / offers range directly:

```json
{
  "mode": "search",
  "specialsCategory": "special-buys",
  "maxItems": 100
}
```

List a category (including Special Buys) with detail enrichment:

```json
{
  "mode": "search",
  "categories": ["Special Buys", "Dairy, Eggs & Fridge"],
  "fetchDetails": true,
  "sortBy": "price_asc",
  "maxItems": 100
}
```

Filter a range by brand:

```json
{
  "mode": "search",
  "categories": ["Limited Time Only"],
  "brands": ["URBAN EATS"],
  "maxItems": 30
}
```

Paste URLs (product, category, or search):

```json
{
  "mode": "url",
  "urls": [
    "https://www.aldi.com.au/products/dairy-eggs-fridge/milk/k/1111111160",
    "https://www.aldi.com.au/product/expressi-milk-frother-000000000000545135"
  ],
  "fetchDetails": true
}
```

### Input options

- **mode** - `search` (keyword and/or category) or `url` (paste links)
- **searchTerms** - keywords to search the catalogue (search mode)
- **specialsCategory** - pick a promotional range in search mode: `special-buys` (Limited Time Only), `lower-prices`, or `super-savers`; leave empty for a normal run
- **categories** - category names, URL slugs, or numeric keys; accepts `Special Buys`, `Limited Time Only`, `Lower Prices`, `Super Savers`, `Dairy, Eggs & Fridge`, `meat-seafood/beef`, etc.
- **excludeCategories** - department names to drop from the OUTPUT, e.g. `Liquor`, `Limited Time Only`, `Front of Store`. Matched against each product's OWN reported department (its `categoryPath`), so it also catches products that a promo range like Lower Prices, Super Savers, or The People's Picks cross-lists from a department you never named in `categories` - see "Excluding departments" below
- **brands** - filter by brand name as shown on ALDI
- **themes** - filter by ALDI theme label (e.g. `Pantry Staples`)
- **sortBy** - `relevance` (default), `price_asc`, `price_desc`, or `name`
- **urls** - ALDI product, category, or search URLs (url mode)
- **fetchDetails** - add the full detail fields to every product (one extra request per product)
- **maxItems** - cap on products across all terms/categories/URLs (0 = unlimited)
- **maxPages** - optional safety cap on pages per term/category (60 products per page)
- **proxy** - proxy configuration; the default works on any Apify plan
- **resumeFromRunId** - continue one interrupted crawl from a previous run id of this actor
- **incrementalMode** - remember this search's baseline across scheduled runs and return only what changed
- **stateKey** - optional manual key for incremental mode's baseline (see below)
- **emitUnchanged** - also return every unchanged product every run (incremental mode)
- **emitExpired** - also return products that disappeared since last run (incremental mode)
- **mcpConnectors** - optional MCP connectors to export results into (Notion, Linear, Airtable, Apify)
- **notionParentPageUrl** - Notion page under which item pages are created (Notion connector only)
- **maxNotifyListings** - cap on items written to each connector per run; does not affect the dataset

### Resume & recurring updates

Two independent features cover different jobs:

- **Resume from run/dataset (`resumeFromRunId`)** - continues ONE specific crawl that was interrupted or capped by Max products. Paste the earlier run's id and this run skips every product id that run already collected, so you get the rest of the catalogue without duplicate rows or duplicate charges. If this run itself is resumed by the Apify platform (a migration or the Resurrect button), its own in-progress state always takes precedence over `resumeFromRunId` - the pasted id is ignored with a log message in that case.
- **Incremental mode (`incrementalMode`)** - for a search/category/URL set you run on a **schedule** (daily/weekly). The actor remembers its own baseline for that exact search (state key = your `stateKey`, or an automatic hash of mode + search terms + categories + specials range + brands + themes + sort + URLs + fetch-details detail toggle - two different searches never share a baseline). Each product is classified against that baseline:
  - **NEW** - not seen before
  - **UPDATED** - a comparable field changed (`changedFields` names exactly which)
  - **UNCHANGED** - identical to last run; suppressed (not returned, not billed) unless `emitUnchanged` is on
  - **REAPPEARED** - was previously marked EXPIRED and is back
  - **EXPIRED** - tracked before but not found this run, only emitted with `emitExpired` on, and only after a **complete** scan of every search term/category/URL in this run (a Max products cap, an unresolved category, an unrecognized URL, or `resumeFromRunId` all withhold EXPIRED detection for that run rather than risk a false positive - the log explains why whenever it's withheld)

`resumeFromRunId` + `incrementalMode` together bootstrap a new incremental baseline from the resumed crawl's ids (so those products don't look NEW again next scheduled run) - but only when incremental mode has no saved state for this search yet; if it already does, the run fails fast rather than silently mixing two different resume mechanisms.

Turning detail enrichment on/off, changing any filter, or changing the search terms/categories/URLs all count as a **different** search for incremental mode's automatic key - use `stateKey` if you deliberately want to share one baseline across a changing input.

### Specials, Special Buys, and pricing

ALDI Australia runs an everyday-low-price model: its promotional surface is the three promo ranges rather than a rotating half-price / clearance collection. Pick one with the **specialsCategory** input in search mode, or address it as a category:

- **Special Buys** (`special-buys`, a.k.a. Limited Time Only) - the weekly limited ranges, with the on-sale date captured
- **Lower Prices** (`lower-prices`)
- **Super Savers** (`super-savers`)

When a product is genuinely discounted, the record carries the numeric `wasPrice`, `savingsAmount`, and `savingsPercent` alongside the `wasPriceDisplay` / `savingsDisplay` strings and an `onSale` flag. Because ALDI prices are mostly everyday-low rather than strike-through, these was-price fields are populated only when the site actually shows a reduction.

The `categories` input accepts the top-level ranges and any nested grocery aisle. Grocery aisles can be given by name (`Dairy, Eggs & Fridge`), by slug (`dairy-eggs-fridge`, `dairy-eggs-fridge/milk`), or by the numeric key from a category URL. Category name matching ignores apostrophe/punctuation style, so `The People's Picks` and `The People’s Picks` both resolve.

**Lower Prices / Super Savers / Higher Protein Food and Drink report a large product count on ALDI's own site, but adding them to `categories` alongside your grocery aisles usually yields few *additional* rows.** They're cross-listing ranges, not separate departments — a discounted item is tagged with both its real aisle (e.g. `Dairy, Eggs & Fridge`) *and* the promo range, and the actor's within-run dedupe keeps only the first copy it sees. If your grocery aisles are scraped first, most promo-range products are already collected by the time their own category is reached. This is expected, not under-scraping — to find which of your results are currently discounted, filter on `onSale` / `wasPrice` on the record itself rather than on `categoryPath`. A per-category `reportedTotal` vs `newlyCollected` breakdown is written to `OUTPUT.scopes` every run if you want to see this directly.

Separately: a portion of ALDI's catalogue (seasonal/thematic merchandise — e.g. Oktoberfest, Father's Day Food, Storage and Home) is tagged only with a `theme`, not attached to the category tree at all, so it isn't reachable via `categories` no matter which department names you list. Most of it is non-grocery Special-Buys-style merchandise; a small slice is genuine grocery. There's currently no `themes`-only scrape mode (only a same-request narrowing filter) — ask if you want that added.

### Excluding departments (e.g. a grocery-only weekly scrape)

`categories`/`specialsCategory` are an **include** list - there's no way to scope Lower Prices, Super Savers, or The People's Picks to grocery aisles only at the source, because ALDI itself cross-lists products from any department (including Liquor and Limited Time Only) into those promotional ranges. A product returned under a `categories: ["Super Savers"]` run still reports its own true department in `categoryPath` (e.g. `Liquor > Wine`), independent of the range you queried it under.

Use **excludeCategories** to drop those by department after the fact, actor-side - it filters on each record's own `categoryPath`, not on your `categories` input, so it also removes cross-listed items you never explicitly queried:

```json
{
  "mode": "search",
  "categories": ["Fruits & Vegetables", "Pantry", "Dairy, Eggs & Fridge", "Lower Prices", "Super Savers"],
  "excludeCategories": ["Liquor", "Limited Time Only", "Front of Store"],
  "fetchDetails": true
}
```

Excluded products are never pushed to the dataset and never incur the detail-enrichment charge; the run summary (`OUTPUT.excluded_by_category`, and the `Done:` log line) reports how many were dropped this way.

### About product reviews

ALDI Australia does **not** publish product reviews or star ratings on aldi.com.au. This was verified empirically:

- No review or rating vendor (Bazaarvoice, Yotpo, PowerReviews, Feefo, Reevoo, or similar) is loaded anywhere on the product pages.
- The product listing and product-detail responses that power the storefront contain no `rating`, `reviewCount`, `averageRating`, or review-body fields.

Because the site does not expose reviews, this actor does not emit review fields. If ALDI adds reviews in future, this actor will be updated to capture them.

### Send results into your apps (MCP connectors)

Optionally pipe results into the apps you already use via Model Context Protocol (MCP) connectors - Notion, Linear, Airtable, or Apify. Authorize a connector once under Apify, Settings, Integrations, then select it in the input. The connector receives a condensed, human-readable summary per item; the full record always stays in the dataset.

### Output

Every product is pushed to the dataset as a flat JSON record with the fields above. Use the **Products Overview** dataset view for a quick table, or export to JSON, CSV, or Excel from the Apify Console.

### Notes

- Prices are in AUD. `price` and `unitPrice` are numeric dollars; the `*Display` fields carry the pre-formatted strings as shown on the site.
- Unit price basis: `unitOfMeasure` is the unit ALDI quotes the comparison price in (`g`, `kg`, `L`, `ml`, `each`, `sheets`, ...), and `unitMeasure` is that unit together with its reference quantity (`100g`, `1L`, `100each`). Both are parsed from the same `comparisonDisplay` string the site renders, and only when the string's price matches the numeric `unitPrice` exactly. Always read `unitPrice` together with `unitMeasure`, never alone: a `$2.38` unit price on a "$2.38 per 100 g" product is per 100 g, not per gram.
- Was-price and savings appear only when a product is currently discounted.
- The actor deduplicates by SKU within a run.
- Incremental mode's change detection compares **every** field, including `price`, `wasPrice`, `savingsAmount`/`savingsPercent`, `onSale`, and availability - these ARE the product on a grocery catalogue, so a price or stock change is exactly the change a recurring run exists to report; none of them are treated as noise.

# Actor input Schema

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

How to find products. 'search' builds listings from search terms and/or categories with filters; 'url' scrapes ALDI product, category or search URLs you paste.

## `searchTerms` (type: `array`):

Keywords to search the ALDI catalogue, e.g. 'milk', 'coffee', 'air fryer'. Each term is paginated fully (up to Max products). Leave empty to use categories instead.

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

Category names or URL slugs to list, e.g. 'Special Buys', 'Limited Time Only', 'Lower Prices', 'Super Savers', 'Dairy Eggs & Fridge', 'meat-seafood/beef'. Also accepts a raw numeric category key. Special Buys and weekly-specials ranges are categories here. Leave empty to use search terms instead.

## `specialsCategory` (type: `string`):

Scrape a promotional range directly (search mode). ALDI Australia's promo ranges are Special Buys (Limited Time Only), Lower Prices and Super Savers. Adds the chosen range to the run alongside any search terms or categories. To scrape a specials listing in url mode instead, paste its ALDI listing URL. Leave as 'No specials filter' for a normal catalogue run.

## `excludeCategories` (type: `array`):

Department names to drop from the OUTPUT, e.g. 'Liquor', 'Limited Time Only', 'Front of Store'. Matched against each product's OWN reported department (its categoryPath), not just against what you listed in Categories/Specials — this also catches products that promo ranges like Lower Prices, Super Savers or The People's Picks cross-list from an excluded department. Leave empty to keep everything.

## `brands` (type: `array`):

Filter by brand name as shown on ALDI, e.g. 'EXPRESSI', 'URBAN EATS'. Leave empty for all brands.

## `themes` (type: `array`):

Filter by ALDI theme label, e.g. 'Pantry Staples', 'Limited Time Only Meat'. Leave empty for all.

## `sortBy` (type: `string`):

Result ordering (search mode).

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

ALDI product, category or search page URLs to scrape (url mode). Category and search URLs return their full result set (up to Max products); product URLs return a single detail record. Filter fields are ignored in this mode.

## `fetchDetails` (type: `boolean`):

Add full product-detail fields to every result: long description, ingredients, allergens, nutritional claims, usage and storage instructions, country of origin and the complete image gallery. Adds one request per product.

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

Maximum number of products to scrape across all terms/categories/URLs. Set 0 for unlimited.

## `maxPages` (type: `integer`):

Optional safety cap on the number of result pages per search term or category (60 products per page). Leave empty for no page limit; the run stops at Max products.

## `resumeFromRunId` (type: `string`):

Paste a previous run id from THIS actor to continue one interrupted crawl: this run skips every product id that run already collected, so a capped or aborted crawl can pick up where it left off without duplicate pushes/charges. A separate feature from Incremental mode below — see its description for when to use each. Leave empty for a normal run.

## `incrementalMode` (type: `boolean`):

For a search/category/URL set you run on a schedule (daily/weekly): the actor remembers its own baseline for this exact search and returns only NEW / UPDATED / REAPPEARED / EXPIRED products next time, instead of the full catalogue again. Unlike Resume from run/dataset above (which continues ONE specific interrupted run), this remembers the search itself across separate scheduled runs. Off by default so a normal run's output is unchanged.

## `stateKey` (type: `string`):

Only used with Incremental mode. Leave empty to key the baseline automatically off this run's search terms/categories/specials/brands/themes/sort/URLs/fetchDetails (two different searches never share state). Set your own text here to deliberately share one baseline across otherwise-different inputs, or to isolate multiple monitoring campaigns that would otherwise hash to the same key.

## `emitUnchanged` (type: `boolean`):

Only used with Incremental mode. By default an UNCHANGED product (identical to last run) is NOT returned and NOT billed. Turn this on to also return every unchanged product every run — this returns and bills every tracked product, not just what changed.

## `emitExpired` (type: `boolean`):

Only used with Incremental mode. When on, a product tracked by a previous run that no longer appears after a COMPLETE scan of this search is returned once with changeType EXPIRED, then dropped from the baseline. Skipped (with a log message) on a Max products-capped, resumed, or otherwise partial run, since an incomplete scan cannot tell 'gone' apart from 'not reached yet'. This returns and bills the expired rows in addition to NEW/UPDATED ones.

## `proxy` (type: `object`):

Proxy used for requests. Any Apify proxy works; the default is fine for most runs. Runs on any Apify plan.

## `mcpConnectors` (type: `array`):

Optionally send the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify, Settings, Integrations, then select it here. The connector receives a condensed, human-readable summary per item (title + key fields), not the full JSON; the complete record stays in the dataset. Leave empty to skip. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "searchTerms": [
    "milk"
  ],
  "specialsCategory": "",
  "sortBy": "relevance",
  "urls": [
    "https://www.aldi.com.au/products/dairy-eggs-fridge/milk/k/1111111160"
  ],
  "fetchDetails": false,
  "maxItems": 20,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "proxy": {
    "useApifyProxy": true
  },
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (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 = {
    "mode": "search",
    "searchTerms": [
        "milk"
    ],
    "urls": [
        "https://www.aldi.com.au/products/dairy-eggs-fridge/milk/k/1111111160"
    ],
    "proxy": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/aldi-com-au-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 = {
    "mode": "search",
    "searchTerms": ["milk"],
    "urls": ["https://www.aldi.com.au/products/dairy-eggs-fridge/milk/k/1111111160"],
    "proxy": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/aldi-com-au-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 '{
  "mode": "search",
  "searchTerms": [
    "milk"
  ],
  "urls": [
    "https://www.aldi.com.au/products/dairy-eggs-fridge/milk/k/1111111160"
  ],
  "proxy": {
    "useApifyProxy": true
  }
}' |
apify call abotapi/aldi-com-au-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/aldi-com-au-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/rfbg3YFyGXtuzsSwt/builds/gvk43495gT66x4svb/openapi.json
