# Reclame Aqui Scraper - Complaints & Reputation (`dami_studio/reclame-aqui-scraper`) Actor

Scrape Reclame Aqui, Brazil's consumer complaint site. One row per company: reputation score, index status, answer and resolution rates across five periods. One row per complaint: full text, the company's reply, the consumer's 0-10 rating, city and date. No login, no API key.

- **URL**: https://apify.com/dami\_studio/reclame-aqui-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** E-commerce, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $9.20 / 1,000 row returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

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

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

## How to integrate an Actor?

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

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

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

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

# README

## Reclame Aqui Scraper: complaints, replies and company reputation

Pulls two things off Reclame Aqui, Brazil's public consumer-complaint platform:

1. **The company's reputation card.** The score out of 10, the index status (RA1000, Ótimo, Bom,
   Regular, Ruim, Não Recomendada), how many complaints came in, how many were answered, how many
   the consumer marked resolved, how many said they would buy again, and the average time to a first
   answer, for **every period the site publishes**: last 6 months, last 12 months, last calendar
   year, the year before that, and the last 3 years.
2. **The complaints themselves.** Title, the full text the consumer wrote, status, the company's
   reply, the consumer's closing 0–10 score and comment, whether they would deal with the company
   again, city, state, the dates, and the public URL.

Give it a company and it returns both. Nothing else to configure.

***

### What you put in

Any of these three, one per line, mixed freely:

```json
{
  "companies": [
    "https://www.reclameaqui.com.br/empresa/nubank/",
    "magazine-luiza-loja-online",
    "Casas Bahia"
  ],
  "maxComplaintsPerCompany": 50,
  "includeCompanyProfile": true,
  "includeCompanyReply": true,
  "sortOrder": "newest"
}
```

A brand typed out gets looked up the same way the site's own search box does, and the best match
wins. **Big Brazilian retailers are split into several separate pages.** "Loja Online", "Loja
Física", "Marketplace" and the store card are four different companies with four different scores.
If that distinction matters to you, paste the exact URL. When the actor has to guess, the company
row carries `matchedBy` and an `otherPagesForThisBrand` list so you can see what else it could have
picked.

#### Filters

| Input | What it does |
|---|---|
| `complaintStatus` | `ANSWERED` (the company replied) or `PENDING` (it has not). Those are the only two Reclame Aqui itself filters on |
| `resolution` | only resolved, or only unresolved |
| `onlyRated` | only complaints the consumer came back and scored |
| `sortOrder` | newest first, or oldest first. Oldest first starts at the company's very first complaint, which for a long-established brand means 2013 |
| `maxComplaintsPerCompany` | set it to `0` to get the reputation card and skip the complaints entirely |

***

### What comes out

Two row shapes in one dataset, told apart by `rowType`.

#### `rowType: "company"`

```json
{
  "rowType": "company",
  "companyId": "88850",
  "companyName": "Nubank",
  "shortname": "nubank",
  "url": "https://www.reclameaqui.com.br/empresa/nubank/",
  "cnpj": "18236120000158",
  "segment": "Bancos e Financeiras",
  "defaultPeriod": "last6Months",
  "reputationScore": 8.7,
  "indexStatus": "RA1000",
  "consumerScore": 7.73,
  "answeredPercent": 99.9,
  "resolvedPercent": 93.3,
  "wouldBuyAgainPercent": 81.2,
  "averageAnswerHours": 98.1,
  "complaintsTotalAllTime": 60372,
  "complaintsLast30Days": 9393,
  "periods": [
    {
      "period": "last12Months",
      "from": "2025-09-01T00:00:00",
      "to": "2026-08-31T23:59:59",
      "reputationScore": 8.7,
      "indexStatus": "RA1000",
      "complaints": 115311,
      "complaintsAnswered": 115064,
      "complaintsNotAnswered": 148,
      "complaintsRated": 48845,
      "answeredPercent": 99.8,
      "resolvedPercent": 92.4,
      "wouldBuyAgainPercent": 80.2,
      "averageAnswerHours": 114.2
    }
  ],
  "verifiedCompany": true,
  "socialProfiles": [{ "type": "instagram", "url": "https://www.instagram.com/nubank/" }]
}
```

The headline numbers on that row are the period the site shows by default, the last six months.
The other four are in `periods`, same shape, so a year-on-year comparison is one `map` away.

#### `rowType: "complaint"`

```json
{
  "rowType": "complaint",
  "complaintId": "l06ktVrFogTC0m_D",
  "complaintNumber": "259446661",
  "companyName": "Nubank",
  "title": "Nubank está retendo os meus contratos de empréstimo consignado do INSS",
  "text": "Nubank retendo meus contratos de empréstimo consignado do inss...",
  "status": "ANSWERED",
  "answered": true,
  "resolved": false,
  "rated": true,
  "consumerScore": 0,
  "consumerComment": "infelizmente continua retendo meus contratos...",
  "wouldBuyAgain": false,
  "companyReply": "Olá. Somos o time do Reclame Aqui do Nubank e recebemos a sua manifestação...",
  "companyReplyAt": "2026-09-18T17:27:49",
  "companyReplyCount": 1,
  "consumerReplies": [{ "text": "Continua sem solução.", "at": "2026-09-18T17:46:20" }],
  "city": "Rio de Janeiro",
  "state": "RJ",
  "publishedAt": "2026-09-18T16:30:22",
  "url": "https://www.reclameaqui.com.br/nubank/nubank-esta-retendo..._l06ktVrFogTC0m_D/"
}
```

`status` is the company's side: `ANSWERED` or `PENDING`. `resolved` is the consumer's side, and it
only means something once `rated` is true. Most complaints from the last few days have not been
rated yet, so `resolved: false` on a two-day-old row means "not yet", not "the company failed".

***

### What this does not do

- **No consumer names, and none are available.** Reclame Aqui does not publish them. The API returns
  `****` for the complainant's name, e-mail and IP, and the public page shows only the city, the
  state and the date. So this actor emits the city and the state and nothing else about the person.
- **Free text gets masked before it is written.** People do paste their own CPF, phone number or
  e-mail into a complaint, and companies sometimes quote it back. Every string field is scanned for
  e-mail addresses, CPF, CNPJ, CEP and Brazilian phone numbers, and any hit is replaced with
  `[CPF removido]` and friends. The run log says how many it caught. It is a pattern match, not
  magic, and it will not catch a number someone wrote out in words.
- **5,100 complaints per company, per filter.** That is Reclame Aqui's own listing ceiling, not a
  choice made here. To go further back, run the same company again with a filter set. Each
  combination of status, resolution and rating is its own 5,100-row listing, and `sortOrder:
  "oldest"` opens the other end of the history.
- **No private or deleted complaints**, no "Reclame Aqui Verificada" internal tickets, no
  attachments, no images, no audio. Only what a signed-out visitor can read.
- **Sub-brands are separate companies.** There is no roll-up for a group. Ask for each page.
- **`averageAnswerHours` is the site's own figure**, carried through unchanged, not something
  computed from the complaints in your run.
- **It is paced.** Reclame Aqui rate-limits bursts hard, so the actor keeps two requests in the air
  and slows itself down when it sees a refusal. Measured: 60 complaints plus the company card came
  back in 30 seconds with replies on, and 10 seconds with them off. Scale from there, and expect a
  few thousand complaints to take minutes, not seconds.
- **No sentiment scoring, no categories, no summaries.** It returns what the site says.

***

### Pricing

Pay per event. You are charged **once per data row**: one charge for a company profile row, one
charge for each complaint row. Nothing else bills.

**Free, always:**

- the sample row an empty run returns
- every diagnostic row: a company Reclame Aqui does not have, a malformed input, a listing that
  came back empty, a run cut short by a refusal
- the company lookup when you type a brand name instead of a short name

If a run returns nothing you asked for, it charges nothing for it.

***

### FAQ

**Is this the same score I see on the company's page?**
Yes. `reputationScore` and `indexStatus` are the figures Reclame Aqui publishes on the company page,
for the same default period the page shows.

**Do I need a Reclame Aqui login or an API key?**
No. Everything here is what a signed-out visitor can see.

**Can I track a company over time?**
Run it on a schedule with `maxComplaintsPerCompany: 0`. That returns just the reputation card, one
row, one charge, and the five periods let you see the trend inside a single run.

**Can I get the complaints my own company received?**
Yes, and it is the main use for it. Put your own Reclame Aqui page in `companies`, set
`complaintStatus` to `PENDING`, and you get everything waiting on an answer, with the text, the
city and the link.

**Why is `companyReply` null on some rows?**
Either the company has not replied yet (`answered: false`), or `includeCompanyReply` was off. The
reply text lives on the complaint's own page, not in the listing, so it needs that extra read.

**Why did "Americanas" return a different company than I expected?**
Because several exist. The company row's `otherPagesForThisBrand` lists the ones it did not pick. Copy the short name you want and put that in `companies` instead.

**How far back does it go?**
As far as the company's history runs. Asking for the oldest first on a long-established retailer
came back with a complaint filed in May 2013.

**What happens if Reclame Aqui refuses mid-run?**
The rows already returned stay, and stay charged, because they are real. The run then writes one
uncharged diagnostic row saying where it stopped and exits successfully rather than failing. A
failed run would still bill you the start fee for nothing.

**Which fields can be null?**
`consumerScore`, `consumerComment` and `wouldBuyAgain` until the consumer rates the complaint.
`companyReply` until the company answers. `cnpj`, `website` and `socialProfiles` on companies that
never filled them in. `reputationScore` and the percentages on a company that does not currently
carry an index. The site shows `--` there and this returns `null` rather than inventing a zero.

# Actor input Schema

## `companies` (type: `array`):

One company per line. A Reclame Aqui page URL (https://www.reclameaqui.com.br/empresa/nubank/), the short name from that URL (nubank), or the brand written out (Magazine Luiza). Written-out names are looked up and the best match is used — big retailers are split into several pages (loja online, loja física, marketplace), so paste the exact URL when you care which one you get.

## `maxComplaintsPerCompany` (type: `integer`):

How many complaints to pull for each company, newest first. Set 0 to get the company profile only. Reclame Aqui's own listing stops at 5100 complaints per filter combination, so that is the ceiling here too.

## `includeCompanyProfile` (type: `boolean`):

Writes one row per company with the reputation score, the index status (RA1000, Otimo, Bom, Regular, Nao Recomendada), and the answer, resolution and would-buy-again rates for every period Reclame Aqui publishes.

## `includeCompanyReply` (type: `boolean`):

Reads each complaint's own page to get what the company actually wrote back, plus the consumer's follow-ups. This is one extra request per complaint, so a run with it on takes roughly twice as long. Turn it off if you only need titles, status and ratings.

## `complaintStatus` (type: `string`):

Reclame Aqui only filters on these two. "Answered" means the company has replied at least once; "Waiting for a reply" means it has not.

## `resolution` (type: `string`):

Only complaints the consumer marked as resolved, or only the ones they marked unresolved. Both of these imply the consumer came back and rated the outcome.

## `onlyRated` (type: `boolean`):

Keeps the complaints that have a 0-10 score and a closing comment from the consumer. Most recent complaints do not yet.

## `sortOrder` (type: `string`):

Newest first is what the site shows. Oldest first starts at the company's very first complaint, which is the way to read a company's history from the beginning.

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

Optional. Leave this alone unless you need the run to go out through a particular network. Your own proxy servers are used exactly as given.

## Actor input object example

```json
{
  "companies": [
    "nubank"
  ],
  "maxComplaintsPerCompany": 50,
  "includeCompanyProfile": true,
  "includeCompanyReply": true,
  "sortOrder": "newest",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (type: `string`):

One row per company profile (reputation score, index status, answer and resolution rates for every period Reclame Aqui publishes, complaint totals, segment, CNPJ, address) and one row per complaint (title, full text, status, resolved flag, the company's reply, the consumer's final rating and comment, city, state, date and the public URL). Empty input, an unknown company or a page Reclame Aqui refuses writes an uncharged sample or diagnostic row instead.

# 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 = {
    "companies": [
        "nubank"
    ],
    "maxComplaintsPerCompany": 50,
    "includeCompanyProfile": true,
    "includeCompanyReply": true,
    "complaintStatus": "",
    "resolution": "",
    "onlyRated": false,
    "sortOrder": "newest",
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/reclame-aqui-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 = {
    "companies": ["nubank"],
    "maxComplaintsPerCompany": 50,
    "includeCompanyProfile": True,
    "includeCompanyReply": True,
    "complaintStatus": "",
    "resolution": "",
    "onlyRated": False,
    "sortOrder": "newest",
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/reclame-aqui-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 '{
  "companies": [
    "nubank"
  ],
  "maxComplaintsPerCompany": 50,
  "includeCompanyProfile": true,
  "includeCompanyReply": true,
  "complaintStatus": "",
  "resolution": "",
  "onlyRated": false,
  "sortOrder": "newest",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call dami_studio/reclame-aqui-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/reclame-aqui-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/bsbMqpc90fXfOCOIM/builds/z7fYyKz3CVHgi6edg/openapi.json
