# Netshoes Brazil - Products, Prices, Stock & Reviews (`abotapi/netshoes-scraper`) Actor

Scrape sportswear and footwear from netshoes.com.br. Search by keyword with the store's own brand, size, colour and gender filters, or paste product and category links. Returns BRL price, was-price, discount, instalments, stock per size, colour variants, media, specs and reviews with fit feedback.

- **URL**: https://apify.com/abotapi/netshoes-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** E-commerce
- **Features:** Limited permissions
- **Stats:** 8 total users, 5 monthly users, 77.5% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 product results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Netshoes Scraper: Brazilian Sportswear Prices, Stock & Reviews

Netshoes Scraper turns netshoes.com.br, Brazil's sportswear and footwear store, into a clean product data API. Search by keyword with the store's own brand, department, size, colour and gender filters, or paste product, category, brand and search links and scrape them directly. Every product colour comes back as one row with price in BRL, was-price, discount, instalment terms, stock, colour variants and media, plus optional full specifications and customer reviews with the store's own size and fit feedback, then export to JSON, CSV or Excel, or pull the results straight into your app through the API.

### Why This Scraper?

- **Two ways to find products.** Search by keyword, or paste product, category, brand, campaign or search links and scrape them directly.
- **One row per product colour, at no extra cost.** Identity, current price and was-price in BRL, discount, instalment terms, stock per size and colour siblings are always included.
- **Full detail on demand.** Switch on description, specifications, materials, per-size stock and the whole image gallery per run, and pay only for what you use.
- **Customer voice included.** Ratings, written reviews and the store's own size and fit surveys, so you can tell whether a product runs small before you buy it.
- **Built for monitoring.** Incremental mode returns only new, changed, restocked and, optionally, delisted products on a schedule, and a refused run fails loudly instead of returning an empty dataset.
- **Brazil-native filters.** Brand, department, product type, numeric shoe size, colour and gender all match the store's own Portuguese vocabulary and spelling, resolved for you at run time.
- **Resume a large pull.** An interrupted run can continue from where it left off without paying twice for products already collected.

### Use Cases

- **Price and discount monitoring:** track price drops, instalment terms and promotional badges across a brand or department on a schedule.
- **Competitive intelligence for sportswear sellers:** compare assortment, pricing and stock across competing brands sold on the same marketplace.
- **Size and fit research:** mine the store's own fit surveys and reviews to see which products run small or large before recommending them.
- **Stock and restock alerts:** watch specific products or a whole category for sold-out and back-in-stock changes with Incremental mode.
- **Catalogue and content teams:** build a structured feed of descriptions, specifications and images for a product comparison site or app.

### Data You Get

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

| Field | Example |
| --- | --- |
| `sku` | `"ABC-12345-001"` (one colour of a product; this is the row's identity) |
| `productCode` | `"ABC-12345"` (the product family, all colours) |
| `title` | `"Sample Running Sneaker"` |
| `brand` | `"Sample Brand"` |
| `department` / `productType` | `"Running"` / `"Tenis"` |
| `url` | Netshoes link to this product |
| `currency` | `"BRL"` |
| `price` | `249.9` |
| `originalPrice` | `349.9` |
| `discountAmount` / `discountPercent` / `onSale` | `100.0` / `29` / `true` |
| `paymentMethod` / `paymentMethodPrice` | `"PIX"` / `237.41` |
| `installments` | `{"count": 3, "amountEach": 83.3, "total": 249.9, "interestFree": true}` |
| `sellerName` / `soldByStore` | `"Netshoes"` / `true` (marketplace sellers are named) |
| `inStock` / `sizes` | `true` / `["38", "39", "40", "41", "42"]` |
| `sizesInStock` | sizes actually available to buy right now (needs a product page read: **Read each product's page** or **Fetch customer reviews**) |
| `color` / `colorVariants` | main colour, plus the same product's other colours with their own links |
| `freeShipping` / `fastFulfilment` / `preSale` | shipping and launch policy for this listing |
| `badges` | `["NOVIDADE"]` |
| `featuredImage` / `images` | product photo and the full gallery |
| `rating` / `reviewCount` | `4.6` / `120` |
| `variants` | stock per size, each with its own SKU, price and EAN (needs a product page read) |
| `specs` / `material` / `technology` / `weightGrams` / `origin` | full copy and physical specifications (with **Read each product's page**) |
| `reviews` / `fitFeedback` / `percentRecommended` | written reviews and the size and fit survey (with **Fetch customer reviews and fit feedback**) |
| `changeType` / `changedFields` / `firstSeenAt` / `lastSeenAt` | incremental mode: `"NEW"` / `[]` / timestamps |

Reading a product's own page also adds `longDescription`, `highlights`, `dimensionsCm`, `warranty`, `releaseDate`, `sizeType`, `sizeGuideAvailable`, `thumbnails`, `videos`, `breadcrumb`, `categoryPath`, `installmentPlans`, `variantSku`, `genders`, `brandSlug` and `additionalDeliveryDays`. Reviews add `ratingHistogram`, `reviewsReturned`, `reviewsTruncated` and answered `questions`, with the asking customer's email address always dropped. Every record also carries `detailsFetched`, `sourceUrl`, `searchTerm`, `reviewsRequested` and `scrapedAt`.

### How to Use

1. Pick a **mode**: `search` (by keyword) or `url` (paste Netshoes links).
2. Fill in the fields for that mode, then apply any brand, department, size, colour, gender or price filters you want; they apply to both modes.
3. Set **Max products** to control run size and cost, then click **Start**.
4. Download the dataset as JSON, CSV or Excel, or read it through the API.

**Search by keyword:**

```json
{
  "mode": "search",
  "queries": ["tenis adidas"],
  "maxItems": 50
}
```

**Search with filters, discounted only:**

```json
{
  "mode": "search",
  "queries": ["chuteira campo"],
  "brand": "Nike",
  "size": "40",
  "onSaleOnly": true,
  "maxItems": 100
}
```

If you leave `urls` untouched in url mode, the actor now scrapes four sample links, one per link shape (keyword search, product page, category or brand path, filtered and paged search). Replace them with your own links.

**Paste links, with full product-page detail:**

```json
{
  "mode": "url",
  "urls": [
    "https://www.netshoes.com.br/busca?q=tenis%20adidas",
    "https://www.netshoes.com.br/futebol/chuteiras"
  ],
  "fetchDetails": true
}
```

**Monitor a category on a schedule, reviews included:**

```json
{
  "mode": "url",
  "urls": ["https://www.netshoes.com.br/running/tenis"],
  "fetchReviews": true,
  "incrementalMode": true,
  "emitExpired": true
}
```

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/netshoes-scraper").call(run_input={"mode": "search", "queries": ["tenis adidas"], "maxItems": 50})
for product in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(product["title"], product["price"], product["inStock"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/netshoes-scraper').call({ mode: 'search', queries: ['tenis adidas'], maxItems: 50 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### Link shapes you can paste

In url mode the actor reads these shapes of Netshoes link: a keyword search (`/busca?q=...`), a product page (`/p/<name>-<SKU>`), a category or brand path (for example `/running/tenis` or `/running/tenis/adidas`), and a filtered, paged search (facet and `page` parameters). If you leave `urls` untouched, one sample of each shape is used. A link you paste that starts past page 1 is read from that page.

#### Output tab views

The Output tab offers four views: **Products** (the result-card fields), **Product detail** (specifications, per-size stock and gallery), **Customer reviews** (one row per written review, unwound from the reviews array) and **Changes since the last run** (incremental status and changed fields; with Compact updates on, also the old and new values of each price or stock change and the per-size changes). Customer reviews are capped per product by **Max reviews per product**, from 1 to 100 (default 20).

#### Keeping a run cheap

A result page already carries price, discount, sizes, images and rating for about 100 products, at no extra cost. **Read each product's page** and **Fetch customer reviews** each need one extra page per product and are billed with one Product page detail event per product that reaches the dataset. They share that one page, so turning both on costs the same as turning on either. A pasted product link always reads the product page, because there is no result card behind it to fall back on, so it is billed one Product page detail event whenever it reaches the dataset, whatever the toggle says.

#### How Max products and Max result pages shape a run

When **Max products** is set, every keyword and link in a run gets its own slice of it, split evenly across them, so one popular search cannot use up the whole run and starve the rest. A search reads whole result pages and stops paging once the products it has read reach its slice. That count covers every product on those pages not already collected earlier in the run, before your filters and Incremental mode are applied, not just the ones that reach the dataset. The run as a whole still stops at **Max products** counted on rows actually returned.

Two consequences are worth knowing:

- **A search can stop paging early.** Products on a page that your Brand, Size, Colour, price or other filters drop, or that Incremental mode classifies `UNCHANGED` (and suppresses), still count toward that search's slice. A page of such products can end the search before a later page with products you would actually get is ever read, so the run returns fewer rows than **Max products** allows.
- **Overlapping searches can miss products.** When the last page a search reads holds more products than its remaining slice, the extra ones are not returned but are still marked as seen, so a later keyword or link in the same run that finds them skips them too.

If you run several overlapping keywords or links together, or combine a cap with strict filters or Incremental mode, and need a complete result, run them one at a time, or set **Max products** to `0` (the 100000 maximum, split across the searches in the run) and bound the run with **Max result pages** instead: each search's slice is then so large that it is not reached in practice.

#### Resume and recurring updates

- **Resume** (`resumeFromRunId`) continues one interrupted run: paste its run or dataset ID and the actor skips everything already collected there, so you don't pay twice.
- **Incremental mode** (`incrementalMode`) is for scheduled runs over the same search or links. Each product is classified `NEW`, `UPDATED` (with `changedFields`), `UNCHANGED` (suppressed and not billed unless `emitUnchanged` is on), `REAPPEARED` or `EXPIRED` (only after a run that scanned the whole tracked scope, and only with `emitExpired`). `stateKey` names or shares the stored state. With Incremental mode off, output is exactly as before.

**What counts as a change.** Compared: price, original price, discount, on-sale flag, payment-method price, `inStock`, `sizesInStock`, sizes, per-size variant price and availability, colour siblings, title, brand, seller, shipping and badges. Excluded by default but still returned in the row: scrape timestamps, image and video links (the media host rotates) and instalment text and plans (derived from the price). Size and variant lists are compared regardless of the order the store returns them. Ratings and reviews never count. Switching the exclusions never causes a one-time wave of `UPDATED` rows: `UNCHANGED` is decided by re-reading the stored record under the current settings.

- `changeScope: priceAndStock` compares only price, original price, discount, on-sale flag, stock and per-size price and availability, so a title, badge or label edit is never a billable `UPDATED` row. Switching `changeScope`, or editing `ignoreFieldsForChanges`, keeps your existing saved state: nothing comes back as `NEW`, each stored product is simply re-compared under the new setting.
- `compactUpdates` turns `UPDATED` and `EXPIRED` rows into slim rows (ids, link, title, current price and stock, `priceChanges` old and new, `variantChanges` per size as `ADDED`, `REMOVED` or `CHANGED`). `NEW` rows stay full. A compact row does not carry product-page detail fields, so the detail surcharge is not charged for it.
- `expireAfterMissedScans` applies with `emitExpired`. `EXPIRED` means the product dropped out of the scanned results; it does not prove it is unavailable, because no product page is opened. Use `inStock` and `sizesInStock` for availability.
- A pasted link that starts past page 1, a run stopped by **Max products** or **Max result pages**, a resumed run, or a run whose search or filters differ from those the state key was last saved with never produces `EXPIRED` rows.

`EXPIRED` rows count against **Max products** like every other row. They are only produced after a run that scanned the whole tracked scope, so a run that stopped because it reached **Max products** returns none. When the scan finished with room to spare, a run returns at most as many `EXPIRED` rows as that room allows (**Max products** minus the rows the run already returned). Products held back that way stay tracked and are returned, and billed, as `EXPIRED` on a later complete run. With **Max products** at `0` (the 100000 maximum) that ceiling is 100000 rows for the whole run.

### Send results into your apps (MCP connectors)

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step **after** the scrape: the Apify dataset is never changed.

**What gets written to the connector:** a condensed, human-readable **summary** of each product, not the full JSON. Each item becomes one entry with a **title** and its key fields flattened to plain text. The **complete record always stays in the Apify dataset**.

1. Authorize a connector once under **Apify, Settings, Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"Pipe results into your apps"** input field. (If the picker is empty, you haven't authorized a connector yet.)
3. For **Notion**, also set `notionParentPageUrl` to the page where items should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | `search` for keywords plus filters, or `url` to scrape pasted Netshoes links. |
| `queries` | array | `["tenis adidas"]` | Search keywords (search mode). Portuguese words match far more than English ones. |
| `urls` | array | four sample links (one per link shape) | Netshoes product, search, category or brand links (url mode). |
| `keyword` | string | (none) | Single-keyword alias of `queries`, added to that list. |
| `startUrl` | string | (none) | Single-link alias of `urls`; when set it is scraped in url mode. With keywords filled in and Mode on search it is ignored, and the log says so. |
| `brand` | string | (none) | Keep only products of this brand, for example Nike, Adidas or Olympikus. |
| `category` | string | (none) | Keep only products in this department, as the store names it, for example Running or Futebol. |
| `productType` | string | (none) | Keep only this type of product, as the store names it, for example Tenis or Chuteiras. |
| `size` | string | (none) | Keep only products offered in this size, for example 40 or M. |
| `color` | string | (none) | Keep only products in this main colour, in Portuguese, for example Preto or Azul. |
| `gender` | string | (empty, any) | `feminino`, `masculino`, `menina`, `menino`, `bebe_menina` or `bebe_menino`. |
| `minPrice` | integer | (none) | Only keep products at or above this price, in BRL. |
| `maxPrice` | integer | (none) | Only keep products at or below this price, in BRL. |
| `inStockOnly` | boolean | `false` | Keep only products that can be bought right now. |
| `onSaleOnly` | boolean | `false` | Keep only products whose current price is below their was-price. |
| `sortBy` | string | `relevance` | `relevance`, `best_sellers`, `new_releases`, `offers`, `review_stars`, `price_asc` or `price_desc`. |
| `fetchDetails` | boolean | `false` | Read every product's own page for full description, specifications, per-size stock and the full gallery. |
| `fetchReviews` | boolean | `false` | Also collect customer reviews, the per-star breakdown and the size and fit surveys. |
| `maxReviewsPerProduct` | integer | `20` | Cap on reviews collected per product when reviews are fetched, from 1 to 100. |
| `maxItems` | integer | `20` | Maximum number of products to return across the whole run, up to 100000. `0` = the 100000 maximum. |
| `maxPages` | integer | `0` | Maximum result pages per keyword or link. `0` = no limit set; a safety limit of 2000 pages per keyword or link (far above what **Max products** can reach) always applies, and the log says when a limit stops a search that still had results. |
| `resumeFromRunId` | string | (none) | Continue one interrupted run without re-collecting or re-billing its products. |
| `incrementalMode` | boolean | `false` | Return only new and changed products on scheduled runs. |
| `stateKey` | string | (none) | Name or share an incremental-mode state. |
| `changeScope` | string | `all` | `priceAndStock` limits change detection to price, stock and per-size price/availability. |
| `compactUpdates` | boolean | `false` | Slim `UPDATED` and `EXPIRED` rows with `priceChanges` and `variantChanges`. |
| `expireAfterMissedScans` | integer | `1` | Mark `EXPIRED` only after this many consecutive complete scans missed the product. |
| `ignoreFieldsForChanges` | array | (none) | Extra fields (or dotted paths such as `variants.ean`) that never count as a change. |
| `emitUnchanged` | boolean | `false` | Also return (and bill) unchanged products. |
| `emitExpired` | boolean | `false` | Also return (and bill) products no longer found after a full scan, within the room left under **Max products**. |
| `proxy` | object | `{"useApifyProxy": true}` | Connection settings. |
| `mcpConnectors` | array | (none) | Optional: send a summary of each product to apps you authorized under Integrations. |
| `notionParentPageUrl` | string | (none) | Notion connector only: page under which products are created. |
| `maxNotifyListings` | integer | `50` | Cap on products written to each connector per run. |

### Output Example

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

```json
{
  "sku": "ABC-12345-001",
  "variantSku": "ABC-12345-001-40",
  "productCode": "ABC-12345",
  "title": "Sample Running Sneaker",
  "brand": "Sample Brand",
  "brandSlug": "sample-brand",
  "department": "Running",
  "productType": "Tenis",
  "genders": ["Masculino"],
  "categoryPath": ["Running", "Tenis", "Sample Brand", "Preto"],
  "url": "https://www.netshoes.com.br/p/ABC-12345-001",
  "currency": "BRL",
  "price": 249.9,
  "originalPrice": 349.9,
  "discountAmount": 100.0,
  "discountPercent": 29,
  "onSale": true,
  "paymentMethod": "PIX",
  "paymentMethodPrice": 237.41,
  "installments": {"count": 3, "amountEach": 83.3, "total": 249.9, "interestFree": true},
  "sellerName": "Netshoes",
  "soldByStore": true,
  "inStock": true,
  "sizes": ["38", "39", "40", "41", "42"],
  "sizesInStock": ["38", "39", "41", "42"],
  "variants": [
    {"sku": "ABC-12345-001-40", "size": "40", "available": true, "price": 249.9, "originalPrice": 349.9, "ean": "7890000000001"}
  ],
  "color": {"name": "Preto+Branco", "primaryName": "Preto", "primaryHex": "#000000", "secondaryName": "Branco", "secondaryHex": "#ffffff"},
  "colorVariants": [
    {"sku": "ABC-12345-002", "colorName": "Azul", "url": "https://www.netshoes.com.br/p/ABC-12345-002", "available": true}
  ],
  "freeShipping": true,
  "fastFulfilment": false,
  "additionalDeliveryDays": 0,
  "preSale": false,
  "badges": ["NOVIDADE"],
  "featuredImage": "https://static.netshoes.com.br/sample/ABC-12345-001/zoom1.jpg",
  "images": ["https://static.netshoes.com.br/sample/ABC-12345-001/zoom1.jpg"],
  "material": "Textil",
  "technology": "Sample Cushioning",
  "weightGrams": 620,
  "origin": "Nacional",
  "sizeGuideAvailable": true,
  "rating": 4.6,
  "reviewCount": 120,
  "percentRecommended": 92,
  "fitFeedback": [
    {"topic": "Tamanho", "average": 3.1, "midpoint": 3.0, "scaleLow": "Justo", "scaleMid": "Na Medida", "scaleHigh": "Largo", "responses": 120}
  ],
  "reviewsRequested": true,
  "reviewsReturned": 5,
  "reviewsTruncated": true,
  "changeType": "NEW",
  "changedFields": [],
  "firstSeenAt": "2026-09-30T12:00:00Z",
  "lastSeenAt": "2026-09-30T12:00:00Z",
  "scrapedAt": "2026-09-30T12:00:00Z"
}
```

### Plan Requirement

Netshoes sits behind a bot-protection edge that refuses ordinary connections on its result and product pages (an "Access Denied" page), on Apify's standard datacenter pool and on plain residential connections alike. Leave the proxy group list empty and the run handles this on its own: it first opens one short browser session with the store (about 20 to 60 seconds) and then reads pages at full speed. If that session cannot be opened, or the store refuses pages, the run moves to Apify Proxy's enhanced group (`UNBLOCKER`): immediately after a refusal on the first connection, otherwise after a few refusals in a row. If the enhanced group is also refused, or is not included in your plan, it moves on to backup connections, and it stays on the lane that works. Whether the enhanced group serves pages depends on your Apify plan and its availability at the time of the run, so check the run log.

That enhanced group is billed on your own Apify plan at a higher cost per request. If your plan does not include proxy access, the run says so in plain words rather than returning an empty dataset. These are the messages you may see in the run log:

- Warning: "The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable." followed by an upgrade pointer.
- Info: "Served over the backup connection (the default connection was unavailable)."
- Failure: "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required".
- Failure: "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested Netshoes pages."
- Failure: "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search. Please try again later."

You can supply your own proxy URLs instead; custom proxies get no automatic backup connection.

### FAQ

#### How much does it cost?

Three events can appear on a run, and the **Pricing** tab shows the current rates:

- **Dataset item:** one event per product row returned in the dataset (incremental `UNCHANGED` rows are suppressed and not billed unless **Emit unchanged products** is on).
- **Product page detail:** one event per product row whose own page is read, whether through **Read each product's page**, **Fetch customer reviews** or a pasted product link. Turning both toggles on still costs one event per product.
- **Actor start:** one event each time a run starts.

Use **Max products** to cap the cost of any run. Failed or empty pages are never billed.

#### Is it legal to scrape Netshoes?

This actor collects only publicly available product data. You are responsible for how you use it: follow Netshoes' terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Prices and specifications are generally facts, but product photos and brand names may be subject to third-party rights.

#### Can I get only new or changed products on a schedule?

Yes. Schedule the actor from the **Schedules** tab and turn on **Incremental mode**. Each run then returns only new, updated and restocked products, and unchanged ones are not billed unless you turn on **Emit unchanged**.

#### Why do my search keywords return so few results?

Netshoes is a Brazilian store, so Portuguese keywords match far more than English ones: "tenis adidas" finds thousands of matches, "adidas sneakers" finds almost none. A few popular phrases are also mapped by the store to a promotional page instead of a product list; when that happens the run says so and names the page, instead of reporting an empty catalogue.

#### Why did my run fail instead of returning an empty dataset?

If the store refuses every request, the run stops with a clear message so "no matches today" is never confused with "nothing could be read". Run it again in a few minutes. If only some pages could be read, you still get what was found, and the run tells you the result is incomplete.

#### Why did an incremental run return fewer `EXPIRED` products than have disappeared?

**Max products** caps the whole run, `EXPIRED` rows included. After the live scan, a run returns (and bills) at most the room left under **Max products** as `EXPIRED` rows, and a run that reached **Max products** returns none. The products held back stay tracked and come back as `EXPIRED` on a later complete run. Raise **Max products** if you want every vanished product reported in one run.

#### Why did a run with several overlapping searches or links return fewer products than expected?

With **Max products** set, each search or link gets a slice of it, and that slice counts every not-yet-collected product on the pages the search reads, before filters and Incremental mode are applied, not only the products that reach the dataset. A page of filtered-out or `UNCHANGED` products can therefore end a search before its later pages are read. And when a search's last page holds more products than its remaining slice, the extra ones are marked as seen without being returned, so a later, overlapping search in the same run skips them. Run overlapping searches or links separately, or set **Max products** to `0` (the 100000 maximum) and bound the run with **Max result pages**, if you need a complete result.

#### Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.

### 🔗 Want more e-commerce data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>🛒 <a href="https://apify.com/abotapi/americanas-com-br-scraper"><b>Americanas Brazil</b></a><br>Scrape Americanas Brazil (americanas.com.br) by keyword, department or pasted link...</td><td>🔌 <a href="https://apify.com/abotapi/casasbahia-com-br-scraper"><b>Casas Bahia Brazil Electronics &amp; Home Goods Scraper</b></a><br>Scrape products from Casas Bahia Brazil by keyword, department, or URL. Filter by brand...</td></tr>
<tr><td>🛒 <a href="https://apify.com/abotapi/drogasil-com-br-scraper"><b>Drogasil Brazil Pharmacy, Health &amp; Beauty Scraper</b></a><br>Scrape Drogasil (drogasil.com.br) medicines, health and beauty products by keyword...</td><td>🧩 <a href="https://apify.com/abotapi/mercadolivre-com-br-scraper"><b>Mercado Livre Brazil</b></a><br>Scrape Mercado Livre Brazil's best-sellers, category and product-video feeds without...</td></tr>
<tr><td>🛒 <a href="https://apify.com/abotapi/uniqlo-com-scraper"><b>UNIQLO Scraper</b></a><br>Scrape UNIQLO products across 21 country storefronts in local currency. Price with pre...</td><td>🛒 <a href="https://apify.com/abotapi/nitori-net-jp-scraper"><b>Nitori Japan Furniture &amp; Home Goods Scraper</b></a><br>Scrape Nitori Japan (nitori-net.jp) furniture and home goods by keyword, category or...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/netshoes-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

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

Choose 'search' for keywords plus filters, or 'url' to scrape pasted Netshoes links. Each mode reads only its own field below.

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

Only used when mode = search, ignored in URL mode. One or more keywords, for example 'tenis adidas', 'chuteira campo' or 'camisa flamengo'.

## `urls` (type: `array`):

Only used when mode = url, ignored in search mode. Product links under /p/, search links under /busca and category or brand paths such as /running/tenis/adidas are all accepted. A listing link that already carries page=N starts at that page rather than at page 1.

## `keyword` (type: `string`):

Single-keyword alias of 'Search keywords', added to that list. Kept for compatibility with other Netshoes scrapers.

## `startUrl` (type: `string`):

Single-link alias of 'Netshoes links'. When set it is scraped as a pasted link (also when no keywords are given). If keywords are filled in while Mode is 'Search by keyword', this link is ignored and the log says so; switch Mode to 'Scrape pasted links' to scrape it. Kept for compatibility with other Netshoes scrapers.

## `brand` (type: `string`):

Optional. Keep only products of this brand, for example 'Nike', 'Adidas', 'Olympikus' or 'New Balance'.

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

Optional. Keep only products in this department, as the store names it, for example 'Running', 'Futebol', 'Academia' or 'Casual'.

## `productType` (type: `string`):

Optional. Keep only this type of product, as the store names it, for example 'Tenis', 'Chuteiras', 'Camisetas' or 'Chinelos'.

## `size` (type: `string`):

Optional. Keep only products offered in this size, for example '40', 'M' or 'GG'. Brazilian footwear sizes are numeric and differ from EU and US sizing.

## `color` (type: `string`):

Optional. Keep only products in this main colour, in Portuguese as the store names it, for example 'Preto', 'Branco', 'Azul' or 'Vermelho'. A product sold in a two-colour combination such as 'Preto+Branco' matches either of its colours.

## `gender` (type: `string`):

Optional. Keep only products for this audience. The store's own list, in Portuguese: feminino (women), masculino (men), menina (girls), menino (boys), bebe-menina (baby girls), bebe-menino (baby boys).

## `minPrice` (type: `integer`):

Optional. Only keep products whose current price is at or above this amount, in Brazilian reais. The store does not offer a working price filter on its own result pages, so this is applied to the price this Actor reads for each product.

## `maxPrice` (type: `integer`):

Optional. Only keep products whose current price is at or below this amount, in Brazilian reais. Applied to the price this Actor reads for each product, for the same reason as the minimum above.

## `inStockOnly` (type: `boolean`):

Optional. Keep only products that can be bought right now. Sold-out products are returned by default so you can track them.

## `onSaleOnly` (type: `boolean`):

Optional. Keep only products whose current price is below their was-price.

## `sortBy` (type: `string`):

How the store orders its result pages. Netshoes mixes a small number of promoted products into every ordering, so the first rows of a page are not always in strict order.

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

Read every product's own page for full description, specifications, material, technology, weight and dimensions, origin, warranty, per-size stock and EANs, named colour variants and the full image gallery. Off by default because it costs one extra page per product. A pasted product link always reads the product page, because there is no result card behind it, so a product link is always billed one 'Product page detail' event whatever this toggle says.

## `fetchReviews` (type: `boolean`):

Also collect customer reviews for each product: rating, text, author name, recommendation and date, plus the per-star breakdown, the share of reviewers who recommend it, and the store's size and fit surveys (fit, comfort, quality and weight, each with the wording of its own scale). Also reads the product page, so it carries the same per-product surcharge as the toggle above. The average rating and review count are always returned without this.

## `maxReviewsPerProduct` (type: `integer`):

Cap on how many individual reviews are collected for each product when 'Fetch customer reviews and fit feedback' is on. Newest first. A Netshoes product page publishes only its most recent reviews inline (usually about a dozen) however many it has in total, so 'reviewsTruncated' is set on any product with more.

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

Maximum number of products to return across the whole run, EXPIRED rows from Incremental mode included. This is the run's cap. Use 0 for the maximum of 100000 products per run.

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

Maximum number of result pages to take per keyword or link. Each page carries about 100 products. 0 = no page limit set; the run then stops at Max products, when there are no more results, when a page repeats the previous one, or at a safety limit of 2000 pages per keyword or link (far above what Max products can reach). When a limit you set here stops a search that still had results, the log says so.

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

Connection used for every request. Leave the default; the run escalates on its own when the store refuses a page.

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

Paste a previous run ID or dataset ID to continue a large catalogue pull without returning or charging for products already collected there. Use this after an interrupted run. For recurring monitoring of the same search, use Incremental mode below instead.

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

Turn this on for daily or recurring monitoring. The first run returns every matching product as NEW. Later runs normally return only NEW, UPDATED and REAPPEARED products, which makes it a cheap way to watch price drops and restocks. What is compared: price, original price, discount, on-sale flag, payment-method price, stock (inStock, sizesInStock), sizes, per-size variant price and availability, colour siblings, title, brand, seller, shipping and badges. What is excluded by default (still returned in the row, never a change): scrape timestamps, image and video links, and instalment text and plans; size and variant lists are compared regardless of the order the store returns them. Ratings and reviews never count. 'Changes to watch' narrows this to price and stock only. Turn on 'Emit unchanged' or 'Emit expired' only when you also want those rows returned (and billed). State is kept separately for each search and filter setup; use State key to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from a previous run above.

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

Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently configured runs. Leave empty to derive a key automatically from the search and filter settings, so two different searches never mix state.

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

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

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

Off by default. Turn on to also return products that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search, never when Max products capped it or Resume was used. EXPIRED rows count against Max products: a run returns at most the room left under it, and the rest are returned on a later complete run. This returns, and bills, extra rows, so leave it off unless you need expiry tracking.

## `changeScope` (type: `string`):

All fields (default): any change to the compared product data marks a product UPDATED. Price and stock only: just price, original price, discount, on-sale flag, stock and per-size price and availability count, so title, badge, shipping or colour-label edits never produce a billable UPDATED row. Changing this never starts a new baseline: the existing saved state is kept (under an automatic or a named State key) and each stored product is simply compared under the new setting.

## `compactUpdates` (type: `boolean`):

Off by default. When on, UPDATED and EXPIRED rows are slim: ids, link, title, current price and stock, plus priceChanges (old and new value of each field that moved) and variantChanges (per-size ADDED, REMOVED or CHANGED). NEW rows stay full. A compact row does not carry product-page detail fields, so no detail surcharge is charged for it.

## `expireAfterMissedScans` (type: `integer`):

Used with 'Emit expired'. A product is marked EXPIRED only after it was missing from this many CONSECUTIVE complete scans (default 1). EXPIRED means the product dropped out of the scanned results; it does not prove the product is unavailable, because no product page is opened. Check inStock and sizesInStock for availability.

## `ignoreFieldsForChanges` (type: `array`):

Field names that must not count as a change, for example title or badges. Use a dotted path to ignore a key inside every element of a list, for example variants.ean. Ignored fields are still returned in the row. Image links, video links and instalment text are already ignored by default. Changing this list never starts a new baseline: the saved state is kept and each stored product is re-compared under the new list, so no product comes back as NEW.

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

Optionally send results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify, Settings, API & Integrations, then select it here. Notion gets a page per product; other connectors get a best-effort write or digest. Each connector receives a condensed summary per product, not the full record; the complete record always stays in the dataset. Leave empty to skip; this never changes the dataset output. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL or id of the Notion page under which product pages are created. Required to enable the Notion export; ignored by other connectors.

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

Cap on products written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "queries": [
    "tenis adidas"
  ],
  "urls": [
    "https://www.netshoes.com.br/busca?q=tenis%20adidas",
    "https://www.netshoes.com.br/p/tenis-adidas-response-runner-2-FBA-86LU-026",
    "https://www.netshoes.com.br/running/tenis/adidas",
    "https://www.netshoes.com.br/busca?q=tenis&marca=adidas&page=2"
  ],
  "gender": "",
  "inStockOnly": false,
  "onSaleOnly": false,
  "sortBy": "relevance",
  "fetchDetails": false,
  "fetchReviews": false,
  "maxReviewsPerProduct": 20,
  "maxItems": 20,
  "maxPages": 0,
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "changeScope": "all",
  "compactUpdates": false,
  "expireAfterMissedScans": 1,
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `detail` (type: `string`):

No description

## `reviews` (type: `string`):

No description

## `changes` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

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

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "mode": "search",
    "queries": [
        "tenis adidas"
    ],
    "urls": [
        "https://www.netshoes.com.br/busca?q=tenis%20adidas",
        "https://www.netshoes.com.br/p/tenis-adidas-response-runner-2-FBA-86LU-026",
        "https://www.netshoes.com.br/running/tenis/adidas",
        "https://www.netshoes.com.br/busca?q=tenis&marca=adidas&page=2"
    ],
    "inStockOnly": false,
    "onSaleOnly": false,
    "fetchDetails": false,
    "fetchReviews": false,
    "proxy": {
        "useApifyProxy": true
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "compactUpdates": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/netshoes-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "mode": "search",
    "queries": ["tenis adidas"],
    "urls": [
        "https://www.netshoes.com.br/busca?q=tenis%20adidas",
        "https://www.netshoes.com.br/p/tenis-adidas-response-runner-2-FBA-86LU-026",
        "https://www.netshoes.com.br/running/tenis/adidas",
        "https://www.netshoes.com.br/busca?q=tenis&marca=adidas&page=2",
    ],
    "inStockOnly": False,
    "onSaleOnly": False,
    "fetchDetails": False,
    "fetchReviews": False,
    "proxy": { "useApifyProxy": True },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "compactUpdates": False,
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/netshoes-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "search",
  "queries": [
    "tenis adidas"
  ],
  "urls": [
    "https://www.netshoes.com.br/busca?q=tenis%20adidas",
    "https://www.netshoes.com.br/p/tenis-adidas-response-runner-2-FBA-86LU-026",
    "https://www.netshoes.com.br/running/tenis/adidas",
    "https://www.netshoes.com.br/busca?q=tenis&marca=adidas&page=2"
  ],
  "inStockOnly": false,
  "onSaleOnly": false,
  "fetchDetails": false,
  "fetchReviews": false,
  "proxy": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "compactUpdates": false
}' |
apify call abotapi/netshoes-scraper --silent --output-dataset

```

## MCP server setup

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