# Allegro Scraper - Offers, Prices, Sellers & Reviews (`abotapi/allegro-pl-scraper`) Actor

Scrape Allegro by keyword or URL. Extract offers, prices, delivery, seller details, and product reviews. Includes incremental change monitoring, resume support, and MCP connectors for automated workflows.

- **URL**: https://apify.com/abotapi/allegro-pl-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 20.0% runs succeeded, 1 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $1.80 / 1,000 offer records

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

## Allegro Poland Marketplace Scraper

Extract offers from Allegro, Poland's #1 marketplace, into a clean Apify dataset. Search from your own phrases or paste Allegro listing links, then narrow the results by keyword. Every saved row carries the offer, price, delivery and seller fields the listing page exposes.

### Why This Scraper?

- Two clear modes: build searches from your own phrases, or paste Allegro listing links (`/listing?string=...`) and walk them forward.
- Useful offer fields in every row: title, price, currency, delivery text, offer URL, image, category and the offer's own parameters (colour, memory, screen size, ...).
- Seller signals in the same row: seller login, business/private flag, positive-feedback percentage and the Super Seller badge.
- Sponsored and Best-price-guarantee flags are returned as data, so you can keep or drop promoted cards yourself instead of guessing.
- Output limits and the keyword filter apply in both modes, so a pasted-URL run can still be narrowed.
- MCP connector export can send a concise offer summary into Notion, Linear, Airtable, or Apify connectors while the full record stays in the dataset.
- **Resume & recurring updates** - turn on Incremental mode to get only NEW, UPDATED, and REAPPEARED offers on every scheduled run, or use Resume from a previous run to continue one interrupted crawl. See "Incremental & Dedup Mode" below.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live offer.

| Field | Example |
| --- | --- |
| `offerId` | `10000000001` |
| `title` | `Sample Smartphone 8 GB / 128 GB 5G` |
| `url` | `https://allegro.pl/oferta/10000000001` |
| `price` | `1234.56` |
| `currency` | `PLN` |
| `originalPrice` | `1499` |
| `deliveryInfo` | `darmowa dostawa` |
| `image` | `https://a.allegroimg.com/s180/sample/sample-thumbnail` |
| `category` | `320000` |
| `parameters` | `{"Kolor": "czarny", "Wbudowana pamięć": "128 GB"}` |
| `sponsored` | `false` |
| `bestPriceGuarantee` | `true` |
| `sellerName` | `sample_seller` |
| `sellerBusiness` | `true` |
| `sellerFeedbackPercent` | `98.5` |
| `superSeller` | `false` |
| `sourceUrl` | `https://allegro.pl/listing?string=smartfon` |
| `sourceLabel` | `search-1:smartfon` |

### How to Use

Search one phrase:

```json
{
  "mode": "search",
  "queries": ["iphone"],
  "maxItems": 20
}
```

Search several phrases and keep only matching titles:

```json
{
  "mode": "search",
  "queries": ["iphone", "macbook"],
  "keywords": ["pro max", "etui"],
  "maxItems": 25
}
```

Paste Allegro listing links (a `&p=N` page number in the link is used as the starting page):

```json
{
  "mode": "url",
  "startUrls": [
    { "url": "https://allegro.pl/listing?string=laptop" },
    { "url": "https://allegro.pl/listing?string=laptop&p=2" }
  ],
  "maxItems": 10
}
```

Send an offer digest into an app connector:

```json
{
  "mode": "search",
  "queries": ["macbook"],
  "maxItems": 10,
  "mcpConnectors": ["YOUR_CONNECTOR_ID"],
  "notionParentPageUrl": "https://www.notion.so/your-page",
  "maxNotifyListings": 10
}
```

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | Choose `search` to build searches from your phrases, or `url` for pasted Allegro listing links. |
| `queries` | array | `["iphone"]` | Search phrases for search mode. Each phrase is one search walk. |
| `startUrls` | array | sample URL | Allegro listing/search URLs (`/listing?...`) for URL mode. A `&p=N` in the link is honoured as the first page read. Single offer links (`/oferta/...`, `/produkt/...`) are not a start source. |
| `keywords` | array | empty | Keeps only offers whose **title** contains at least one of these terms (case-insensitive). |
| `maxItems` | integer | `20` | Maximum matching offers to save across the whole run. Minimum 1; the cap is always finite. |
| `maxPages` | integer | `0` | Maximum result pages per phrase or pasted URL. `0` walks every result page; `maxItems` is the primary output cap. |
| `proxy` | object | Apify residential, country PL | Connection settings. Allegro serves pages to Polish/EU visitors, so keep the Residential (Poland) preset. |
| `mcpConnectors` | array | empty | Optional connector ids for sending a concise summary into your apps. |
| `notionParentPageUrl` | string | empty | Required only for Notion page export. |
| `maxNotifyListings` | integer | `50` | Maximum rows sent to each selected connector. Does not affect the dataset. |
| `resumeFromRunId` | string | empty | Optional previous run ID (or dataset ID) from this actor. Offers already collected there (matched by `offerId`) are skipped, so the run returns only new ones. For recurring daily monitoring of the same search, use `incrementalMode` instead. |
| `incrementalMode` | boolean | `false` | Daily/recurring monitoring of the same search. First run returns everything as `NEW`; later runs return only `NEW`/`UPDATED`/`REAPPEARED` by default. See below. |
| `stateKey` | string | empty | Optional name for a monitoring campaign, so its incremental state stays stable or is deliberately shared. Auto-derived from your mode/phrases/URLs/keywords when left empty. |
| `emitUnchanged` | boolean | `false` | Incremental mode only. Also return offers unchanged since the last run, marked `UNCHANGED`. Adds and bills extra rows you already have. |
| `emitExpired` | boolean | `false` | Incremental mode only. Also return offers from a previous run that are no longer found, marked `EXPIRED`, once a run has fully scanned the search (not capped, not a resume). Adds and bills extra synthetic rows. |

### Incremental & Dedup Mode

**Incremental mode** (`incrementalMode`) is for a schedule (for example, daily): the actor remembers the previous run of the *same* search by itself, so you never paste a run ID. The first run returns everything as `NEW`. Later runs return only `NEW`, `UPDATED`, and `REAPPEARED` offers by default; unchanged offers are suppressed (and not charged). Turn on `emitUnchanged` or `emitExpired` only when you also want those rows returned (and billed for). State is isolated per mode/phrases/URLs/keywords setup automatically; set `stateKey` to name or deliberately share a monitoring campaign.

This is a different tool from `resumeFromRunId`, which continues ONE specific interrupted run from a pasted run/dataset ID. Use `resumeFromRunId` after a run was cut off; use `incrementalMode` for a schedule that runs the same search again and again.

Every returned record in incremental mode also carries:

| Field | Meaning |
| --- | --- |
| `changeType` | One of `NEW`, `UPDATED`, `UNCHANGED`, `REAPPEARED`, `EXPIRED`. |
| `changedFields` | Names of the fields that changed since the last run (empty for `NEW`/`UNCHANGED`/`REAPPEARED`/`EXPIRED`). |
| `firstSeenAt` | When this offer was first seen by this monitoring campaign. |
| `lastSeenAt` | When this offer was last seen. |

`EXPIRED` rows are only produced when a run fully scans the tracked search end to end (no `maxItems` cap hit, no `resumeFromRunId`, every source read to its natural end) and `emitExpired` is on. A capped or resumed run leaves the previous state untouched for those offers rather than guessing they disappeared.

Example: run the same search every day and receive only changes:

```json
{ "mode": "search", "queries": ["iphone"], "incrementalMode": true }
```

### Output Example

> Sample shape: values are illustrative placeholders, not from a live offer.

```json
{
  "offerId": "10000000001",
  "title": "Sample Smartphone 8 GB / 128 GB 5G",
  "url": "https://allegro.pl/oferta/10000000001",
  "price": 1234.56,
  "currency": "PLN",
  "originalPrice": 1499,
  "deliveryInfo": "darmowa dostawa",
  "sponsored": false,
  "bestPriceGuarantee": true,
  "sellerName": "sample_seller",
  "sellerBusiness": true,
  "sellerFeedbackPercent": 98.5,
  "superSeller": false,
  "category": "320000",
  "parameters": {
    "Kolor": "czarny",
    "Pamięć RAM": "8 GB",
    "Wbudowana pamięć": "128 GB"
  },
  "image": "https://a.allegroimg.com/s180/sample/sample-thumbnail",
  "sourceUrl": "https://allegro.pl/listing?string=smartfon",
  "sourceLabel": "search-1:smartfon"
}
```

When a search matches nothing, the run saves a single explanatory row instead of an empty dataset:

```json
{
  "error": true,
  "code": "ZERO_RESULTS",
  "message": "No matching offers were found. Try a broader phrase or fewer filters."
}
```

### Send Results Into Your Apps

The optional MCP connector inputs let you send a condensed, human-readable summary of each selected offer into your connected apps. Authorize a connector under Apify > Settings > API & Integrations, select it in `mcpConnectors`, and set `notionParentPageUrl` when using Notion.

The connector export is a side output. It does not change the dataset, and connector write errors do not fail the run. The complete record always remains available in the Apify dataset.

### Plan Requirement

For best reliability, keep the default Apify Residential proxy with country Poland. `maxItems` is a maximum cap, not a guaranteed count; a run can save fewer rows when the phrase, URL and keyword filter match fewer active offers.

# Actor input Schema

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

Search mode builds Allegro listing searches from the Search phrases field. URL mode reads the Allegro URLs you paste in Start URLs.

## `queries` (type: `array`):

One or more search phrases. Each phrase is one search walk; every offer found is returned once.

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

Allegro listing/search links to start from, e.g. https://allegro.pl/listing?string=iphone (a \&p=N page number in the link is honoured). Forward-paged from where each link starts. Single offer links (/oferta/... or /produkt/...) are not a start source — use Search phrases or a listing link instead.

## `keywords` (type: `array`):

Only keep offers where at least one keyword appears in the offer title (case-insensitive). Leave empty to keep every offer the search returns.

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

Open each offer's own page to add full description, brand, SKU, category, detail price/currency and availability. Slower, and charged per enriched offer on top of the offer record.

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

Maximum offers to return across the whole run. 0 is not valid; the cap is always finite.

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

Maximum Allegro result pages to read per search phrase or pasted URL. Leave empty to walk every result page; max items is the primary output cap.

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

Paste a previous run ID (or dataset ID) from this actor to continue a large offer pull without returning or charging for offers already collected there (matched by offerId). Use this after an interrupted run. For recurring daily monitoring of the same search, use Incremental mode below instead.

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

Turn this on for daily or recurring monitoring. The first run returns all matching offers as NEW. Later runs normally return only NEW, UPDATED, and REAPPEARED offers. Turn on "Emit unchanged" or "Emit expired" only when you also want those offers returned (and billed). State is kept separately for each search/URL and filter setup; use State key when you want to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from a previous run above.

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

Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. Leave empty to let the actor derive a key automatically from the mode/search phrases/URLs/keywords: different searches then never mix state with each other.

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

Off by default. Turn on to also return offers that have not changed since the last run, marked UNCHANGED. This returns (and bills) extra rows you already have, so leave it off unless you specifically want the full snapshot every run.

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

Off by default. Turn on to also return offers that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search, not when Max items capped it or when Resume was used. This returns (and bills) extra synthetic rows, so leave it off unless you need expiry tracking.

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

Allegro serves pages only through a browser session on a Polish consumer-class exit, so the default is the Residential tier with country Poland.

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

Optionally send the scraped offers into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify > Settings > API & Integrations, then select it here. Notion gets a page-per-offer export; other connectors receive a best-effort write or digest. 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 offer pages are created. Required to enable the Notion export; ignored by other connectors.

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

Cap on how many offers are written to each selected connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "queries": [
    "iphone"
  ],
  "startUrls": [
    {
      "url": "https://allegro.pl/listing?string=iphone"
    }
  ],
  "keywords": [],
  "fetchDetails": false,
  "maxItems": 20,
  "maxPages": 0,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "PL"
  },
  "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",
    "queries": [
        "iphone"
    ],
    "startUrls": [
        {
            "url": "https://allegro.pl/listing?string=iphone"
        }
    ],
    "keywords": [],
    "fetchDetails": false,
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "PL"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/allegro-pl-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",
    "queries": ["iphone"],
    "startUrls": [{ "url": "https://allegro.pl/listing?string=iphone" }],
    "keywords": [],
    "fetchDetails": False,
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "PL",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/allegro-pl-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",
  "queries": [
    "iphone"
  ],
  "startUrls": [
    {
      "url": "https://allegro.pl/listing?string=iphone"
    }
  ],
  "keywords": [],
  "fetchDetails": false,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "PL"
  }
}' |
apify call abotapi/allegro-pl-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/allegro-pl-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/ypZbeyswclpdXHBz8/builds/2n6d1zQbkKPGq6Elb/openapi.json
