# Casas Bahia Brazil Electronics & Home Goods Scraper (`abotapi/casasbahia-com-br-scraper`) Actor

Scrape products from Casas Bahia Brazil by keyword, department, or URL. Filter by brand, price, discount, rating, and instalment terms. Extract SKUs, titles, brands, current and original prices, Pix offers, BRL instalment plans, stock, pickup options, images, variants, specifications, and reviews.

- **URL**: https://apify.com/abotapi/casasbahia-com-br-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

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

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

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

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Casas Bahia Brazil Electronics & Home Goods Scraper

Scrape the Casas Bahia catalogue (casasbahia.com.br) and get one clean, flat row per product
variant: identity, live price, the pre-discount price and discount, the instant-payment (Pix)
price, the instalment terms, stock and pickup signals, media, variants, specifications and
customer reviews. Everything in Brazilian reais.

Two ways to use it:

- **Search** by keyword and/or department, with the store's own filters (department, brand,
  price band, minimum discount, minimum rating) and its own sort orders.
- **URL** mode: paste product links, department links, search-result links or bare product
  codes. Product links are fetched in full; catalogue links are walked forward page by page.

### Country scope

Casas Bahia is a **Brazil-only** retailer. It publishes one storefront, one currency (BRL) and
one language (pt-BR); there is no country, market or locale switch anywhere on the site, and no
alternate-market address resolves. Verified live before this actor was built, which is why there
is **no country input**: a picker with one working value would only be misleading.

### What you get

| Group | Fields |
|---|---|
| Identity | `productId` (SKU), `productGroupId`, `title`, `baseTitle`, `variantName`, `brand`, `brandId`, `ean`, `url`, `categoryName`, `categoryPath`, `categoryIds` |
| Price | `price`, `originalPrice`, `discountPercent`, `discountAmount`, `listPrice`, `onSale`, `currency` (`BRL`) |
| Instant payment | `cashPrice`, `cashPriceLabel` (for example `No Pix`), `cashDiscountPercent`, `hasPixDiscount`, `hasBoletoDiscount` |
| Instalments | `installmentText` (for example `12x de R$324,99 sem juros`), `installmentCount`, `installmentValue`, `installmentInterestFree`, `installmentOptions[]` with `type`, `count`, `installmentValue`, `monthlyInterest`, `totalPrice`, `interestFree` |
| Availability | `inStock`, `availability` (`national`, `regional`, `commercial`), `pickupInStore`, `deliveryEstimate`, `shippingCost` |
| Seller | `sellerId`, `sellerName`, `sellerCount`, `offerCount`, `minOfferPrice`, `maxOfferPrice` |
| Media | `image`, `images[]`, `videos[]`, `manuals[]` |
| Variants | `variantOptions[]` (for example Colour, Model, Storage with their values) and `variants[]` with `productId`, `name`, `attributes`, `image`, `url`, `available`, `selected` |
| Details | `description`, `specifications[]` (`group`, `name`, `value`), `highlights[]`, `warranty`, `manufacturerName`, `manufacturerContact`, `manufacturerSupportUrl` |
| Reviews | `rating`, `reviewCount`, `ratingBreakdown[]`, `recommendationPercentage`, `reviewSummary`, `reviewsAvailable`, `reviews[]` |
| Bookkeeping | `sponsored`, `productOrigin`, `searchMode`, `scrapedAt` |
| Incremental mode | `changeType`, `changedFields`, `firstSeenAt`, `lastSeenAt` |

#### Reviews

Reviews are a real, fully available feature on this store and are returned by default.
Each product carries an average `rating`, a `reviewCount`, a five-row `ratingBreakdown`, the
share of reviewers who recommend the product (`recommendationPercentage`), the store's own
written summary (`reviewSummary`), and the individual review rows the product page publishes:
author, date, rating, text, helpful and not-helpful counts. Shopper-uploaded review photos
and clips are not returned. Reviews cost **no extra request**: they come from the same product page as the
description and specifications, so `includeReviews` is free once `fetchDetails` is on.

A product with no reviews returns `reviews: []`, `reviewCount: 0`, `rating: null` and
`reviewsAvailable: false`. Nothing is invented.

`maxReviewsPerProduct` caps the number of individual rows returned; the average, the count and
the per-star breakdown are always complete regardless of that cap.

#### Delivery estimate and shipping cost

`deliveryEstimate` and `shippingCost` are always `null`, and that is a property of the store,
not a gap in this actor. Casas Bahia quotes delivery windows and freight **only** against a
customer postal code (CEP) entered on the product page: with no CEP the page's own freight
panel is empty, so there is no anonymous shipping figure to read. What the store does publish
without a CEP is captured: `pickupInStore` (whether the product can be collected in a store),
`availability.national` / `.regional` / `.commercial`, and `inStock`.

### Input

#### Modes

- `mode: "search"` with `queries` (keywords) and/or the `categories` filter.
- `mode: "url"` with `urls`. Accepted shapes:
  - product page, `https://www.casasbahia.com.br/<slug>/p/<sku>`
  - search page, `https://www.casasbahia.com.br/<keyword>/b` (with `?page=`, `?filter=`
    and `?ordenacao=` preserved, so a pasted page 3 starts at page 3)
  - department page, `https://www.casasbahia.com.br/c/<department>?filtro=categoria-c13`
  - a bare product code, for example `1558278013`

#### Filters, in both modes

Every filter narrows a keyword search **and** a pasted department or search link.

| Input | Applied by |
|---|---|
| `categories` (department) | the store |
| `brands` (brand names, for example `Consul`) | the store |
| `minPrice` / `maxPrice` | the store |
| `minDiscountPercent` | the store |
| `minRating` | the store |
| `minInstallments`, `interestFreeInstallmentsOnly` | this actor, after reading each product's payment terms |
| `inStockOnly` | this actor, after reading live availability |
| `sortBy` | the store |

Brand names are matched against the store's **own** brand list for your search, ignoring case
and accents, so you never have to know an internal brand code. If none of the names you enter
exist in that list the run returns nothing and says so, rather than quietly dropping the filter
and handing back the unfiltered catalogue.

`minInstallments` and `interestFreeInstallmentsOnly` are marked "this actor" because the store
publishes no instalment facet. They are honest post-filters, not a claim that the store narrowed
anything.

#### Connection

The default is Apify Proxy on the **standard pool**, which is enough for everything this actor
does: catalogue search, pricing and product pages. This store refuses **individual exits** from
time to time rather than whole pools, and a refused request is retried on a fresh exit
automatically, so an occasional refusal is not something you need to pay your way out of. If
refusals persist, switch the Connection section to the `RESIDENTIAL` group with country `BR`,
which usually clears them but costs considerably more per gigabyte. You can also supply your own
exits. If a run cannot read any results the actor fails loudly with that explanation rather than
reporting an empty search.

#### Resume and recurring updates

- **`resumeFromRunId`** continues ONE interrupted run: paste a previous run or dataset id and
  products already collected there are skipped, not re-returned and not re-billed.
- **`incrementalMode`** (default off) is for recurring monitoring. The first run returns
  everything as `NEW`; later runs return only `NEW`, `UPDATED` and `REAPPEARED`, which is how a
  price cut or a stock change shows up. `emitUnchanged` and `emitExpired` are off by default
  because they return, and bill, extra rows.
- State is keyed on the search, the filters and the detail settings, so two different setups
  never share a baseline. Use `stateKey` to name a campaign or to share one deliberately.

`reviews` is deliberately excluded from the change fingerprint: a busy product would otherwise
be marked `UPDATED` by every new review, and storing the whole review corpus would blow the size
cap on the saved baseline. Change is still detected, through `reviewCount`, `rating` and
`ratingBreakdown`, which are all fingerprinted and do appear in `changedFields`.

#### Export to your apps (optional)

| Input | What it does |
|---|---|
| `mcpConnectors` | Pipe results into your apps (optional). Select one or more authorized MCP connectors. Empty by default, and leaving it empty skips the export entirely. |
| `notionParentPageUrl` | Notion parent page (Notion connector only). The page new item pages are created under. Ignored by every other connector. |
| `maxNotifyListings` | Max items to export per connector. Defaults to 50. Caps the export only, never the dataset. |

### Output sample

```json
{
  "productId": "55072122",
  "productGroupId": "93194933",
  "title": "Smartphone Example 6.8\" 512GB 5G",
  "brand": "Example Brand",
  "url": "https://www.casasbahia.com.br/smartphone-example-55072122.html",
  "categoryPath": ["Telefones e Celulares", "Smartphone", "Smartphone Android"],
  "price": 6754.44,
  "originalPrice": 8998.89,
  "discountPercent": 25,
  "discountAmount": 2244.45,
  "currency": "BRL",
  "onSale": true,
  "cashPrice": 6079.0,
  "cashPriceLabel": "No Pix",
  "cashDiscountPercent": 10,
  "hasPixDiscount": true,
  "installmentText": "24x de R$ 281,44 sem juros",
  "installmentCount": 24,
  "installmentValue": 281.44,
  "installmentInterestFree": true,
  "inStock": true,
  "pickupInStore": true,
  "deliveryEstimate": null,
  "shippingCost": null,
  "sellerName": "Casas Bahia",
  "ean": "0000000000000",
  "variantOptions": [
    { "name": "Cor", "values": ["Preto", "Verde"] },
    { "name": "Armazenamento", "values": ["512GB"] }
  ],
  "rating": 4.8,
  "reviewCount": 6,
  "ratingBreakdown": [{ "rating": 5, "percentage": 83, "label": "Excelente" }],
  "reviews": [
    {
      "reviewId": "0000",
      "author": "Cliente",
      "date": "27/04/2026",
      "rating": 5,
      "text": "Sample review text.",
      "helpfulCount": 1,
      "notHelpfulCount": 0
    }
  ],
  "scrapedAt": "2026-08-28T00:00:00Z"
}
```

Personal details in the sample above are placeholders. Real runs return the review author name
exactly as the store publishes it.

### What you are charged for

Three events:

- **Product result**, charged once per product returned.
- **Detail enrichment**, charged once per product when `fetchDetails` is on. This is the
  description, the specification table, the variants, manufacturer and warranty information, the
  full gallery, the pickup signal, every instalment plan and the reviews. Turn `fetchDetails`
  off for a cheaper run that still returns the live price, the discount, the Pix price and the
  headline instalment terms.
- **Actor start**, charged once per run.

A product suppressed by incremental mode is neither returned nor charged, even though its page
was read to work out that nothing changed.

### Notes and limits

- The catalogue's page size is fixed at 20 rows by the store; larger pages are rejected.
- Some result rows are the store's own promoted placements. They are returned like any other
  product and flagged with `sponsored: true` so you can drop them.
- Search results are capped by the store itself at 10,000 rows per query; narrow with filters to
  go deeper than that.
- `maxItems` defaults to 20. Set it to 0 for an uncapped run, and expect a large department to
  be large.
- Sorting by price is the store's own ordering, applied to the price in its search index.
  Each row's `price` is read live at scrape time, so a product whose price moved since the
  store last indexed it can appear slightly out of order. The price you get is the current
  one, which is the useful half of that trade.
- No concurrency, retry or timeout knobs are exposed. Retries are hard-capped internally and a
  run that cannot read its pages fails fast rather than grinding.

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

Optionally pipe results into Notion, Linear, Airtable or Apify through Model Context Protocol
connectors. Authorize a connector under Apify, Settings, API & Integrations, then select it in
`mcpConnectors`. The dataset output is never changed by this.

# Actor input Schema

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

Choose 'search' to search the catalogue by keyword and filters, or 'url' to scrape pasted links: product pages, department pages, search result pages and bare product codes are all accepted.

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

Only used when mode = search, ignored in url mode. One or more keywords, for example geladeira (fridge), notebook or smart tv. Leave empty to browse by Department alone.

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

Only used when mode = url, ignored in search mode. Mix freely: product pages under /p/<code>, search pages such as /geladeira/b, department pages under /c/..., or a bare product code such as 1558278013.

## `categories` (type: `array`):

Optional. Keep only products in these store departments. Combine with a keyword, or use on its own to browse a whole department. Selecting several returns products from any of them.

## `brands` (type: `array`):

Optional. Keep only products from these brands, written the way the store writes them, for example Consul, Electrolux, Brastemp, Samsung. Names are matched against the store's own brand list for your search, ignoring case and accents. If none of the names you enter exist in that list the run returns nothing rather than silently ignoring the filter.

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

Optional. Keep only products priced at or above this amount, in Brazilian reais. Applied by the store itself, so it narrows the result count at the source.

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

Optional. Keep only products priced at or below this amount, in Brazilian reais. Applied by the store itself, so it narrows the result count at the source.

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

Optional. Keep only products discounted by at least this percentage off their pre-discount price. Applied by the store itself, so it narrows the result count at the source.

## `minRating` (type: `string`):

Optional. Keep only products the store rates at this many stars or more. The store groups products into whole-star buckets by their rounded average, and this asks it for every bucket at or above your choice, so a product averaging 3.8 counts as 4 stars. Products with no rating at all are excluded when this is set.

## `minInstallments` (type: `integer`):

Optional. Keep only products the store will split into at least this many instalments, for example 12 for '12x sem juros'. The store publishes no filter for instalments, so this is applied by this actor after reading each product's payment terms.

## `interestFreeInstallmentsOnly` (type: `boolean`):

Optional. Keep only products with at least one interest-free ('sem juros') instalment plan. Combine with Minimum instalments to require, for example, 12x with no interest. Applied by this actor after reading each product's payment terms.

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

Optional. Keep only products the store will currently accept an order for. Applied by this actor after reading each product's live availability.

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

The order the store returns results in. All five are the store's own orderings.

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

Read each product's own page to add the description, the full specification table, the colour, model and capacity variants with their own addresses, the manufacturer and warranty information, the full image gallery, the pickup-in-store signal and every instalment plan the store offers. Turn off for a faster, cheaper run that returns catalogue and price fields only.

## `includeReviews` (type: `boolean`):

Add each product's customer reviews: average rating, the per-star breakdown, the share of reviewers who recommend it, the store's own review summary, and the individual reviews (author, date, rating, text and helpful counts). Shopper-uploaded review photos and clips are not returned. Requires Fetch product details and costs no extra request. A product with no reviews returns an empty reviews list.

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

Cap on how many individual reviews are returned per product. The average rating, review count and per-star breakdown are always complete regardless of this cap. Use 0 for every review the product page publishes.

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

Maximum number of products to return across the whole run. This is the run's cap. Use 0 for unlimited.

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

Maximum number of result pages read per keyword, department or link. 0 means no limit, which is the default: the run then stops only at Max products, at the end of the results, or when a page returns nothing new.

## `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 daily 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 is how a price cut or a stock change is spotted. Turn on Emit unchanged or Emit expired only when you also want those rows returned (and billed). State is kept separately for each search, filter and detail 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 let the actor derive a key automatically from the search, filters and detail settings, so different searches never mix state with each other.

## `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 the 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, so never when Max products capped it or when Resume was used. This returns, and bills, extra synthetic rows, so leave it off unless you need expiry tracking.

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

The standard pool is the default and handles catalogue search, pricing and product pages. This store refuses individual exits from time to time rather than whole pools, and a refused request is retried on a fresh exit automatically. If a run still cannot read any results, switching to the RESIDENTIAL group with country BR usually clears it, at a higher price per gigabyte.

## `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 rich page-per-item export; other connectors get a best-effort write or digest. Leave empty to skip; 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 item pages are created. Required to enable the Notion export; ignored by other connectors.

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

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

## Actor input object example

```json
{
  "mode": "search",
  "queries": [
    "geladeira"
  ],
  "urls": [
    "https://www.casasbahia.com.br/refrigerador-philco-prf535i-side-by-side-437-litros/p/1558278013",
    "https://www.casasbahia.com.br/geladeira/b"
  ],
  "minRating": "0",
  "interestFreeInstallmentsOnly": false,
  "inStockOnly": false,
  "sortBy": "relevance",
  "fetchDetails": true,
  "includeReviews": true,
  "maxReviewsPerProduct": 20,
  "maxItems": 20,
  "maxPages": 0,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "proxy": {
    "useApifyProxy": true
  },
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (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": [
        "geladeira"
    ],
    "urls": [
        "https://www.casasbahia.com.br/refrigerador-philco-prf535i-side-by-side-437-litros/p/1558278013",
        "https://www.casasbahia.com.br/geladeira/b"
    ],
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "proxy": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/casasbahia-com-br-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": ["geladeira"],
    "urls": [
        "https://www.casasbahia.com.br/refrigerador-philco-prf535i-side-by-side-437-litros/p/1558278013",
        "https://www.casasbahia.com.br/geladeira/b",
    ],
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "proxy": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/casasbahia-com-br-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": [
    "geladeira"
  ],
  "urls": [
    "https://www.casasbahia.com.br/refrigerador-philco-prf535i-side-by-side-437-litros/p/1558278013",
    "https://www.casasbahia.com.br/geladeira/b"
  ],
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "proxy": {
    "useApifyProxy": true
  }
}' |
apify call abotapi/casasbahia-com-br-scraper --silent --output-dataset

```

## MCP server setup

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