# eMAG.ro Scraper: Romania Prices & Stock (`reventadata/emag-scraper`) Actor

Search emag.ro or scrape any category: one row per product with price in lei, recommended price and discount, stock status, rating, review count and category path. Filter by price band, sort order, rating, discount, stock and exact words. Unofficial.

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

## Pricing

from $1.00 / 1,000 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?

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

## eMAG.ro Scraper: Products, Prices, Stock and Discounts

> **Unofficial.** This Actor is not affiliated with, authorised by, endorsed by, or in any way
> officially connected to eMAG. "eMAG" is a trademark of its respective owner and is used here only
> to describe what this tool reads. It reads the same public listing pages a browser does.

Search emag.ro for any term or scrape any category, and get one clean row per product: price in lei, recommended price and discount, rating, review count, stock status,
category path and badges.

### What does it do?

For each search term or listing URL it walks the result pages (60 products each), reads the product
cards the page serves, and saves one row per product. Any search term or category listing URL is
accepted; measured 2026-10-21 on TVs, phones and laptops.

### Why use it?

- **Numeric prices.** `price` is the current price in lei as a number. `listPrice` is the recommended
  retail price (PRP) printed on the card, `previousPrice` the struck-through price when the card shows
  one, and `discountPercent` the drop from it, or from the PRP when no struck-through price is shown.
- **Stock as data.** `availability` is `IN_STOCK`, `LIMITED_STOCK`, `IN_STOCK_SUPPLIER`, `PREORDER`, `OUT_OF_STOCK` or
  `SHOWROOM_ONLY` (`UNKNOWN` for a code not yet understood), with `inStock` true only when the product can be bought now.
- **Filters that run before you pay.** Price band (also sent to the site, so a band reaches products
  beyond the 100-page limit), sort order, minimum rating, minimum discount, in stock only, exact matches
  only, words the name or category must or must not contain.
- **Any listing.** Give search terms, or paste category and search URLs from emag.ro.

### How to use it

1. Put one or more terms in `searchQueries` (for example `televizor`), or listing URLs in `startUrls`.
2. Set `maxItems` and, if you want, a sort order and filters.
3. Run the Actor and download the dataset as JSON, CSV or Excel.

#### Input

| Field | Type | Description |
|---|---|---|
| `searchQueries` | array | Search terms, one search per term. |
| `startUrls` | array | emag.ro search or category URLs (`https://www.emag.ro/search/<term>` or a path ending `/c`). |
| `maxItems` | integer | Total products to save across all terms and URLs. Default 100. |
| `maxPagesPerListing` | integer | Pages per term or URL. Default 5, at most 100: the site serves no more. |
| `sort` | string | `RELEVANCE`, `PRICE_ASC`, `PRICE_DESC`, `REVIEWS`, `DISCOUNT` or `NEWEST`. |
| `minPrice`, `maxPrice` | number | Price band in lei. |
| `minRating` | number | Minimum average rating, 0 to 5. Unrated products are dropped when set. |
| `minDiscountPercent` | integer | Minimum drop from the PRP or struck-through price. Products showing neither are dropped when set. |
| `onlyInStock` | boolean | Keep only products that can be bought now (`IN_STOCK`, `LIMITED_STOCK` and `IN_STOCK_SUPPLIER`). |
| `exactMatchesOnly` | boolean | For search terms, keep only names containing every word of the term. |
| `includeKeywords`, `excludeKeywords` | array | Words the name or category must or must not contain, ignoring case and accents. |
| `proxyConfiguration` | object | Apify residential proxy in Romania, required: plain, datacenter and Unblocker requests get a captcha page (measured 2026-10-21). |

#### Sample input

```json
{
    "searchQueries": ["televizor"],
    "maxItems": 20,
    "maxPagesPerListing": 1,
    "sort": "PRICE_ASC",
    "onlyInStock": true
}
```

#### Output

One row per product. The sample is a real row from a run on 2026-10-21, with `scrapedAt` set to a fixed time.

```json
{
  "id": "84228664",
  "productId": "25343957",
  "sku": "DBFZX6BBM",
  "name": "Telefon mobil Apple iPhone 11, 64GB, Red",
  "url": "https://www.emag.ro/telefon-mobil-apple-iphone-11-64gb-red-mwlv2rm-a/pd/DBFZX6BBM/",
  "price": 1769,
  "currency": "RON",
  "listPrice": 1949,
  "previousPrice": null,
  "discountPercent": 9,
  "rating": 4.73,
  "reviewCount": 1639,
  "availability": "LIMITED_STOCK",
  "inStock": true,
  "stockLabel": "ultimul produs in stoc",
  "category": "Telefoane Mobile",
  "categoryTrail": [
    "Laptop, Tablete & Telefoane",
    "Telefoane mobile & accesorii",
    "Telefoane Mobile"
  ],
  "badges": [
    "Cadou"
  ],
  "hasGeniusBadge": true,
  "image": "https://s13emagst.akamaized.net/products/25344/25343943/images/res_2ec40f3f3324a4437f787a04070f3687.jpg?width=720&height=720&hash=1B37587619F2A5EF9F030051E74321FF",
  "position": 10,
  "matchedKeyword": "iphone",
  "page": 1,
  "scrapedAt": "2026-10-21T00:00:00+00:00"
}
```

### Good to know

- **The site's search is fuzzy.** A term can return loosely related products, and eMAG may answer a
  term with the products of one category (`iphone` returns 287 phones, other brands included). Turn on
  `exactMatchesOnly` to keep only names that contain every word.
- **A listing ends where the site's own result count ends.** Past the last real page eMAG silently
  serves a broader search; the Actor stops there instead of mixing the two. A listing is capped at 100
  pages (6,000 products): use a price band or a sort to reach further into a big category.
- **Card data only.** Rows carry what the listing card shows. Seller names and full specifications are
  on the product page and are not read.
- **Products repeat across pages.** The site shows some products on more than one page of a listing; each product is saved once, so a run may read more pages than `maxItems / 60`.
- **Prices change.** Each row is the price at `scrapedAt`.

### How fast is it?

Measured 2026-10-21: 1,000 `televizor` products in 91 seconds.

### Pricing

Pay-per-event: **$0.001 per result** saved to the dataset (**$1.00 per 1,000 results**), plus **$0.00005** charged once
when the run starts. No subscription. Filters run before a row is saved, so rows they drop are not charged.

# Actor input Schema

## `searchQueries` (type: `array`):

Search terms for emag.ro, one search per term (for example televizor, aspirator robot, iphone 16). Leave empty if you give listing URLs instead.

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

emag.ro category or search URLs to scrape, one per line. Only https://www.emag.ro/search/<term> and category paths ending in /c are accepted, for example https://www.emag.ro/laptopuri/c. Filters already in the URL are kept.

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

Total number of products to save across all search terms and URLs. Each result page holds up to 60 products.

## `maxPagesPerListing` (type: `integer`):

Result pages to walk per search term or URL. The site serves at most 100 pages (6,000 products) per listing; narrow a big category with a price band or a sort to reach further.

## `sort` (type: `string`):

Order of the results on the site. Applied through the site's own sort link for that listing.

## `minPrice` (type: `number`):

Keep only products priced at or above this amount in lei. Also narrows what the site returns, so a band can reach products beyond the 100-page limit. Leave empty for no minimum.

## `maxPrice` (type: `number`):

Keep only products priced at or below this amount in lei. Also narrows what the site returns. Leave empty for no maximum.

## `minRating` (type: `number`):

Keep only products with an average customer rating at or above this value (0 to 5). Products with no reviews are dropped when this is set.

## `minDiscountPercent` (type: `integer`):

Keep only products whose price is at least this many percent under the struck-through price or, when none is shown, the PRP printed on the card. Products showing neither are dropped when this is set.

## `onlyInStock` (type: `boolean`):

Keep only products that can be bought now: in stock or last units. Drops pre-orders, out-of-stock and showroom-only products.

## `exactMatchesOnly` (type: `boolean`):

For search terms, keep only products whose name contains every word of the term, ignoring case and accents. The site's search is fuzzy and returns loosely related products, even books for a nonsense term.

## `includeKeywords` (type: `array`):

Keep only products whose name or category contains every one of these words, ignoring case and accents.

## `excludeKeywords` (type: `array`):

Drop products whose name or category contains any of these words, ignoring case and accents.

## `proxyConfiguration` (type: `object`):

Apify residential proxy in Romania is required: plain, datacenter and Unblocker requests get a captcha page from eMAG.

## Actor input object example

```json
{
  "searchQueries": [
    "televizor"
  ],
  "maxItems": 20,
  "maxPagesPerListing": 1,
  "sort": "RELEVANCE",
  "onlyInStock": false,
  "exactMatchesOnly": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "RO"
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

Products scraped from emag.ro.

# 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 = {
    "searchQueries": [
        "televizor"
    ],
    "maxItems": 20,
    "maxPagesPerListing": 1,
    "sort": "RELEVANCE",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "RO"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("reventadata/emag-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 = {
    "searchQueries": ["televizor"],
    "maxItems": 20,
    "maxPagesPerListing": 1,
    "sort": "RELEVANCE",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "RO",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("reventadata/emag-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 '{
  "searchQueries": [
    "televizor"
  ],
  "maxItems": 20,
  "maxPagesPerListing": 1,
  "sort": "RELEVANCE",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "RO"
  }
}' |
apify call reventadata/emag-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,reventadata/emag-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/5e2q3R8CZw2PCMDbC/builds/4n03NLbfxwtiVpsfn/openapi.json
