{
  "data": {
    "id": "jXxZGiZI21rMaBjWd",
    "actId": "OvQfJfLaxERNsd6Re",
    "userId": "7cjPEQWyDq9e8akXC",
    "startedAt": "2026-10-23T18:18:30.737Z",
    "finishedAt": "2026-10-23T18:18:54.243Z",
    "status": "SUCCEEDED",
    "meta": {
      "origin": "CLI"
    },
    "stats": {
      "durationMillis": 23402,
      "runTimeSecs": 23.402,
      "computeUnits": 0.02600222222222222,
      "imageSizeBytes": 2316462592
    },
    "options": {
      "useCache": true,
      "betaPackages": false,
      "memoryMbytes": 4096,
      "diskMbytes": 8192
    },
    "exitCode": 0,
    "inputSchema": "{\n  \"$schema\": \"https://apify.com/schemas/v1/input.ide.json\",\n  \"title\": \"Trendyol Product, Price & Review Scraper\",\n  \"description\": \"Scrape Trendyol.com products from categories, brands, stores, searches or product URLs. Optionally add product details with every seller's price, Trendyol's review summary (rating distribution, aspect sentiment, AI summary) and individual reviews.\",\n  \"type\": \"object\",\n  \"schemaVersion\": 1,\n  \"properties\": {\n    \"startUrls\": {\n      \"title\": \"Trendyol URLs\",\n      \"type\": \"array\",\n      \"description\": \"Category (e.g. https://www.trendyol.com/fritoz-x-c1055), brand, brand + category, store (/magaza/...-m-123), search (/sr?q=...) or product URLs (...-p-123). Filters and sorting already in the URL are kept.\",\n      \"editor\": \"requestListSources\",\n      \"prefill\": [\n        {\n          \"url\": \"https://www.trendyol.com/fritoz-x-c1055\"\n        }\n      ]\n    },\n    \"searchQueries\": {\n      \"title\": \"Search keywords\",\n      \"type\": \"array\",\n      \"description\": \"Keywords to search on Trendyol, one per line (e.g. \\\"airfryer\\\", \\\"kahve makinesi\\\").\",\n      \"editor\": \"stringList\",\n      \"prefill\": []\n    },\n    \"maxProductsPerSource\": {\n      \"title\": \"Max products per URL / keyword\",\n      \"type\": \"integer\",\n      \"description\": \"Stop collecting from each category, search or store after this many products. Trendyol shows 36 products per page.\",\n      \"minimum\": 1,\n      \"maximum\": 10000,\n      \"default\": 50\n    },\n    \"sortBy\": {\n      \"title\": \"Sort listings by\",\n      \"type\": \"string\",\n      \"description\": \"Sort order for keyword searches and URLs that don't already include one.\",\n      \"editor\": \"select\",\n      \"enum\": [\n        \"RELEVANCE\",\n        \"PRICE_ASC\",\n        \"PRICE_DESC\",\n        \"NEWEST\",\n        \"BEST_SELLER\",\n        \"MOST_FAVOURITE\",\n        \"MOST_RATED\"\n      ],\n      \"enumTitles\": [\n        \"Recommended (Trendyol default)\",\n        \"Lowest price\",\n        \"Highest price\",\n        \"Newest\",\n        \"Best sellers\",\n        \"Most favourited\",\n        \"Most reviewed\"\n      ],\n      \"default\": \"RELEVANCE\"\n    },\n    \"skipSponsored\": {\n      \"title\": \"Skip sponsored products\",\n      \"type\": \"boolean\",\n      \"description\": \"Leave out ads (sponsored cards) that Trendyol mixes into listings.\",\n      \"default\": false\n    },\n    \"includeProductDetails\": {\n      \"title\": \"Add product details (all sellers, attributes, variants)\",\n      \"type\": \"boolean\",\n      \"description\": \"Opens each product page: every seller's price and rating (buy-box analysis), barcode, attributes, sizes/variants with stock, exact favourite count, category path. Charged as an extra event per product.\",\n      \"default\": false,\n      \"sectionCaption\": \"Product details & reviews\"\n    },\n    \"includeReviewSummary\": {\n      \"title\": \"Add review summary\",\n      \"type\": \"boolean\",\n      \"description\": \"Adds Trendyol's review summary to each product: 1–5 star distribution, aspect sentiment (e.g. 'Kalite 94% positive'), size feedback and Trendyol's AI review summary. No extra charge.\",\n      \"default\": false\n    },\n    \"includeReviews\": {\n      \"title\": \"Scrape reviews\",\n      \"type\": \"boolean\",\n      \"description\": \"Saves individual reviews inside each product record as a nested reviews array, not separate dataset rows. Expand reviews for CSV/Excel; filter blank reviewId rows for a review-only sheet. Reviewer names, height and weight are not collected.\",\n      \"default\": false\n    },\n    \"maxReviewsPerProduct\": {\n      \"title\": \"Max reviews per product\",\n      \"type\": \"integer\",\n      \"description\": \"Upper limit of reviews saved per product.\",\n      \"minimum\": 1,\n      \"maximum\": 5000,\n      \"default\": 20\n    },\n    \"reviewSort\": {\n      \"title\": \"Review order\",\n      \"type\": \"string\",\n      \"description\": \"Which reviews come first.\",\n      \"editor\": \"select\",\n      \"enum\": [\n        \"RELEVANCE\",\n        \"NEWEST\"\n      ],\n      \"enumTitles\": [\n        \"Trendyol default (most relevant)\",\n        \"Newest first\"\n      ],\n      \"default\": \"RELEVANCE\"\n    },\n    \"priceMonitorKey\": {\n      \"title\": \"Price monitor name\",\n      \"type\": \"string\",\n      \"description\": \"Set a name (e.g. \\\"airfryer-watch\\\") and schedule the run. Each product then gets previousPrice, priceChange, priceChangePercent and isNewInMonitor compared with the last run that used the same name. Leave empty to disable. With \\\"Changes only\\\" on, this name also selects the changes-only snapshot (leave empty to derive it from your queries and URLs).\",\n      \"editor\": \"textfield\",\n      \"sectionCaption\": \"Price monitoring\"\n    },\n    \"changesOnly\": {\n      \"title\": \"Changes only (price & stock monitor)\",\n      \"type\": \"boolean\",\n      \"description\": \"Compare every product with the previous run that used the same input (or the same monitor name) and write only **new** products and products whose **price or stock changed** (each row gets a changeType: new, price_changed, stock_changed, variants_changed, unavailable, back_in_stock). Products that disappeared are written as free 'removed' rows. Unchanged products are skipped and not charged. Off (default) = normal full scrape. Schedule the Actor with this on to get a daily change feed.\",\n      \"default\": false\n    },\n    \"proxyConfiguration\": {\n      \"title\": \"Proxy\",\n      \"type\": \"object\",\n      \"description\": \"Default: no proxy. Requests use a real Chromium browser. A configured proxy is used only when explicitly supplied; automatic residential fallback is disabled by default.\",\n      \"editor\": \"proxy\",\n      \"prefill\": {\n        \"useApifyProxy\": false\n      },\n      \"default\": {\n        \"useApifyProxy\": false\n      },\n      \"sectionCaption\": \"Advanced\"\n    },\n    \"autoProxyFallback\": {\n      \"title\": \"Retry blocked pages with another connection\",\n      \"type\": \"boolean\",\n      \"description\": \"Optional, disabled by default. Explicitly enable to retry blocked pages once through Apify Proxy RESIDENTIAL (TR), or directly if an explicitly configured proxy was blocked.\",\n      \"default\": false\n    },\n    \"maxConcurrency\": {\n      \"title\": \"Max parallel requests\",\n      \"type\": \"integer\",\n      \"description\": \"Higher is faster but more likely to be rate-limited by Trendyol.\",\n      \"minimum\": 1,\n      \"maximum\": 16,\n      \"default\": 4\n    },\n    \"maxBrowserSessions\": {\n      \"title\": \"Browser sessions\",\n      \"type\": \"integer\",\n      \"description\": \"Number of parallel browser sessions, each with its own proxy IP.\",\n      \"minimum\": 1,\n      \"maximum\": 10,\n      \"default\": 3\n    }\n  }\n}",
    "readme": "# Trendyol Scraper – Prices, All Sellers & Reviews\n\nScrape **Trendyol.com**, Turkey's largest marketplace: products from categories, brands, stores, searches or product URLs, with the price customers actually see (including cart discounts), **every seller's offer for a product (buy-box view)**, attributes, sizes with stock, **Trendyol's own review analytics** (star distribution, aspect sentiment, AI summary) and individual reviews. **Price & stock monitoring** is built in: turn on *changes only*, schedule it, and get just the new, changed and removed products since the last run.\n\n- ✅ Category, brand, brand + category, store, search and product URLs\n- ✅ Real Turkish-lira prices: displayed price, seller price, struck-through price, cart-discount label (“Sepette %40 İndirim”), “lowest price in 30 days” label\n- ✅ All sellers of a product with price, seller score, stock, free shipping and buy-box winner\n- ✅ Review summary: 1–5 star distribution, aspects such as *Kalite 94% positive*, size feedback, Trendyol's AI summary\n- ✅ Reviews with rating, text, date, likes, seller, size/variant and photos — newest first if you want\n- ✅ Price & stock monitoring: optional **changes-only mode** returns only new, re-priced, sold-out, back-in-stock or removed products — unchanged ones are free\n- ✅ Pay only for what you get, with a hard cost cap\n\n## How it works\n\nTrendyol answers plain HTTP scrapers with a Cloudflare “Just a moment” challenge. This Actor uses a real Chromium browser **only as an HTTP client**: each session opens one Trendyol page, and every listing page, product page and review request is then fetched from inside that page. Images, styles and Trendyol's JavaScript bundles are never downloaded, so traffic stays close to that of a plain HTTP scraper (≈80 KB per page).\n\nSince version 1.0.7 the Actor runs **without a proxy by default**: requests are paced so Trendyol's rate limits are respected, and search and store listings are read through Trendyol's JSON search endpoint. When explicitly enabled, pages that Trendyol still blocks are retried once through Apify Proxy RESIDENTIAL (TR) — the optional **Retry blocked pages with another connection** fallback, off by default. You can also choose a proxy yourself; then each browser session gets its own proxy IP. If the review API keeps refusing, products are delivered without reviews (with a `reviewsError` explaining why) instead of being lost.\n\n## Input\n\nWhen a product page supplies a price, its `price`, `sellingPrice`, `originalPrice` and `discountPercent` replace the listing price snapshot together. A missing reference price or discount is returned as `null`, clearing any older listing discount. Listing-only `cartDiscountLabel` and `lowestPriceInDays` are also cleared because the detail price does not verify those labels. If the detail price is unavailable, all listing price fields and labels are retained together.\n\nFor the captured public Schafer product `345141997`, the detail-price fields are `{ \"price\": 3765, \"sellingPrice\": 3765, \"originalPrice\": null, \"discountPercent\": null, \"currency\": \"TRY\" }`. This is a historical fixture example, not a current price quote.\n\n| Field | What it does |\n| --- | --- |\n| **Trendyol URLs** | Category (`/fritoz-x-c1055`), brand (`/schafer-x-b373`), brand + category, store (`/magaza/...-m-123`), search (`/sr?q=...`) or product (`...-p-123`) URLs. Filters already in the URL are kept. |\n| **Search keywords** | Keywords searched on Trendyol. |\n| **Max products per URL / keyword** | 36 products per Trendyol page; the Actor paginates until this limit. |\n| **Sort listings by** | Recommended, lowest/highest price, newest, best sellers, most favourited, most reviewed. |\n| **Add product details** | Opens each product page: all sellers, barcode, attributes, variants, exact favourites, category path. |\n| **Add review summary** | Star distribution, aspect sentiment, size feedback, AI summary. |\n| **Scrape reviews / Max reviews per product / Review order** | Individual reviews, nested in the product. |\n| **Price monitor name** | Compare prices with the previous run that used the same name. |\n| **Changes only (price & stock monitor)** | Off by default. On: only new and changed products are saved and charged; see [Price & stock monitoring](#price--stock-monitoring). |\n| **Proxy** | Default: no proxy (fast and reliable for search, listings, product details and reviews). **Retry blocked pages with another connection** (off by default) can retry blocked pages once through the residential TR proxy when explicitly enabled. |\n\nExample – the 200 cheapest air fryers with sellers and the latest 30 reviews:\n\n```json\n{\n  \"startUrls\": [{ \"url\": \"https://www.trendyol.com/fritoz-x-c1055\" }],\n  \"sortBy\": \"PRICE_ASC\",\n  \"maxProductsPerSource\": 200,\n  \"includeProductDetails\": true,\n  \"includeReviewSummary\": true,\n  \"includeReviews\": true,\n  \"maxReviewsPerProduct\": 30,\n  \"reviewSort\": \"NEWEST\"\n}\n```\n\n## Output\n\nOne dataset item per product. Real record from a test run (shortened), with “Add product details” on:\n\n```json\n{\n  \"productId\": 1030831041,\n  \"name\": \"AIF 2030 XL Sıcak Tutma Hazneli Air Fryer 1500W Siyah-Krom\",\n  \"brand\": \"English Home\",\n  \"category\": \"Fritöz\",\n  \"url\": \"https://www.trendyol.com/english-home/aif-2030-xl-sicak-tutma-hazneli-air-fryer-1500w-siyah-krom-p-1030831041?merchantId=157151&boutiqueId=61\",\n  \"price\": 3203,\n  \"sellingPrice\": 3203,\n  \"originalPrice\": 3750,\n  \"discountPercent\": 15,\n  \"currency\": \"TRY\",\n  \"rating\": 4,\n  \"ratingCount\": 5,\n  \"favoriteCount\": 623,\n  \"position\": 36,\n  \"isSponsored\": false,\n  \"sellerName\": \"EKO-EKOL\",\n  \"sellerCount\": 3,\n  \"lowestSellerPrice\": 3203,\n  \"sellers\": [\n    { \"sellerName\": \"EKO-EKOL\", \"sellerScore\": 8.5, \"price\": 3203, \"originalPrice\": 3750, \"inStock\": true, \"freeShipping\": true, \"isBuyBoxWinner\": true },\n    { \"sellerName\": \"İkizler27 avm\", \"sellerScore\": 9, \"price\": 3333, \"inStock\": true, \"freeShipping\": true, \"isBuyBoxWinner\": false },\n    { \"sellerName\": \"AnkaBilgisayar\", \"sellerScore\": 7.9, \"price\": 4649, \"inStock\": true, \"freeShipping\": true, \"isBuyBoxWinner\": false }\n  ],\n  \"barcode\": \"8684233467834\",\n  \"categoryPath\": \"Elektronik > Elektrikli Ev Aletleri > Yiyecek & İçecek Hazırlama > Yiyecek Hazırlama > Fritöz\",\n  \"attributes\": { \"Frekans\": \"50 Hz / 60 Hz\", \"Materyal\": \"Plastik\", \"Garanti Tipi\": \"Resmi Distribütör Garantili\" },\n  \"scrapedAt\": \"2026-10-02T16:53:30.697Z\"\n}\n```\n\nWith “Add review summary” and “Scrape reviews” on, a product also gets (another real record, Schafer Thermochef XL, reviews newest first):\n\n```json\n{\n  \"productId\": 345141997,\n  \"reviewSummary\": {\n    \"averageRating\": 4.4,\n    \"ratingCount\": 4018,\n    \"commentCount\": 1406,\n    \"ratingDistribution\": { \"1\": 365, \"2\": 89, \"3\": 190, \"4\": 410, \"5\": 2964 },\n    \"aspects\": [\n      { \"name\": \"Fiyat\", \"mentions\": 160, \"positive\": 137, \"negative\": 23, \"positivePercent\": 86 },\n      { \"name\": \"Kalite\", \"mentions\": 67, \"positive\": 59, \"negative\": 4, \"positivePercent\": 94 }\n    ],\n    \"aiSummary\": [\"Ürünün pratik kullanımı ve zaman tasarrufu sağlaması, birçok yorumda öne çıkan bir avantajdır.\", \"...\"]\n  },\n  \"reviews\": [\n    {\n      \"reviewId\": 669674915,\n      \"reviewRating\": 5,\n      \"reviewText\": \"Çok güzel pişirmesi kullanımı çok iyi kargolamada iyi\",\n      \"reviewDate\": \"2026-10-02T08:07:59.879Z\",\n      \"reviewLikes\": 0,\n      \"reviewSellerName\": \"Schafer\",\n      \"reviewVariant\": null,\n      \"reviewLanguage\": \"tr\",\n      \"reviewMediaUrls\": [],\n      \"reviewTrusted\": true\n    }\n  ],\n  \"reviewsError\": null\n}\n```\n\nDataset views in Console: **Products**, **Reviews (expanded; may include products without reviews)** and **Price changes**. The dataset stores one item per product, with individual reviews inside `reviews[]` when requested; it does **not** emit separate `recordType: \"review\"` records. Review fields include `reviewId`, `reviewRating`, `reviewText` and `reviewDate`; the product title is `name` (not `title`). Fields only product pages provide are `null` when “Add product details” is off. A `RUN_SUMMARY` record in the key-value store lists counts, failed pages and the stop reason.\n\n### Export product prices and one row per review\n\nFor a product-price CSV or Excel export, select the **Products** view without unwinding `reviews`. For review expansion, `?view=reviews&format=csv` (or `format=xlsx`) uses the existing view; `?unwind=reviews&fields=productId,name,reviewId,reviewRating,reviewText,reviewDate,url&format=csv` is an explicit alternative. Apify merges each review into its parent product during unwind, so these review column names are top-level **only after expansion**.\n\nAn expanded view is not a review-only filter: items with a missing or null `reviews` field can remain as product rows with blank review columns. Do not count all exported rows as reviews. In Excel/Google Sheets, filter `reviewId` to nonblank values before saving a review-only sheet. Keep product-price data in a separate sheet; repeated product prices in expanded review rows are not separate offers.\n\nFor an exact local split, download the existing dataset as **JSON without a view or unwind**, save it as `products.json`, and run this Python 3 script in the same directory. It writes `product-prices.csv` and `reviews.csv`, preserving Unicode, quotes and commas. No Actor run, API call or credentials are needed for this local conversion. Missing, null and empty review arrays produce zero review rows. Removed-product monitoring records (`changeType: \"removed\"`) are excluded from the current-price CSV; retain the raw JSON if you need those events.\n\n```python\nimport csv\nimport json\nfrom pathlib import Path\n\nitems = json.loads(Path(\"products.json\").read_text(encoding=\"utf-8-sig\"))\nprice_fields = [\"productId\", \"name\", \"price\", \"sellingPrice\", \"originalPrice\", \"currency\", \"url\", \"scrapedAt\"]\nreview_fields = [\"productId\", \"name\", \"reviewId\", \"reviewRating\", \"reviewText\", \"reviewDate\", \"reviewLikes\", \"reviewSellerName\", \"reviewVariant\", \"url\"]\nproducts = [p for p in items if p.get(\"productId\") is not None and p.get(\"changeType\") != \"removed\"]\nreviews = [dict(p, **r) for p in products for r in (p.get(\"reviews\") or [])]\nfor filename, fields, rows in [\n    (\"product-prices.csv\", price_fields, products),\n    (\"reviews.csv\", review_fields, reviews),\n]:\n    with open(filename, \"w\", encoding=\"utf-8-sig\", newline=\"\") as out:\n        writer = csv.DictWriter(out, fieldnames=fields, extrasaction=\"ignore\")\n        writer.writeheader()\n        writer.writerows(rows)\n```\n\n**Pagination and limits:** Dataset API `offset` and `limit` count stored product items, not expanded review rows. For example, `limit=1&unwind=reviews` can expand to several reviews from one product. When paging, advance the offset by the product-item page size, not the expanded row count. The Actor's `maxReviewsPerProduct` caps collection per product; `reviewSort` controls review order. A dataset may contain fewer reviews than requested: products can have no reviews, the cost limit can trim the saved array, or the review API can remain rate-limited. On that last condition the product is retained with `reviews: []` and `reviewsError`; a product 404 is skipped and counted in `RUN_SUMMARY`. An empty review sheet alone does not prove successful collection: inspect `reviewsError` and `RUN_SUMMARY`.\n\nReferences: [Dataset schema transformations](https://docs.apify.com/storage/dataset-schema), [Dataset items API and pagination](https://docs.apify.com/api/v2/dataset-items-get).\n\n**Price fields:** `price` is what Trendyol displays (including cart promotions; listings sort by it), `sellingPrice` is the seller's price before cart promotions, `originalPrice` is the crossed-out price.\n\n## Status rows (no failed runs for bad inputs)\n\nInputs that produce no products don't fail the run. Instead you get a free row with `\"type\": \"status\"`:\n\n| `status` | Meaning |\n| --- | --- |\n| `invalid_input` | No URL or keyword was given |\n| `unsupported_url` | Not a Trendyol listing/product URL (e.g. the home page or another site) |\n| `not_found` | Trendyol returned 404 (product removed, wrong URL) |\n| `blocked` | Trendyol kept showing a Cloudflare challenge, even after the automatic retry with another connection; set the proxy to RESIDENTIAL, country TR |\n| `failed` | The page failed after retries; the `message` says why |\n\nFilter them out with `type != \"status\"`, or use the *Products* view. They are never charged.\n\n## Pricing\n\nPay per event — platform and proxy costs are included:\n\n| Event | Charged when |\n| --- | --- |\n| Product | a product is saved |\n| Product details | a product is saved with “Add product details” on |\n| Review | each saved review |\n\nThe review summary has no extra charge. Set **Maximum cost per run** in the run options: the Actor checks the remaining budget before each product and stops cleanly, so you are never charged for data you don't receive.\n\n## Price & stock monitoring\n\nTwo ways to follow prices over time:\n\n**Changes only (recommended for schedules).** Turn on **Changes only** and schedule the Actor. Every run is compared with the previous run of the same input (or of the same **Price monitor name**, if you set one), and only what changed is saved:\n\n| `changeType` | Meaning | Charged |\n| --- | --- | --- |\n| `new` | first time this product is seen (every product on the first run) | yes, as a normal product |\n| `price_changed` | price went up or down (`previousPrice`, `priceChange`, `priceChangePercent`) | yes |\n| `unavailable` | still listed but sold out — temporarily unavailable | yes |\n| `back_in_stock` | sold out last time, buyable again | yes |\n| `stock_changed` / `variants_changed` | a size/variant sold out, came back, appeared or disappeared (needs **Add product details**) | yes |\n| `removed` | product is gone: its page returns 404 (`removedReason: not_found`), or it was missing from a fully crawled listing two runs in a row (`not_listed`) | **free** |\n| unchanged | not saved | **free** |\n\nEach changed row also has a `changes` list (e.g. `{\"field\": \"price\", \"from\": 1299, \"to\": 1149}`), `previousInStock`, `previousSeenAt` and `firstSeenAt`. Products are matched by Trendyol's stable product ID (`productId`), so renamed products are not reported as new. Open the **Changes** view, or connect a webhook / Google Sheets / Slack integration to get only the changes. With details or reviews on, they are only saved (and charged) for changed rows.\n\nSchedule example — check 300 air fryers every morning and get only the price and stock changes:\n\n```json\n{\n  \"startUrls\": [{ \"url\": \"https://www.trendyol.com/fritoz-x-c1055\" }],\n  \"maxProductsPerSource\": 300,\n  \"changesOnly\": true,\n  \"priceMonitorKey\": \"airfryer-watch\"\n}\n```\n\nSave it as a task, then in Apify Console choose **Schedules → Create new**, cron `0 7 * * *` (07:00 every day), and add the task. Keep the input the same between runs: a different input or monitor name starts a new watch list (all products `new` again).\n\n**Price fields on every row.** Without changes-only, set **Price monitor name**, e.g. `airfryer-watch`, and schedule the Actor with the same input. Each product gets `previousPrice`, `priceChange`, `priceChangePercent` and `isNewInMonitor`. Open the **Price changes** view or filter on `priceChange != 0`.\n\n## Tips\n\n- Start with 20–50 products to check the fields you need, then scale.\n- Listing-only runs are the cheapest and fastest; turn on details or reviews only when you need them.\n- The default uses no proxy and disables automatic residential fallback. Search and store pages are read through Trendyol's JSON search endpoint, which is fast and rarely challenged. If a run reports many `blocked` rows, set the proxy to RESIDENTIAL, country TR.\n- Many reviews per product take longer: the review API is rate-limited per IP.\n\n## Limitations\n\n- Product descriptions (the HTML “Ürün Açıklaması” block) are not included — Trendyol loads them separately.\n- Sizes/variants include stock and barcode but not a separate price per size (Trendyol prices the selected offer).\n- Trendyol can change its pages at any time. The Actor fails loudly (no silent empty results) and is monitored with tests against captured pages.\n- Only the Turkish storefront (TRY) is supported; `/en/` URLs are mapped to it.\n\n## Is it legal to scrape Trendyol?\n\nThis Actor collects publicly visible product, price and review data and does not log in. Reviewer names, height and weight, and sellers' tax and address details are deliberately **not** collected. Trendyol's robots.txt disallows search-result URLs (`/sr?...`); category, brand and product pages are a better fit where that matters to you. You are responsible for using the data in line with Trendyol's terms, the Turkish personal-data law (KVKK) and other laws that apply to you. This is not legal advice.\n\n## FAQ\n\n**Why are some reviews missing?** `maxReviewsPerProduct` caps them; and if Trendyol rate-limits its review API repeatedly, products are delivered with `reviewsError` set instead of reviews.\n\n**Why does a product appear only once when it matches two keywords?** Products are de-duplicated across all inputs in a run; `sourceInput` shows where it was found first.\n\n**Can I get a specific seller's offer?** Use a product URL with `?merchantId=...` — it is kept in `url`.\n\n**Found a problem?** Open an issue on the Actor's Issues tab with the run ID and the URL.\n\n\n## Use cases\n\n- Monitor Trendyol prices, discounts and stock on a schedule.\n- Compare every seller offer, not only the buy box.\n- Export a category, brand or store catalogue to JSON, CSV or Excel.\n- Collect reviews only when you turn them on, so a price run stays cheaper.\n",
    "actorDefinition": {
      "$schema": "https://apify.com/schemas/v1/actor.ide.json",
      "actorSpecification": 1,
      "name": "trendyol-scraper",
      "title": "Trendyol Scraper – Prices, All Sellers & Reviews",
      "description": "Scrape Trendyol.com products from categories, brands, stores, searches or product URLs: prices with cart discounts, every seller's offer (buy-box), attributes, variants, Trendyol's review summary with aspect sentiment, and reviews. Built-in price-change monitor.",
      "version": "1.0",
      "buildTag": "latest",
      "meta": {
        "templateId": "js-crawlee-playwright-chrome",
        "generatedBy": "Claude (claude-opus-5-5)"
      },
      "defaultMemoryMbytes": 1024,
      "minMemoryMbytes": 1024,
      "maxMemoryMbytes": 8192,
      "dockerfile": "../Dockerfile",
      "readme": "# Trendyol Scraper – Prices, All Sellers & Reviews\n\nScrape **Trendyol.com**, Turkey's largest marketplace: products from categories, brands, stores, searches or product URLs, with the price customers actually see (including cart discounts), **every seller's offer for a product (buy-box view)**, attributes, sizes with stock, **Trendyol's own review analytics** (star distribution, aspect sentiment, AI summary) and individual reviews. **Price & stock monitoring** is built in: turn on *changes only*, schedule it, and get just the new, changed and removed products since the last run.\n\n- ✅ Category, brand, brand + category, store, search and product URLs\n- ✅ Real Turkish-lira prices: displayed price, seller price, struck-through price, cart-discount label (“Sepette %40 İndirim”), “lowest price in 30 days” label\n- ✅ All sellers of a product with price, seller score, stock, free shipping and buy-box winner\n- ✅ Review summary: 1–5 star distribution, aspects such as *Kalite 94% positive*, size feedback, Trendyol's AI summary\n- ✅ Reviews with rating, text, date, likes, seller, size/variant and photos — newest first if you want\n- ✅ Price & stock monitoring: optional **changes-only mode** returns only new, re-priced, sold-out, back-in-stock or removed products — unchanged ones are free\n- ✅ Pay only for what you get, with a hard cost cap\n\n## How it works\n\nTrendyol answers plain HTTP scrapers with a Cloudflare “Just a moment” challenge. This Actor uses a real Chromium browser **only as an HTTP client**: each session opens one Trendyol page, and every listing page, product page and review request is then fetched from inside that page. Images, styles and Trendyol's JavaScript bundles are never downloaded, so traffic stays close to that of a plain HTTP scraper (≈80 KB per page).\n\nSince version 1.0.7 the Actor runs **without a proxy by default**: requests are paced so Trendyol's rate limits are respected, and search and store listings are read through Trendyol's JSON search endpoint. When explicitly enabled, pages that Trendyol still blocks are retried once through Apify Proxy RESIDENTIAL (TR) — the optional **Retry blocked pages with another connection** fallback, off by default. You can also choose a proxy yourself; then each browser session gets its own proxy IP. If the review API keeps refusing, products are delivered without reviews (with a `reviewsError` explaining why) instead of being lost.\n\n## Input\n\nWhen a product page supplies a price, its `price`, `sellingPrice`, `originalPrice` and `discountPercent` replace the listing price snapshot together. A missing reference price or discount is returned as `null`, clearing any older listing discount. Listing-only `cartDiscountLabel` and `lowestPriceInDays` are also cleared because the detail price does not verify those labels. If the detail price is unavailable, all listing price fields and labels are retained together.\n\nFor the captured public Schafer product `345141997`, the detail-price fields are `{ \"price\": 3765, \"sellingPrice\": 3765, \"originalPrice\": null, \"discountPercent\": null, \"currency\": \"TRY\" }`. This is a historical fixture example, not a current price quote.\n\n| Field | What it does |\n| --- | --- |\n| **Trendyol URLs** | Category (`/fritoz-x-c1055`), brand (`/schafer-x-b373`), brand + category, store (`/magaza/...-m-123`), search (`/sr?q=...`) or product (`...-p-123`) URLs. Filters already in the URL are kept. |\n| **Search keywords** | Keywords searched on Trendyol. |\n| **Max products per URL / keyword** | 36 products per Trendyol page; the Actor paginates until this limit. |\n| **Sort listings by** | Recommended, lowest/highest price, newest, best sellers, most favourited, most reviewed. |\n| **Add product details** | Opens each product page: all sellers, barcode, attributes, variants, exact favourites, category path. |\n| **Add review summary** | Star distribution, aspect sentiment, size feedback, AI summary. |\n| **Scrape reviews / Max reviews per product / Review order** | Individual reviews, nested in the product. |\n| **Price monitor name** | Compare prices with the previous run that used the same name. |\n| **Changes only (price & stock monitor)** | Off by default. On: only new and changed products are saved and charged; see [Price & stock monitoring](#price--stock-monitoring). |\n| **Proxy** | Default: no proxy (fast and reliable for search, listings, product details and reviews). **Retry blocked pages with another connection** (off by default) can retry blocked pages once through the residential TR proxy when explicitly enabled. |\n\nExample – the 200 cheapest air fryers with sellers and the latest 30 reviews:\n\n```json\n{\n  \"startUrls\": [{ \"url\": \"https://www.trendyol.com/fritoz-x-c1055\" }],\n  \"sortBy\": \"PRICE_ASC\",\n  \"maxProductsPerSource\": 200,\n  \"includeProductDetails\": true,\n  \"includeReviewSummary\": true,\n  \"includeReviews\": true,\n  \"maxReviewsPerProduct\": 30,\n  \"reviewSort\": \"NEWEST\"\n}\n```\n\n## Output\n\nOne dataset item per product. Real record from a test run (shortened), with “Add product details” on:\n\n```json\n{\n  \"productId\": 1030831041,\n  \"name\": \"AIF 2030 XL Sıcak Tutma Hazneli Air Fryer 1500W Siyah-Krom\",\n  \"brand\": \"English Home\",\n  \"category\": \"Fritöz\",\n  \"url\": \"https://www.trendyol.com/english-home/aif-2030-xl-sicak-tutma-hazneli-air-fryer-1500w-siyah-krom-p-1030831041?merchantId=157151&boutiqueId=61\",\n  \"price\": 3203,\n  \"sellingPrice\": 3203,\n  \"originalPrice\": 3750,\n  \"discountPercent\": 15,\n  \"currency\": \"TRY\",\n  \"rating\": 4,\n  \"ratingCount\": 5,\n  \"favoriteCount\": 623,\n  \"position\": 36,\n  \"isSponsored\": false,\n  \"sellerName\": \"EKO-EKOL\",\n  \"sellerCount\": 3,\n  \"lowestSellerPrice\": 3203,\n  \"sellers\": [\n    { \"sellerName\": \"EKO-EKOL\", \"sellerScore\": 8.5, \"price\": 3203, \"originalPrice\": 3750, \"inStock\": true, \"freeShipping\": true, \"isBuyBoxWinner\": true },\n    { \"sellerName\": \"İkizler27 avm\", \"sellerScore\": 9, \"price\": 3333, \"inStock\": true, \"freeShipping\": true, \"isBuyBoxWinner\": false },\n    { \"sellerName\": \"AnkaBilgisayar\", \"sellerScore\": 7.9, \"price\": 4649, \"inStock\": true, \"freeShipping\": true, \"isBuyBoxWinner\": false }\n  ],\n  \"barcode\": \"8684233467834\",\n  \"categoryPath\": \"Elektronik > Elektrikli Ev Aletleri > Yiyecek & İçecek Hazırlama > Yiyecek Hazırlama > Fritöz\",\n  \"attributes\": { \"Frekans\": \"50 Hz / 60 Hz\", \"Materyal\": \"Plastik\", \"Garanti Tipi\": \"Resmi Distribütör Garantili\" },\n  \"scrapedAt\": \"2026-10-02T16:53:30.697Z\"\n}\n```\n\nWith “Add review summary” and “Scrape reviews” on, a product also gets (another real record, Schafer Thermochef XL, reviews newest first):\n\n```json\n{\n  \"productId\": 345141997,\n  \"reviewSummary\": {\n    \"averageRating\": 4.4,\n    \"ratingCount\": 4018,\n    \"commentCount\": 1406,\n    \"ratingDistribution\": { \"1\": 365, \"2\": 89, \"3\": 190, \"4\": 410, \"5\": 2964 },\n    \"aspects\": [\n      { \"name\": \"Fiyat\", \"mentions\": 160, \"positive\": 137, \"negative\": 23, \"positivePercent\": 86 },\n      { \"name\": \"Kalite\", \"mentions\": 67, \"positive\": 59, \"negative\": 4, \"positivePercent\": 94 }\n    ],\n    \"aiSummary\": [\"Ürünün pratik kullanımı ve zaman tasarrufu sağlaması, birçok yorumda öne çıkan bir avantajdır.\", \"...\"]\n  },\n  \"reviews\": [\n    {\n      \"reviewId\": 669674915,\n      \"reviewRating\": 5,\n      \"reviewText\": \"Çok güzel pişirmesi kullanımı çok iyi kargolamada iyi\",\n      \"reviewDate\": \"2026-10-02T08:07:59.879Z\",\n      \"reviewLikes\": 0,\n      \"reviewSellerName\": \"Schafer\",\n      \"reviewVariant\": null,\n      \"reviewLanguage\": \"tr\",\n      \"reviewMediaUrls\": [],\n      \"reviewTrusted\": true\n    }\n  ],\n  \"reviewsError\": null\n}\n```\n\nDataset views in Console: **Products**, **Reviews (expanded; may include products without reviews)** and **Price changes**. The dataset stores one item per product, with individual reviews inside `reviews[]` when requested; it does **not** emit separate `recordType: \"review\"` records. Review fields include `reviewId`, `reviewRating`, `reviewText` and `reviewDate`; the product title is `name` (not `title`). Fields only product pages provide are `null` when “Add product details” is off. A `RUN_SUMMARY` record in the key-value store lists counts, failed pages and the stop reason.\n\n### Export product prices and one row per review\n\nFor a product-price CSV or Excel export, select the **Products** view without unwinding `reviews`. For review expansion, `?view=reviews&format=csv` (or `format=xlsx`) uses the existing view; `?unwind=reviews&fields=productId,name,reviewId,reviewRating,reviewText,reviewDate,url&format=csv` is an explicit alternative. Apify merges each review into its parent product during unwind, so these review column names are top-level **only after expansion**.\n\nAn expanded view is not a review-only filter: items with a missing or null `reviews` field can remain as product rows with blank review columns. Do not count all exported rows as reviews. In Excel/Google Sheets, filter `reviewId` to nonblank values before saving a review-only sheet. Keep product-price data in a separate sheet; repeated product prices in expanded review rows are not separate offers.\n\nFor an exact local split, download the existing dataset as **JSON without a view or unwind**, save it as `products.json`, and run this Python 3 script in the same directory. It writes `product-prices.csv` and `reviews.csv`, preserving Unicode, quotes and commas. No Actor run, API call or credentials are needed for this local conversion. Missing, null and empty review arrays produce zero review rows. Removed-product monitoring records (`changeType: \"removed\"`) are excluded from the current-price CSV; retain the raw JSON if you need those events.\n\n```python\nimport csv\nimport json\nfrom pathlib import Path\n\nitems = json.loads(Path(\"products.json\").read_text(encoding=\"utf-8-sig\"))\nprice_fields = [\"productId\", \"name\", \"price\", \"sellingPrice\", \"originalPrice\", \"currency\", \"url\", \"scrapedAt\"]\nreview_fields = [\"productId\", \"name\", \"reviewId\", \"reviewRating\", \"reviewText\", \"reviewDate\", \"reviewLikes\", \"reviewSellerName\", \"reviewVariant\", \"url\"]\nproducts = [p for p in items if p.get(\"productId\") is not None and p.get(\"changeType\") != \"removed\"]\nreviews = [dict(p, **r) for p in products for r in (p.get(\"reviews\") or [])]\nfor filename, fields, rows in [\n    (\"product-prices.csv\", price_fields, products),\n    (\"reviews.csv\", review_fields, reviews),\n]:\n    with open(filename, \"w\", encoding=\"utf-8-sig\", newline=\"\") as out:\n        writer = csv.DictWriter(out, fieldnames=fields, extrasaction=\"ignore\")\n        writer.writeheader()\n        writer.writerows(rows)\n```\n\n**Pagination and limits:** Dataset API `offset` and `limit` count stored product items, not expanded review rows. For example, `limit=1&unwind=reviews` can expand to several reviews from one product. When paging, advance the offset by the product-item page size, not the expanded row count. The Actor's `maxReviewsPerProduct` caps collection per product; `reviewSort` controls review order. A dataset may contain fewer reviews than requested: products can have no reviews, the cost limit can trim the saved array, or the review API can remain rate-limited. On that last condition the product is retained with `reviews: []` and `reviewsError`; a product 404 is skipped and counted in `RUN_SUMMARY`. An empty review sheet alone does not prove successful collection: inspect `reviewsError` and `RUN_SUMMARY`.\n\nReferences: [Dataset schema transformations](https://docs.apify.com/storage/dataset-schema), [Dataset items API and pagination](https://docs.apify.com/api/v2/dataset-items-get).\n\n**Price fields:** `price` is what Trendyol displays (including cart promotions; listings sort by it), `sellingPrice` is the seller's price before cart promotions, `originalPrice` is the crossed-out price.\n\n## Status rows (no failed runs for bad inputs)\n\nInputs that produce no products don't fail the run. Instead you get a free row with `\"type\": \"status\"`:\n\n| `status` | Meaning |\n| --- | --- |\n| `invalid_input` | No URL or keyword was given |\n| `unsupported_url` | Not a Trendyol listing/product URL (e.g. the home page or another site) |\n| `not_found` | Trendyol returned 404 (product removed, wrong URL) |\n| `blocked` | Trendyol kept showing a Cloudflare challenge, even after the automatic retry with another connection; set the proxy to RESIDENTIAL, country TR |\n| `failed` | The page failed after retries; the `message` says why |\n\nFilter them out with `type != \"status\"`, or use the *Products* view. They are never charged.\n\n## Pricing\n\nPay per event — platform and proxy costs are included:\n\n| Event | Charged when |\n| --- | --- |\n| Product | a product is saved |\n| Product details | a product is saved with “Add product details” on |\n| Review | each saved review |\n\nThe review summary has no extra charge. Set **Maximum cost per run** in the run options: the Actor checks the remaining budget before each product and stops cleanly, so you are never charged for data you don't receive.\n\n## Price & stock monitoring\n\nTwo ways to follow prices over time:\n\n**Changes only (recommended for schedules).** Turn on **Changes only** and schedule the Actor. Every run is compared with the previous run of the same input (or of the same **Price monitor name**, if you set one), and only what changed is saved:\n\n| `changeType` | Meaning | Charged |\n| --- | --- | --- |\n| `new` | first time this product is seen (every product on the first run) | yes, as a normal product |\n| `price_changed` | price went up or down (`previousPrice`, `priceChange`, `priceChangePercent`) | yes |\n| `unavailable` | still listed but sold out — temporarily unavailable | yes |\n| `back_in_stock` | sold out last time, buyable again | yes |\n| `stock_changed` / `variants_changed` | a size/variant sold out, came back, appeared or disappeared (needs **Add product details**) | yes |\n| `removed` | product is gone: its page returns 404 (`removedReason: not_found`), or it was missing from a fully crawled listing two runs in a row (`not_listed`) | **free** |\n| unchanged | not saved | **free** |\n\nEach changed row also has a `changes` list (e.g. `{\"field\": \"price\", \"from\": 1299, \"to\": 1149}`), `previousInStock`, `previousSeenAt` and `firstSeenAt`. Products are matched by Trendyol's stable product ID (`productId`), so renamed products are not reported as new. Open the **Changes** view, or connect a webhook / Google Sheets / Slack integration to get only the changes. With details or reviews on, they are only saved (and charged) for changed rows.\n\nSchedule example — check 300 air fryers every morning and get only the price and stock changes:\n\n```json\n{\n  \"startUrls\": [{ \"url\": \"https://www.trendyol.com/fritoz-x-c1055\" }],\n  \"maxProductsPerSource\": 300,\n  \"changesOnly\": true,\n  \"priceMonitorKey\": \"airfryer-watch\"\n}\n```\n\nSave it as a task, then in Apify Console choose **Schedules → Create new**, cron `0 7 * * *` (07:00 every day), and add the task. Keep the input the same between runs: a different input or monitor name starts a new watch list (all products `new` again).\n\n**Price fields on every row.** Without changes-only, set **Price monitor name**, e.g. `airfryer-watch`, and schedule the Actor with the same input. Each product gets `previousPrice`, `priceChange`, `priceChangePercent` and `isNewInMonitor`. Open the **Price changes** view or filter on `priceChange != 0`.\n\n## Tips\n\n- Start with 20–50 products to check the fields you need, then scale.\n- Listing-only runs are the cheapest and fastest; turn on details or reviews only when you need them.\n- The default uses no proxy and disables automatic residential fallback. Search and store pages are read through Trendyol's JSON search endpoint, which is fast and rarely challenged. If a run reports many `blocked` rows, set the proxy to RESIDENTIAL, country TR.\n- Many reviews per product take longer: the review API is rate-limited per IP.\n\n## Limitations\n\n- Product descriptions (the HTML “Ürün Açıklaması” block) are not included — Trendyol loads them separately.\n- Sizes/variants include stock and barcode but not a separate price per size (Trendyol prices the selected offer).\n- Trendyol can change its pages at any time. The Actor fails loudly (no silent empty results) and is monitored with tests against captured pages.\n- Only the Turkish storefront (TRY) is supported; `/en/` URLs are mapped to it.\n\n## Is it legal to scrape Trendyol?\n\nThis Actor collects publicly visible product, price and review data and does not log in. Reviewer names, height and weight, and sellers' tax and address details are deliberately **not** collected. Trendyol's robots.txt disallows search-result URLs (`/sr?...`); category, brand and product pages are a better fit where that matters to you. You are responsible for using the data in line with Trendyol's terms, the Turkish personal-data law (KVKK) and other laws that apply to you. This is not legal advice.\n\n## FAQ\n\n**Why are some reviews missing?** `maxReviewsPerProduct` caps them; and if Trendyol rate-limits its review API repeatedly, products are delivered with `reviewsError` set instead of reviews.\n\n**Why does a product appear only once when it matches two keywords?** Products are de-duplicated across all inputs in a run; `sourceInput` shows where it was found first.\n\n**Can I get a specific seller's offer?** Use a product URL with `?merchantId=...` — it is kept in `url`.\n\n**Found a problem?** Open an issue on the Actor's Issues tab with the run ID and the URL.\n\n\n## Use cases\n\n- Monitor Trendyol prices, discounts and stock on a schedule.\n- Compare every seller offer, not only the buy box.\n- Export a category, brand or store catalogue to JSON, CSV or Excel.\n- Collect reviews only when you turn them on, so a price run stays cheaper.\n",
      "input": {
        "$schema": "https://apify.com/schemas/v1/input.ide.json",
        "title": "Trendyol Product, Price & Review Scraper",
        "description": "Scrape Trendyol.com products from categories, brands, stores, searches or product URLs. Optionally add product details with every seller's price, Trendyol's review summary (rating distribution, aspect sentiment, AI summary) and individual reviews.",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "startUrls": {
            "title": "Trendyol URLs",
            "type": "array",
            "description": "Category (e.g. https://www.trendyol.com/fritoz-x-c1055), brand, brand + category, store (/magaza/...-m-123), search (/sr?q=...) or product URLs (...-p-123). Filters and sorting already in the URL are kept.",
            "editor": "requestListSources",
            "prefill": [
              {
                "url": "https://www.trendyol.com/fritoz-x-c1055"
              }
            ]
          },
          "searchQueries": {
            "title": "Search keywords",
            "type": "array",
            "description": "Keywords to search on Trendyol, one per line (e.g. \"airfryer\", \"kahve makinesi\").",
            "editor": "stringList",
            "prefill": []
          },
          "maxProductsPerSource": {
            "title": "Max products per URL / keyword",
            "type": "integer",
            "description": "Stop collecting from each category, search or store after this many products. Trendyol shows 36 products per page.",
            "minimum": 1,
            "maximum": 10000,
            "default": 50
          },
          "sortBy": {
            "title": "Sort listings by",
            "type": "string",
            "description": "Sort order for keyword searches and URLs that don't already include one.",
            "editor": "select",
            "enum": [
              "RELEVANCE",
              "PRICE_ASC",
              "PRICE_DESC",
              "NEWEST",
              "BEST_SELLER",
              "MOST_FAVOURITE",
              "MOST_RATED"
            ],
            "enumTitles": [
              "Recommended (Trendyol default)",
              "Lowest price",
              "Highest price",
              "Newest",
              "Best sellers",
              "Most favourited",
              "Most reviewed"
            ],
            "default": "RELEVANCE"
          },
          "skipSponsored": {
            "title": "Skip sponsored products",
            "type": "boolean",
            "description": "Leave out ads (sponsored cards) that Trendyol mixes into listings.",
            "default": false
          },
          "includeProductDetails": {
            "title": "Add product details (all sellers, attributes, variants)",
            "type": "boolean",
            "description": "Opens each product page: every seller's price and rating (buy-box analysis), barcode, attributes, sizes/variants with stock, exact favourite count, category path. Charged as an extra event per product.",
            "default": false,
            "sectionCaption": "Product details & reviews"
          },
          "includeReviewSummary": {
            "title": "Add review summary",
            "type": "boolean",
            "description": "Adds Trendyol's review summary to each product: 1–5 star distribution, aspect sentiment (e.g. 'Kalite 94% positive'), size feedback and Trendyol's AI review summary. No extra charge.",
            "default": false
          },
          "includeReviews": {
            "title": "Scrape reviews",
            "type": "boolean",
            "description": "Saves individual reviews inside each product record as a nested reviews array, not separate dataset rows. Expand reviews for CSV/Excel; filter blank reviewId rows for a review-only sheet. Reviewer names, height and weight are not collected.",
            "default": false
          },
          "maxReviewsPerProduct": {
            "title": "Max reviews per product",
            "type": "integer",
            "description": "Upper limit of reviews saved per product.",
            "minimum": 1,
            "maximum": 5000,
            "default": 20
          },
          "reviewSort": {
            "title": "Review order",
            "type": "string",
            "description": "Which reviews come first.",
            "editor": "select",
            "enum": [
              "RELEVANCE",
              "NEWEST"
            ],
            "enumTitles": [
              "Trendyol default (most relevant)",
              "Newest first"
            ],
            "default": "RELEVANCE"
          },
          "priceMonitorKey": {
            "title": "Price monitor name",
            "type": "string",
            "description": "Set a name (e.g. \"airfryer-watch\") and schedule the run. Each product then gets previousPrice, priceChange, priceChangePercent and isNewInMonitor compared with the last run that used the same name. Leave empty to disable. With \"Changes only\" on, this name also selects the changes-only snapshot (leave empty to derive it from your queries and URLs).",
            "editor": "textfield",
            "sectionCaption": "Price monitoring"
          },
          "changesOnly": {
            "title": "Changes only (price & stock monitor)",
            "type": "boolean",
            "description": "Compare every product with the previous run that used the same input (or the same monitor name) and write only **new** products and products whose **price or stock changed** (each row gets a changeType: new, price_changed, stock_changed, variants_changed, unavailable, back_in_stock). Products that disappeared are written as free 'removed' rows. Unchanged products are skipped and not charged. Off (default) = normal full scrape. Schedule the Actor with this on to get a daily change feed.",
            "default": false
          },
          "proxyConfiguration": {
            "title": "Proxy",
            "type": "object",
            "description": "Default: no proxy. Requests use a real Chromium browser. A configured proxy is used only when explicitly supplied; automatic residential fallback is disabled by default.",
            "editor": "proxy",
            "prefill": {
              "useApifyProxy": false
            },
            "default": {
              "useApifyProxy": false
            },
            "sectionCaption": "Advanced"
          },
          "autoProxyFallback": {
            "title": "Retry blocked pages with another connection",
            "type": "boolean",
            "description": "Optional, disabled by default. Explicitly enable to retry blocked pages once through Apify Proxy RESIDENTIAL (TR), or directly if an explicitly configured proxy was blocked.",
            "default": false
          },
          "maxConcurrency": {
            "title": "Max parallel requests",
            "type": "integer",
            "description": "Higher is faster but more likely to be rate-limited by Trendyol.",
            "minimum": 1,
            "maximum": 16,
            "default": 4
          },
          "maxBrowserSessions": {
            "title": "Browser sessions",
            "type": "integer",
            "description": "Number of parallel browser sessions, each with its own proxy IP.",
            "minimum": 1,
            "maximum": 10,
            "default": 3
          }
        }
      },
      "output": {
        "$schema": "https://apify.com/schemas/v1/output.ide.json",
        "actorOutputSchemaVersion": 1,
        "title": "Trendyol Scraper output",
        "properties": {
          "products": {
            "type": "string",
            "title": "Products",
            "template": "{{links.apiDefaultDatasetUrl}}/items?view=products"
          },
          "changes": {
            "type": "string",
            "title": "Changes (changes-only mode)",
            "template": "{{links.apiDefaultDatasetUrl}}/items?view=changes"
          },
          "reviews": {
            "type": "string",
            "title": "Reviews (one row per review)",
            "template": "{{links.apiDefaultDatasetUrl}}/items?view=reviews"
          },
          "priceChanges": {
            "type": "string",
            "title": "Price changes",
            "template": "{{links.apiDefaultDatasetUrl}}/items?view=priceMonitor"
          },
          "runSummary": {
            "type": "string",
            "title": "Run summary",
            "template": "{{links.apiDefaultKeyValueStoreUrl}}/records/RUN_SUMMARY"
          }
        }
      },
      "storages": {
        "dataset": {
          "$schema": "https://apify.com/schemas/v1/dataset.ide.json",
          "actorSpecification": 1,
          "fields": {
            "$schema": "http://json-schema.org/draft-07/schema#",
            "type": "object",
            "additionalProperties": true,
            "properties": {
              "productId": {
                "type": "integer",
                "title": "Product ID",
                "description": "Trendyol content ID (the number after -p- in the URL).",
                "example": 345141997
              },
              "groupId": {
                "type": [
                  "integer",
                  "null"
                ],
                "title": "Product group ID",
                "description": "Groups colour/size versions of the same product.",
                "example": 281264224
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ],
                "title": "Name",
                "description": "Product name.",
                "example": "Thermochef XL Airfryer/Sıcak Hava Fritözü-Siyah"
              },
              "brand": {
                "type": [
                  "string",
                  "null"
                ],
                "title": "Brand",
                "description": "Brand name.",
                "example": "Schafer"
              },
              "brandId": {
                "type": [
                  "integer",
                  "null"
                ],
                "title": "Brand ID",
                "description": "Trendyol brand ID.",
                "example": 1437
              },
              "category": {
                "type": [
                  "string",
                  "null"
                ],
                "title": "Category",
                "description": "Leaf category name.",
                "example": "Fritöz"
              },
              "categoryId": {
                "type": [
                  "integer",
                  "null"
                ],
                "title": "Category ID",
                "description": "Trendyol category ID.",
                "example": 859
              },
              "url": {
                "type": [
                  "string",
                  "null"
                ],
                "title": "URL",
                "description": "Product URL (merchantId kept: it selects the seller's offer).",
                "example": "https://www.trendyol.com/schafer/thermochef-xl-airfryer-sicak-hava-fritozu-siyah-p-345141997"
              },
              "imageUrl": {
                "type": [
                  "string",
                  "null"
                ],
                "title": "Image",
                "description": "Main full-size image."
              },
              "images": {
                "type": "array",
                "title": "Images",
                "description": "All full-size image URLs.",
                "items": {
                  "type": "string"
                }
              },
              "price": {
                "type": [
                  "number",
                  "null"
                ],
                "title": "Price (TRY)",
                "description": "Price Trendyol shows, including cart discounts (\"Sepette\" price). Listings are sorted by this value.",
                "example": 3765
              },
              "sellingPrice": {
                "type": [
                  "number",
                  "null"
                ],
                "title": "Selling price (TRY)",
                "description": "Seller's price before cart promotions.",
                "example": 3765
              },
              "originalPrice": {
                "type": [
                  "number",
                  "null"
                ],
                "title": "Struck-through price (TRY)",
                "description": "Crossed-out price shown next to the price; null when there is none."
              },
              "discountPercent": {
                "type": [
                  "number",
                  "null"
                ],
                "title": "Discount %",
                "description": "Discount of price vs. struck-through price."
              },
              "currency": {
                "type": "string",
                "title": "Currency",
                "description": "Always TRY (Turkish storefront).",
                "example": "TRY"
              },
              "cartDiscountLabel": {
                "type": [
                  "string",
                  "null"
                ],
                "title": "Cart discount",
                "description": "Cart promotion label, e.g. \"Sepette %40 İndirim\"."
              },
              "lowestPriceInDays": {
                "type": [
                  "number",
                  "null"
                ],
                "title": "Lowest price in N days",
                "description": "Set when Trendyol labels the price as the lowest of the last N days."
              },
              "rating": {
                "type": [
                  "number",
                  "null"
                ],
                "title": "Rating",
                "description": "Average rating (1–5).",
                "example": 4.37
              },
              "ratingCount": {
                "type": [
                  "number",
                  "null"
                ],
                "title": "Ratings",
                "description": "Number of ratings.",
                "example": 3996
              },
              "favoriteCountText": {
                "type": [
                  "string",
                  "null"
                ],
                "title": "Favourites (listing)",
                "description": "Rounded favourite count shown on listing cards, e.g. \"104K\".",
                "example": "104K"
              },
              "orderCountText": {
                "type": [
                  "string",
                  "null"
                ],
                "title": "Orders (listing)",
                "description": "Order social-proof text from listing cards, e.g. \"100+\"."
              },
              "sellerId": {
                "type": [
                  "integer",
                  "null"
                ],
                "title": "Seller ID",
                "description": "Merchant ID of the offer shown.",
                "example": 129419
              },
              "inStock": {
                "type": [
                  "boolean",
                  "null"
                ],
                "title": "In stock",
                "description": "Stock status.",
                "example": true
              },
              "freeShipping": {
                "type": [
                  "boolean",
                  "null"
                ],
                "title": "Free shipping",
                "description": "Free cargo.",
                "example": true
              },
              "fastDelivery": {
                "type": [
                  "boolean",
                  "null"
                ],
                "title": "Fast delivery",
                "description": "Trendyol fast-delivery flag (listing)."
              },
              "nextDayDelivery": {
                "type": [
                  "boolean",
                  "null"
                ],
                "title": "Next-day delivery",
                "description": "Next-day delivery flag (listing)."
              },
              "hasCoupon": {
                "type": [
                  "boolean",
                  "null"
                ],
                "title": "Coupon",
                "description": "A collectable coupon is available."
              },
              "isSponsored": {
                "type": "boolean",
                "title": "Sponsored",
                "description": "Ad card inside the listing.",
                "example": false
              },
              "badges": {
                "type": "array",
                "title": "Badges",
                "description": "Badges and labels, e.g. best seller, free cargo, lowest price.",
                "items": {
                  "type": "object"
                }
              },
              "promotions": {
                "type": "array",
                "title": "Promotions",
                "description": "Active promotions (name, type, end date).",
                "items": {
                  "type": "object"
                }
              },
              "position": {
                "type": [
                  "integer",
                  "null"
                ],
                "title": "Position",
                "description": "1-based position in the listing (ads included).",
                "example": 1
              },
              "listingPage": {
                "type": [
                  "integer",
                  "null"
                ],
                "title": "Listing page",
                "description": "Listing page number.",
                "example": 1
              },
              "sourceType": {
                "type": "string",
                "title": "Source type",
                "description": "search, category, store or product.",
                "example": "category"
              },
              "sourceInput": {
                "type": "string",
                "title": "Source",
                "description": "Keyword or URL the product came from."
              },
              "productCode": {
                "type": [
                  "string",
                  "null"
                ],
                "title": "Product code",
                "description": "Seller's product code (details)."
              },
              "barcode": {
                "type": [
                  "string",
                  "null"
                ],
                "title": "Barcode",
                "description": "Barcode of the buy-box offer (details)."
              },
              "reviewCount": {
                "type": [
                  "number",
                  "null"
                ],
                "title": "Written reviews",
                "description": "Number of written reviews (details)."
              },
              "favoriteCount": {
                "type": [
                  "number",
                  "null"
                ],
                "title": "Favourites",
                "description": "Exact favourite count (details)."
              },
              "sellerName": {
                "type": [
                  "string",
                  "null"
                ],
                "title": "Seller",
                "description": "Buy-box seller name (details)."
              },
              "sellerScore": {
                "type": [
                  "number",
                  "null"
                ],
                "title": "Seller score",
                "description": "Seller score 0–10 (details)."
              },
              "sellerCity": {
                "type": [
                  "string",
                  "null"
                ],
                "title": "Seller city",
                "description": "Seller city (details)."
              },
              "categoryPath": {
                "type": [
                  "string",
                  "null"
                ],
                "title": "Category path",
                "description": "Full category path (details)."
              },
              "gender": {
                "type": [
                  "string",
                  "null"
                ],
                "title": "Gender",
                "description": "Target gender (details)."
              },
              "color": {
                "type": [
                  "string",
                  "null"
                ],
                "title": "Colour",
                "description": "Colour of this product version (details)."
              },
              "variantAttribute": {
                "type": [
                  "string",
                  "null"
                ],
                "title": "Variant type",
                "description": "What variants differ by, e.g. Beden (details)."
              },
              "variants": {
                "type": [
                  "array",
                  "null"
                ],
                "title": "Variants",
                "description": "Sizes/variants with stock and barcode (details)."
              },
              "attributes": {
                "type": [
                  "object",
                  "null"
                ],
                "title": "Attributes",
                "description": "Product attributes as name → value (details)."
              },
              "sellers": {
                "type": [
                  "array",
                  "null"
                ],
                "title": "All sellers",
                "description": "Every seller offering this product with price, score, stock and buy-box flag (details)."
              },
              "sellerCount": {
                "type": [
                  "integer",
                  "null"
                ],
                "title": "Seller count",
                "description": "Number of sellers (details)."
              },
              "lowestSellerPrice": {
                "type": [
                  "number",
                  "null"
                ],
                "title": "Lowest seller price",
                "description": "Lowest price among all sellers (details)."
              },
              "installmentCount": {
                "type": [
                  "number",
                  "null"
                ],
                "title": "Max installments",
                "description": "Maximum installments (details)."
              },
              "vatRate": {
                "type": [
                  "number",
                  "null"
                ],
                "title": "VAT %",
                "description": "VAT rate (details)."
              },
              "isRefundable": {
                "type": [
                  "boolean",
                  "null"
                ],
                "title": "Refundable",
                "description": "Returnable product (details)."
              },
              "categoryRankings": {
                "type": [
                  "array",
                  "null"
                ],
                "title": "Category rankings",
                "description": "E.g. best seller #9 in category (details)."
              },
              "sizeChartUrl": {
                "type": [
                  "string",
                  "null"
                ],
                "title": "Size chart",
                "description": "Size chart image (details)."
              },
              "reviewSummary": {
                "type": [
                  "object",
                  "null"
                ],
                "title": "Review summary",
                "description": "Star distribution, aspect sentiment, size feedback and Trendyol's AI summary."
              },
              "reviews": {
                "type": [
                  "array",
                  "null"
                ],
                "title": "Reviews",
                "description": "Reviews (reviewId, reviewRating, reviewText, reviewDate, reviewLikes, reviewSellerName, reviewVariant, reviewLanguage, reviewMediaUrls, reviewTrusted)."
              },
              "reviewsError": {
                "type": [
                  "string",
                  "null"
                ],
                "title": "Reviews error",
                "description": "Set when reviews were requested but Trendyol rate-limited its review API for this product."
              },
              "previousPrice": {
                "type": [
                  "number",
                  "null"
                ],
                "title": "Previous price",
                "description": "Price in the previous monitored run."
              },
              "previousPriceSeenAt": {
                "type": [
                  "string",
                  "null"
                ],
                "title": "Previous price date",
                "description": "When the previous price was seen."
              },
              "priceChange": {
                "type": [
                  "number",
                  "null"
                ],
                "title": "Price change (TRY)",
                "description": "price − previousPrice."
              },
              "priceChangePercent": {
                "type": [
                  "number",
                  "null"
                ],
                "title": "Price change %",
                "description": "Change vs. previous run in %."
              },
              "isNewInMonitor": {
                "type": "boolean",
                "title": "New in monitor",
                "description": "Not seen in earlier monitored runs."
              },
              "scrapedAt": {
                "type": "string",
                "title": "Scraped at",
                "description": "ISO timestamp."
              },
              "type": {
                "type": "string",
                "description": "'status' for rows that explain an input without products (invalid/unsupported URL, not found, blocked); 'removed' for products that disappeared (changes-only mode). Missing on product rows. Status and removed rows are never charged."
              },
              "status": {
                "type": "string",
                "description": "Status row only: invalid_input, unsupported_url, not_found, blocked or failed."
              },
              "message": {
                "type": "string",
                "description": "Status row only: what went wrong and how to fix it."
              },
              "input": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Status row only: the URL or keyword from the input."
              },
              "changeType": {
                "type": "string",
                "enum": [
                  "new",
                  "price_changed",
                  "stock_changed",
                  "variants_changed",
                  "unavailable",
                  "back_in_stock",
                  "removed"
                ],
                "description": "Changes-only mode: what changed since the previous run. Missing in normal mode."
              },
              "changes": {
                "type": "array",
                "description": "Changes-only mode: list of changes, e.g. {\"field\":\"price\",\"from\":100,\"to\":90}."
              },
              "previousInStock": {
                "type": [
                  "boolean",
                  "null"
                ]
              },
              "previousSeenAt": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "firstSeenAt": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "removedReason": {
                "type": "string",
                "enum": [
                  "not_found",
                  "not_listed"
                ],
                "description": "Removed rows: 'not_found' = product page is gone (404 / redirects home); 'not_listed' = missing from a fully crawled store or listing in two runs in a row."
              },
              "lastPrice": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "lastInStock": {
                "type": [
                  "boolean",
                  "null"
                ]
              },
              "lastSeenAt": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "scrapedAt"
            ]
          },
          "views": {
            "products": {
              "title": "Products",
              "description": "One row per product.",
              "transformation": {
                "fields": [
                  "imageUrl",
                  "name",
                  "brand",
                  "price",
                  "originalPrice",
                  "discountPercent",
                  "rating",
                  "ratingCount",
                  "sellerName",
                  "sellerCount",
                  "lowestSellerPrice",
                  "inStock",
                  "isSponsored",
                  "position",
                  "category",
                  "url"
                ]
              },
              "display": {
                "component": "table",
                "properties": {
                  "imageUrl": {
                    "label": "Image",
                    "format": "image"
                  },
                  "name": {
                    "label": "Name",
                    "format": "text"
                  },
                  "brand": {
                    "label": "Brand",
                    "format": "text"
                  },
                  "price": {
                    "label": "Price (TRY)",
                    "format": "number"
                  },
                  "originalPrice": {
                    "label": "Was (TRY)",
                    "format": "number"
                  },
                  "discountPercent": {
                    "label": "Discount %",
                    "format": "number"
                  },
                  "rating": {
                    "label": "Rating",
                    "format": "number"
                  },
                  "ratingCount": {
                    "label": "Ratings",
                    "format": "number"
                  },
                  "sellerName": {
                    "label": "Seller",
                    "format": "text"
                  },
                  "sellerCount": {
                    "label": "Sellers",
                    "format": "number"
                  },
                  "lowestSellerPrice": {
                    "label": "Lowest seller price",
                    "format": "number"
                  },
                  "inStock": {
                    "label": "In stock",
                    "format": "boolean"
                  },
                  "isSponsored": {
                    "label": "Ad",
                    "format": "boolean"
                  },
                  "position": {
                    "label": "Position",
                    "format": "number"
                  },
                  "category": {
                    "label": "Category",
                    "format": "text"
                  },
                  "url": {
                    "label": "URL",
                    "format": "link"
                  }
                }
              }
            },
            "priceMonitor": {
              "title": "Price changes",
              "description": "Only meaningful when a price monitor name is set.",
              "transformation": {
                "fields": [
                  "name",
                  "price",
                  "previousPrice",
                  "priceChange",
                  "priceChangePercent",
                  "isNewInMonitor",
                  "previousPriceSeenAt",
                  "url"
                ]
              },
              "display": {
                "component": "table",
                "properties": {
                  "name": {
                    "label": "Name",
                    "format": "text"
                  },
                  "price": {
                    "label": "Price",
                    "format": "number"
                  },
                  "previousPrice": {
                    "label": "Previous",
                    "format": "number"
                  },
                  "priceChange": {
                    "label": "Change (TRY)",
                    "format": "number"
                  },
                  "priceChangePercent": {
                    "label": "Change %",
                    "format": "number"
                  },
                  "isNewInMonitor": {
                    "label": "New",
                    "format": "boolean"
                  },
                  "previousPriceSeenAt": {
                    "label": "Previous seen",
                    "format": "date"
                  },
                  "url": {
                    "label": "URL",
                    "format": "link"
                  }
                }
              }
            },
            "reviews": {
              "title": "Reviews (expanded; may include products without reviews)",
              "description": "Expands nested reviews and merges review fields into each product. Products without a reviews array may remain with blank review columns; filter reviewId to nonblank values for review-only CSV/Excel. Product prices belong in a separate Products export.",
              "transformation": {
                "unwind": [
                  "reviews"
                ],
                "fields": [
                  "productId",
                  "name",
                  "reviewRating",
                  "reviewText",
                  "reviewDate",
                  "reviewLikes",
                  "reviewSellerName",
                  "reviewVariant",
                  "reviewId",
                  "url"
                ]
              },
              "display": {
                "component": "table",
                "properties": {
                  "productId": {
                    "label": "Product ID",
                    "format": "number"
                  },
                  "name": {
                    "label": "Product",
                    "format": "text"
                  },
                  "reviewRating": {
                    "label": "Stars",
                    "format": "number"
                  },
                  "reviewText": {
                    "label": "Review",
                    "format": "text"
                  },
                  "reviewDate": {
                    "label": "Date",
                    "format": "date"
                  },
                  "reviewLikes": {
                    "label": "Likes",
                    "format": "number"
                  },
                  "reviewSellerName": {
                    "label": "Bought from",
                    "format": "text"
                  },
                  "reviewVariant": {
                    "label": "Variant",
                    "format": "text"
                  },
                  "reviewId": {
                    "label": "Review ID",
                    "format": "number"
                  },
                  "url": {
                    "label": "Product URL",
                    "format": "link"
                  }
                }
              }
            },
            "changes": {
              "title": "Changes (changes-only mode)",
              "description": "New, changed and removed products when \"Changes only\" is on.",
              "transformation": {
                "fields": [
                  "changeType",
                  "name",
                  "price",
                  "previousPrice",
                  "priceChange",
                  "priceChangePercent",
                  "inStock",
                  "previousInStock",
                  "removedReason",
                  "lastPrice",
                  "url"
                ]
              },
              "display": {
                "component": "table",
                "properties": {
                  "changeType": {
                    "label": "Change",
                    "format": "text"
                  },
                  "price": {
                    "label": "Price",
                    "format": "number"
                  },
                  "previousPrice": {
                    "label": "Previous price",
                    "format": "number"
                  },
                  "priceChange": {
                    "label": "Change",
                    "format": "number"
                  },
                  "priceChangePercent": {
                    "label": "Change %",
                    "format": "number"
                  },
                  "inStock": {
                    "label": "In stock",
                    "format": "boolean"
                  },
                  "previousInStock": {
                    "label": "Was in stock",
                    "format": "boolean"
                  },
                  "url": {
                    "label": "URL",
                    "format": "link"
                  }
                }
              }
            }
          }
        },
        "keyValueStore": {
          "$schema": "https://apify.com/schemas/v1/key-value-store.ide.json",
          "actorKeyValueStoreSchemaVersion": 1,
          "title": "Run storage",
          "collections": {
            "runSummary": {
              "title": "Run summary",
              "description": "Counts, stop reason, failed pages and proxy/browser statistics for the run.",
              "key": "RUN_SUMMARY",
              "contentTypes": [
                "application/json"
              ]
            }
          }
        }
      }
    },
    "buildNumber": "1.0.14",
    "imageDigest": "f8ee8313aca4d6a6501f9fb09aa071c51e3129079b5063d32d4e3550620ea195",
    "actVersion": {
      "sourceType": "SOURCE_FILES",
      "buildTag": "export-contract-candidate",
      "versionNumber": "1.0"
    },
    "usage": {
      "ACTOR_COMPUTE_UNITS": 0.02600222222222222
    }
  }
}