# TEDi Scraper — European Discount Variety Retail Products (`studio-amba/tedi-scraper`) Actor

Scrape products, prices, and categories from TEDi.com, the pan-European discount variety retail chain. Covers the six markets TEDi publishes an online catalogue for: Germany, Austria, Belgium, Czech Republic, Romania, and Slovakia.

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

## Pricing

from $1.20 / 1,000 result scrapeds

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/platform/actors/running/actors-in-store#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

## TEDi Scraper — Pan-European Discount Variety Retail Products & Prices

Pull TEDi's published product catalogue — names, prices, categories, and images — across the six countries TEDi runs an online assortment for, without opening the site yourself.

### What is TEDi?

TEDi is a German discount variety retailer with roughly 3,300 stores across more than 20 European countries — household goods, toys, stationery, party supplies, seasonal decor, and drugstore items at low, round-number prices.

TEDi does not run online checkout. What it does publish, on six of its country sites, is a browsable "Sortiment" (assortment) catalogue: every product on shelf, with a name, a spec line, a display price, and a photo. This scraper turns that catalogue into structured data.

Here's what people use it for:

- **Discount-retail price benchmarking** — TEDi's round-number pricing (mostly whole euros/currency units) sets a low-end reference point for household and variety goods. Compare it against Action, Flying Tiger, or your own catalogue.
- **Assortment tracking** — TEDi rotates seasonal and trend items. Scheduled runs catch what's new in a category before it's gone.
- **Cross-border comparison** — the same or similar product can be priced differently, and in a different currency, across Germany, Austria, Belgium, Czechia, Romania, and Slovakia.
- **Category-level market research** — TEDi's category structure (household, toys, stationery, party, drogerie/cosmetics, outdoor) is a useful cross-section of the discount-variety segment.

### Which countries does this cover?

TEDi's own site exposes 17 country/language links, but only **six** actually serve a working product catalogue — verified live, not assumed from the store count:

| Country | Code | Currency | Catalogue slug example |
|---|---|---|---|
| Germany | `DE` | EUR | `/sortiment/haushalt` |
| Austria | `AT` | EUR | `/at/sortiment/haushalt` |
| Belgium (German) | `BE` | EUR | `/be_de/sortiment/haushalt` |
| Czech Republic | `CZ` | CZK | `/cz/sortiment/domacnost` |
| Romania | `RO` | RON | `/ro/sortiment/gospodarie` |
| Slovakia | `SK` | EUR | `/sk/sortiment/domacnost` |

The remaining markets TEDi has physical stores in — Belgium (French), Bulgaria, England, Spain, France, Croatia, Hungary, Italy, Poland, Portugal, Slovenia — return a 404 on `/sortiment`. TEDi runs stores there with a store locator only, no online catalogue to scrape. This actor does not fabricate coverage for those markets.

### How to scrape TEDi data

The scraper supports four input modes.

#### Browse by category (recommended — full coverage of an area)

Pass a category slug, taken from the URL after `/sortiment/`. Slugs are locale-specific.

```json
{
    "country": "DE",
    "category": "haushalt",
    "maxResults": 200
}
```

The actor follows the site's own "load more" pagination (an AJAX call the category page's JavaScript makes) until it reaches the category's real product count or your `maxResults` cap.

#### Search by keyword (Germany, Austria, Belgium only)

```json
{
    "country": "AT",
    "searchQuery": "kissen",
    "maxResults": 20
}
```

TEDi's site search only exists on the German-language sites — verified live, the search endpoint 404s on the Czech, Romanian, and Slovak sites. On those three, a `searchQuery` input falls back to the country's default category and a warning is logged, instead of silently returning nothing.

Search returns a handful of top matches (Solr's grouped "quick results" view), not an exhaustive result set. Use Category browsing for full coverage of an area.

#### Full catalog scrape

Enable `scrapeFullCatalog` to crawl every top-level category for the selected country.

```json
{
    "country": "DE",
    "scrapeFullCatalog": true,
    "maxResults": 5000
}
```

Ignores `category`, `searchQuery`, and `startUrls`. Country catalogue sizes run roughly 800-1,500 products depending on market — set `maxResults` above that or the run stops at the cap.

#### Direct URLs

```json
{
    "startUrls": [
        { "url": "https://www.tedi.com/sortiment/haushalt" }
    ],
    "maxResults": 100
}
```

Each URL is classified by its content (category listing, product detail page, or search results page) rather than by guessing from the URL text — TEDi's product-detail URL segment is a different word per language (`detail` in German, `podrobnost` in Czech, `detaliu` in Romanian, `strana` in Slovak), so text matching alone would silently miss non-German URLs.

**Precedence:** `scrapeFullCatalog` > `searchQuery` > `category` > `startUrls`. Leaving everything empty defaults to the selected country's first discovered category, so an empty `{}` input still returns real data.

### What data does TEDi Scraper extract?

- **Product name**
- **Category** — as shown on the product tile or, on a direct detail-page fetch, the full breadcrumb path
- **Description** — the short spec/material line TEDi shows (size, material, colour), not long marketing copy
- **Price and currency** — TEDi's display price is always a whole number in the local currency (verified live across every category tested — the site's own CSS classes for one-digit and two-digit prices confirm there is no fractional/cents display), paired with the ISO 4217 currency code (`EUR`, `CZK`, or `RON`)
- **In stock** — always `null`. TEDi's Sortiment catalogue is a store-browse showcase for a chain with no online checkout, not a live webshop — it carries no stock/availability signal at all, online or per-store. This is never coerced to `true`/`false`.
- **Product ID** — TEDi's internal identifier where available, otherwise the URL slug
- **Image URL**
- **Country** — which of the six covered markets the item was scraped from
- **URL** — the product's detail page

Example output (Czech Republic, `CZK`):

```json
{
    "name": "Odpadkový koš",
    "category": "Domácnost",
    "description": "3 l, různé barvy, cena",
    "price": 200,
    "currency": "CZK",
    "inStock": null,
    "productId": "odpadkovy-kos-1",
    "imageUrl": "https://www.tedi.com/fileadmin/_processed_/6/1/csm_Muelleimer_1a31d22e69.jpg",
    "url": "https://www.tedi.com/cz/sortiment/podrobnost/odpadkovy-kos-1",
    "country": "CZ",
    "scrapedAt": "2026-08-22T11:10:14.358Z"
}
```

### How much does it cost?

TEDi Scraper uses pay-per-event pricing: $0.005 per Actor start plus $0.002 per result (per scraped product). Platform usage (compute) is included in these prices — this actor makes plain HTTP requests with no proxy or browser rendering, so per-result infrastructure cost is negligible.

| Scenario | Cost |
|---|---|
| 100 products (one category) | ~$0.21 |
| 500 products (multiple categories) | ~$1.01 |
| Full catalog, one country (~1,200 products) | ~$2.41 |

A run's usage cost only settles once the run reports SUCCEEDED — reading the dataset mid-run, before the run finishes, will undercount what it ends up costing. If a run fails, you are charged for the results it delivered before failing, not for the failed attempt itself.

### Can I integrate?

Apify integrates natively with the tools you already use:

- **Google Sheets** — push scraped data directly into a spreadsheet
- **Webhooks** — trigger your pipeline when a run finishes
- **Zapier / Make** — connect TEDi data to thousands of apps
- **Amazon S3 / Google Cloud Storage** — export datasets to cloud storage
- **PostgreSQL / MySQL** — store results in your own database

### Can I use it as an API?

Yes. Call TEDi Scraper programmatically from any language.

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")

run = client.actor("studio-amba/tedi-scraper").call(run_input={
    "country": "DE",
    "category": "haushalt",
    "maxResults": 100,
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(f"{item['name']} — {item['price']} {item['currency']}")
```

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_API_TOKEN' });

const run = await client.actor('studio-amba/tedi-scraper').call({
    country: 'DE',
    category: 'haushalt',
    maxResults: 100,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach(item => console.log(`${item.name} — ${item.price} ${item.currency}`));
```

### FAQ

**Does this scraper need a proxy?**
No. TEDi.com serves plain, unblocked HTTP responses with no bot-detection observed. The actor makes direct requests and paces them to respect the site's `robots.txt` crawl-delay guidance.

**Why don't you cover more than six countries?**
Because more than six don't have anything to scrape. TEDi's site exposes 17 country/language links, but only Germany, Austria, Belgium, Czechia, Romania, and Slovakia actually serve a product catalogue under `/sortiment/` — the rest return a 404 there. This was verified live before building, not assumed from TEDi's store count.

**Why is `inStock` always null?**
Because TEDi doesn't publish one. This is a browse catalogue for a physical-store chain, not a webshop with a cart — there is no online inventory signal to report, on any of the six sites.

**Does the price include cents?**
No, and that's not a scraping limitation — TEDi's own catalogue only ever displays whole-currency prices (5 €, 15 €, 200 Kč). Verified across every category tested.

**Can I scrape multiple countries in one run?**
The `country` field applies to a single run. To compare across countries, run the scraper once per country — orchestrate this with the Apify API or scheduler.

**Does the scraper handle pagination automatically?**
Yes, for category browsing and full-catalog mode. It replicates the site's own "load more" AJAX call until the category's real product count or your `maxResults` cap is reached.

### Limitations

- No online checkout on TEDi means no SKU/EAN barcode and no live stock signal — this is a catalogue, not a webshop feed.
- Site search only works on the German-language sites (Germany, Austria, Belgium) and returns a small top-matches set, not a full result list.
- Category slugs are locale-specific — a German slug like `haushalt` does not work on the Czech, Romanian, or Slovak sites, and vice versa. Browse the target country's site and copy the slug from a real category URL.
- Categories can be renamed or reorganized by TEDi at any time. A stale category slug logs a clear "not found" warning in the run log rather than failing silently.

### Other discount and variety retail scrapers

Looking for data from other European discount retailers? Check out these scrapers from our collection:

- [Action Scraper](https://apify.com/studio-amba/action-scraper) — Europe's fastest-growing non-food discount retailer
- [Douglas.de Scraper](https://apify.com/studio-amba/douglas-de-scraper) — Germany's #1 perfumery/health & beauty chain
- [Bipa Scraper](https://apify.com/studio-amba/bipa-scraper) — Austrian drugstore and beauty retailer
- [Etos Scraper](https://apify.com/studio-amba/etos-scraper) — Dutch drugstore and health & beauty chain

### Your feedback

Found a bug? Missing a field? Want another country added if TEDi ever launches a catalogue there? Open an issue on the actor's page or reach out through the Apify platform. We actively maintain this scraper and ship fixes fast.

# Actor input Schema

## `country` (type: `string`):

Which TEDi country site to scrape. TEDi runs ~3,300 stores across 20+ European countries, but only publishes an online product catalogue for six of them — verified live. The others (Belgium French, Bulgaria, England, Spain, France, Croatia, Hungary, Italy, Poland, Portugal, Slovenia) only have a store locator, no browsable catalogue.

## `category` (type: `string`):

TEDi category slug to browse, taken from the URL after /sortiment/ (e.g. "haushalt" for Household on the German site). Slugs are locale-specific — "haushalt" (DE/AT/BE), "domacnost" (CZ), "gospodarie" (RO), "domacnost" (SK). Browse tedi.com and copy the slug from a category URL. Subcategory paths also work (e.g. "accessoires/brillen"). Ignored when Search Query is filled in.

## `searchQuery` (type: `string`):

Search TEDi's site search for a keyword. Only works on the German-language sites (Germany, Austria, Belgium) — verified live, TEDi's search endpoint 404s on Czech/Romanian/Slovak. Returns a handful of top matches (Solr's grouped quick-results), not an exhaustive result set — use Category for full coverage of an area. Takes precedence over Category and Start URLs when set.

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

Direct TEDi URLs to scrape: a category page (https://www.tedi.com/sortiment/haushalt), a product detail page, or a search results page. Each URL is auto-detected by its content. Ignored when Search Query is filled in.

## `scrapeFullCatalog` (type: `boolean`):

Off by default, which scrapes only the single Category you set (or the country's first category). Turn this on to crawl every top-level category for the selected country instead and collect the full published assortment (roughly 800-1,500 products depending on country) — without it you get one category's worth of products, not the country's full range. Ignores Category, Search Query, and Start URLs. Set Max Results high enough (e.g. 5000) or the run stops at the cap.

## `maxResults` (type: `integer`):

Maximum number of products to return. Hard-capped at 20,000 regardless of input.

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

Proxy settings. TEDi.com serves plain, unblocked HTTP responses with no bot-detection observed — a proxy is not required, but you can enable one for extra reliability at scale.

## Actor input object example

```json
{
  "country": "DE",
  "category": "haushalt",
  "scrapeFullCatalog": false,
  "maxResults": 100,
  "proxyConfiguration": {
    "useApifyProxy": 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 = {
    "category": "haushalt",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("studio-amba/tedi-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 = {
    "category": "haushalt",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("studio-amba/tedi-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 '{
  "category": "haushalt",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call studio-amba/tedi-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,studio-amba/tedi-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/hGc1iVA452xTLe08f/builds/Rc8OAK7cg8PYcLl5g/openapi.json
