# Skroutz Seller Contact Scraper — Email, Phone, GEMI (`memo23/skroutz-sellers-scraper`) Actor

Turn a Skroutz.gr keyword, product or shop URL into a lead list of the shops selling those products — legal name, seat address, GEMI number, AFM when published, phone and email from the shop card. Every competing offer, not just the cheapest. Greece. Pure HTTP. JSON or CSV.

- **URL**: https://apify.com/memo23/skroutz-sellers-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** E-commerce, Lead generation
- **Stats:** 2 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 seller scrapeds

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

## Skroutz Seller Contact Scraper — Email, Phone, GEMI

<p align="center">
  <img src="https://raw.githubusercontent.com/muhamed-didovic/muhamed-didovic.github.io/main/assets/logo-skroutz-sellers.png" alt="Skroutz Seller Contact Scraper" width="160" />
</p>

Turn a Skroutz.gr keyword, product page or shop URL into a list of the **shops** selling those products — legal name, seat address, GEMI number, phone and email from the published shop card.

One unique shop per row. Every competing offer, not just the cheapest. Pure HTTP. JSON or CSV.

<p align="center">
  <img src="https://raw.githubusercontent.com/muhamed-didovic/muhamed-didovic.github.io/main/assets/how-it-works-skroutz-sellers.png" alt="Skroutz keyword to shop contact rows" width="100%" />
</p>

### Why Use This Scraper?

- **Shops, not products.** Price scrapers return offers. This one returns the business behind the offer: the name, address and contact Skroutz publishes on the shop card.
- **Every offer.** A product sold by 40 shops produces up to 40 shop rows, not only the buy-box.
- **One row per shop.** The same shop on several products stays a single row.
- **Lead fields you can dial.** Email is restored from Skroutz's `[AT]` obfuscation. Phone, legal name, address and GEMI come from the same card.

### Overview

Skroutz is Greece's price-comparison marketplace. This actor walks search results or a product page, collects every shop on the offer list, then opens each shop card.

A row is a shop. `vatNumber` (ΑΦΜ) is filled only when the card publishes it. On a live `nivea` sample the cards had legal name, address, GEMI, email and phone, and no AFM.

### Supported Inputs

- **Search:** `searchQueries` such as `nivea`, or a URL `https://www.skroutz.gr/search?keyphrase=nivea`
- **Product:** `https://www.skroutz.gr/s/41193379/Nivea-Protect-Bronze.html`
- **Shop:** `https://www.skroutz.gr/shop/16006/Good-Time` or `.../info`

Other hosts are skipped. skroutz.cy, .ro, .bg and .de are not covered.

### Use Cases

| Who | What they get |
|---|---|
| Brand managers | Shops listing a product, with a phone and email |
| Agencies | A Greece marketplace lead list from a keyword |
| Distributors | Legal name, address and GEMI for shops already selling the brand |

### How It Works

1. A keyword search or a product URL is fetched.
2. Each product's offer list is read (`/s/{id}/shops_list`).
3. Each new shop card is opened (`/shop/{id}/{name}/info`).
4. One dataset row is saved per shop. A second sighting of the same shop id is skipped.
5. The run stops when the per-source shop cap is full.

### Input Configuration

| Field | What it does |
|---|---|
| `searchQueries` | Keywords. Prefill: `nivea`. |
| `startUrls` | Search, product or shop URLs on www.skroutz.gr. |
| `maxItems` | Unique shops **per** search or URL. Prefill: 50. Two queries at 50 can return 100. |
| `includeEmail` | Keep the published email and charge $0.015 when one is present. Default true. |
| `includePhone` | Keep the published phone and charge $0.015 when one is present. Default true. |

Keyword, 5 shops:

```json
{ "searchQueries": ["nivea"], "maxItems": 5 }
```

One product:

```json
{ "startUrls": [{ "url": "https://www.skroutz.gr/s/41193379/Nivea-Protect-Bronze.html" }], "maxItems": 20 }
```

### Output Overview

Each row is one shop. `email` and `phoneNumber` are the published contact. `businessName` is Επωνυμία. `registrationNumber` is the Γ.Ε.ΜΗ. number. `businessAddress` is the registered seat. `sourceProductIds` is the Skroutz product that led to the shop.

### Output Samples

From a `nivea` search on 22 Sep 2026. The first product's offer list had 40 shops; the run stopped at 5.

```json
{
  "sellerId": "16006",
  "marketplace": "skroutz.gr",
  "sellerName": "Good Time",
  "businessName": "TFM ΙΔΙΩΤΙΚΗ ΚΕΦΑΛΑΙΟΥΧΙΚΗ ΕΤΑΙΡΕΙΑ",
  "email": "info@goodtime.gr",
  "phoneNumber": "2107102220",
  "phoneNumbers": ["2107102220"],
  "registrationNumber": "160310303000",
  "vatNumber": null,
  "businessAddress": "Παπανδρέου Γεώργιου 4, Τ.Κ. 12462, Χαϊδάρι, Αττικής",
  "addressCountryCode": "GR",
  "sellerUrl": "https://www.skroutz.gr/shop/16006/Good-Time/info",
  "storefrontUrl": "https://www.skroutz.gr/shop/16006/Good-Time/products.html",
  "sourceProductIds": ["20320762"]
}
```

The other four rows in that run (Pharmacyfirst, Pharm16, AR Pharmacy, CareSpot) also had an email, a phone, a legal name, an address and a GEMI number.

### Pricing

| Event | When it is charged | Price |
|---|---|---|
| Shop row | Every shop saved | $0.004 |
| Published email | `includeEmail` is on and the card has an email | $0.015 |
| Published phone | `includePhone` is on and the card has a phone | $0.015 |
| Actor start | Once per run, per GB of memory | $0.005 |

A shop with both contact fields is $0.034 plus the start fee. A shop with neither contact field is $0.004. Turning a switch off drops that field and skips its charge. A second phone on the same card is not a second charge.

### Key Output Fields

- **Identity:** `sellerId`, `sellerName`, `marketplace`, `sellerUrl`, `storefrontUrl`
- **Legal:** `businessName`, `businessAddress`, `addressCountryCode`, `registrationNumber`, `vatNumber`
- **Contact:** `email`, `phoneNumber`, `phoneNumbers`
- **Provenance:** `sourceProductIds`, `sourceUrl`, `scrapedAt`

### FAQ

**Does every shop have an email?**
The five-shop `nivea` sample did. A shop that publishes none is still saved, and the email event is not charged.

**Where is the AFM?**
Only when the shop card prints ΑΦΜ. The sample cards published GEMI and not AFM, so `vatNumber` was null.

**Is this one row per product?**
No. One row per shop. Offers are how shops are discovered.

**Which URLs are skipped?**
Anything that is not www.skroutz.gr, and paths that are not a search, a `/s/{id}/` product, or a `/shop/{id}/` shop.

### Support

Open an issue on the actor's [Issues tab](https://console.apify.com/actors/N0v6JcPrTcBUeYbCb/issues).

### Additional Services

Need the same shop-row shape for another marketplace, or a scheduled pull on a brand? Open an issue or use the [memo23 store profile](https://apify.com/memo23).

### Explore More Scrapers

- [**Kaufland Seller Contact Scraper**](https://apify.com/memo23/kaufland-sellers-scraper) — the same one-row-per-seller pipeline for Kaufland (de, at, cz, sk, pl, fr, it)
- [**Amazon Seller Contact Scraper**](https://apify.com/memo23/amazon-sellers-scraper) — seller email, phone, VAT and ratings
- More actors on the [memo23 store profile](https://apify.com/memo23)

### 🤖 For AI Agents & LLM Apps

Compact reference for agents calling this actor (`memo23/skroutz-sellers-scraper`).

**Purpose:** Turns a Skroutz.gr keyword, product URL or shop URL into one row per shop with the published card (legal name, address, GEMI, phone, email).

**Minimal input:**

```json
{ "searchQueries": ["nivea"], "maxItems": 25 }
```

Product mode: `{ "startUrls": [{ "url": "https://www.skroutz.gr/s/41193379/Nivea-Protect-Bronze.html" }], "maxItems": 25 }`.

**Output fields:** sellerId, marketplace, sellerUrl, sellerName, storefrontUrl, businessName, registrationNumber, vatNumber, phoneNumber, phoneNumbers, email, businessAddress, addressCountryCode, sourceProductIds, sourceUrl, scrapedAt.

**Behaviors:**

- `maxItems` is per search or URL. Run total is that number times the number of sources.
- Duplicate shop ids collapse to one row.
- `vatNumber` stays null when the card has no AFM. GEMI is `registrationNumber`.
- `includeEmail` and `includePhone` default to true. Set either to false to drop that field and skip its charge.
- Billing: $0.004 per shop row, $0.015 when a kept email is present, $0.015 when a kept phone is present.
- Host: www.skroutz.gr only.

### ⚠️ Disclaimer

This Actor is an independent tool and is not affiliated with, endorsed by, or sponsored by Skroutz S.A. or any of its subsidiaries. All trademarks mentioned are the property of their respective owners.

The scraper accesses only publicly available Skroutz shop cards, search pages and offer lists — no authenticated endpoints, merchant-panel data, or content behind a login wall. Users are responsible for ensuring their use complies with Skroutz's Terms of Service, applicable data-protection law (GDPR, ePrivacy, CCPA), and any marketing / anti-spam rules that apply to how they contact the businesses they find. You are responsible for how you use the scraped data.

### SEO Keywords

skroutz scraper, skroutz.gr scraper, skroutz seller scraper, skroutz shop email, skroutz shop phone, skroutz GEMI, skroutz AFM, skroutz marketplace sellers, skroutz contact scraper, greek marketplace leads, skroutz supplier scraper, skroutz third-party shops, greece seller email scraper, B2B shop contact scraper, ecommerce seller intelligence greece, skroutz lead generation, skroutz JSON export, skroutz CSV export, skroutz shop address, skroutz merchant contact

# Actor input Schema

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

Keywords or brand names to search on skroutz.gr. The unique shops behind the returned products are then scraped. Valid values: non-empty strings. Example: \["nivea", "iphone"]. Default: none. Use Start URLs when you already have a page.

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

Direct skroutz.gr URLs, mixed freely with search queries. Accepted: a search URL, a product URL at /s/{id}/, or a shop URL at /shop/{id}/{name}. Other hosts are skipped. Example: \[{"url":"https://www.skroutz.gr/s/41193379/Nivea-Protect-Bronze.html"}]. Default: empty.

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

Maximum unique shops to scrape for EACH search query or start URL. Two searches with this set to 50 return up to 100 shops. Each product contributes every competing shop, not just the cheapest offer. Valid: integer ≥ 1. Example: 50. Prefill: 50.

## `includeEmail` (type: `boolean`):

When true, keep the shop-card email on the row and charge one seller-email event only if that email is present. When false, email is left empty and that event is not charged. Valid: true or false. Default: true.

## `includePhone` (type: `boolean`):

When true, keep the shop-card phone on the row and charge one seller-phone event only if a phone is present. A second number on the same card is not a second charge. When false, phone fields are left empty and that event is not charged. Valid: true or false. Default: true.

## Actor input object example

```json
{
  "searchQueries": [
    "nivea"
  ],
  "startUrls": [],
  "maxItems": 50,
  "includeEmail": true,
  "includePhone": true
}
```

# Actor output Schema

## `results` (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 = {
    "searchQueries": [
        "nivea"
    ],
    "startUrls": [],
    "maxItems": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/skroutz-sellers-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": ["nivea"],
    "startUrls": [],
    "maxItems": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("memo23/skroutz-sellers-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": [
    "nivea"
  ],
  "startUrls": [],
  "maxItems": 50
}' |
apify call memo23/skroutz-sellers-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,memo23/skroutz-sellers-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/N0v6JcPrTcBUeYbCb/builds/wGCk57hVKvCLPr4yG/openapi.json
