# 99acres.com Property Scraper (India) (`automatic/99acres-in-scraper`) Actor

Collect public property listings from 99acres.com by Indian city, for sale or rent. Each record has the listing URL, asking price in INR, area, bedrooms, locality, city, images and the time collected. Choose your cities, buy or rent, and a result limit — every run returns fresh listings.

- **URL**: https://apify.com/automatic/99acres-in-scraper.md
- **Developed by:** [Philip Kirkbride](https://apify.com/automatic) (community)
- **Categories:** Real estate
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 84.6% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.60 / 1,000 listings

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

## 99acres.com Property Scraper (India)

Collect public property listings and project developments from 99acres.com across major Indian cities. Every record has one normalized shape — URL, price in INR, area in sq.ft., bedrooms (BHK), locality, city, sale or rent, and images — so you can build shortlists, track asking prices and enrich leads with fresh data from each run.

### Quick Start

```json
{"cities":["Bangalore"],"operation":"buy","maxResults":50}
```

The Console prefill searches Bangalore for up to 20 results. An empty input (`{}`), or an input that omits both city fields, uses the six default cities, with up to 5 pages per city and 50 results overall. `maxPages` and `maxResults` are limits, not promised counts; narrow the city list before raising them.

### Input

| Input | Type | Default | Meaning |
| --- | --- | --- | --- |
| `cities` | array | `["Bangalore","Mumbai","Delhi","Pune","Hyderabad","Chennai"]` | Indian cities to search. Free-text city names are accepted. |
| `city` | string | empty | Single-city shorthand, for example `"Mumbai"`. |
| `operation` | string | `buy` | `buy` for sale listings or `rent` for rental listings. |
| `maxPages` | integer | `5` | Maximum result pages per city, 1–10. A city stops early once `maxResults` is reached or a page returns no results. |
| `maxResults` | integer | `50` | Maximum listings across all cities, 1–200. |
| `requestDelaySecs` | number | `1.0` | Pause between page requests, minimum 0.5 s. |
| `proxyUrl`, `proxyUsername`, `proxyPassword` | string | empty | Optional custom proxy. Leave empty to use the actor's default connection. |

### What it returns

Each run returns one record per search result. A result is either an individual **property** or a **project development**, identified by `resultType`.

Common fields: `listing_key` (stable join key), `url` (canonical listing URL), `resultType` (`property` or `project`), `title`, `locality`, `city`, `listing_type` (`buy`/`rent`), `images`, `source` and `scraped_at`.

**Property rows** keep the flat fields:

| Field | Meaning |
| --- | --- |
| `price_inr` | Absolute asking price in INR. For a price range this is the lower bound; `null` when the listing says "price on request" or shows only a per-area rate. |
| `priceMinInr`, `priceMaxInr` | Both ends of a source price range; equal to `price_inr` for a single price. |
| `price_per_sqft_inr` | Asking rate in INR per sq.ft., when the source gives one. A per-square-yard or per-square-metre rate is converted to per sq.ft. so the number matches the field name. |
| `price_text` | The price exactly as shown, for example `₹ 2 Cr` or `₹ 8,982 /sq.ft.`. |
| `area_sqft` | Area normalized to sq.ft. |
| `areaMinSqft`, `areaMaxSqft` | Both ends of an area range; equal to `area_sqft` for a single value. |
| `bedrooms`, `bedroomsMin`, `bedroomsMax` | Bedrooms (BHK) as a single value or as range bounds. |
| `configurations` | `null` for a property row. |

**Project development rows** describe a development instead of one flat: `projectUnitId`, `priceMinInr`/`priceMaxInr`, `areaMinSqft`/`areaMaxSqft`, `bedroomsMin`/`bedroomsMax`, and `configurations` (one entry per BHK card with its own area and price range). The single-value `price_inr`, `area_sqft` and `bedrooms` are `null` because a development is not one unit.

Only real listing or project photos are returned; interface images such as arrows, icons and placeholders are dropped.

### Output example

Illustrative property and project rows (JSON Lines):

```jsonl
{"listing_key":"99acres.com:Z72955163","url":"https://www.99acres.com/residential-land-plot-for-sale-in-banashankari-stage-6-block-3-bangalore-south-133-sqyd-r5-spid-Z72955163","resultType":"property","title":"Residential land / Plot in Banashankari 6th Stage 3rd Block","projectUnitId":null,"price_inr":20000000,"priceMinInr":20000000,"priceMaxInr":20000000,"price_per_sqft_inr":null,"price_text":"₹ 2 Cr","area_sqft":1200.0,"areaMinSqft":1200.0,"areaMaxSqft":1200.0,"bedrooms":null,"bedroomsMin":null,"bedroomsMax":null,"configurations":null,"locality":"Banashankari 6th Stage 3rd Block, Banashankari Stage 6","city":"Bangalore","listing_type":"buy","images":["https://imagecdn.99acres.com/media1/23274/17/465497998M-1768900002221.jpg"],"source":"99acres.com","scraped_at":"2026-10-18T12:00:00+00:00"}
{"listing_key":"99acres.com:r443829","url":"https://www.99acres.com/reviva-by-ssi-chandapura-bangalore-south-npxid-r443829","resultType":"project","title":"Reviva By SSI","projectUnitId":"443829","price_inr":null,"priceMinInr":7942000,"priceMaxInr":11324000,"price_per_sqft_inr":null,"price_text":null,"area_sqft":null,"areaMinSqft":1045.0,"areaMaxSqft":1490.0,"bedrooms":null,"bedroomsMin":2,"bedroomsMax":3,"configurations":[{"label":"2 BHK","areaMinSqft":1045.0,"areaMaxSqft":1085.0,"priceMinInr":7942000,"priceMaxInr":8246000,"bedrooms":2},{"label":"3 BHK","areaMinSqft":1315.0,"areaMaxSqft":1490.0,"priceMinInr":9994000,"priceMaxInr":11324000,"bedrooms":3}],"locality":"Chandapura","city":"Bangalore","listing_type":"buy","images":["https://newprojects.99acres.com/projects/sri_sai_infra_bangalore/reviva_by_ssi/images/rp2cv6d_1747209613_595772169_med.jpg"],"source":"99acres.com","scraped_at":"2026-10-18T12:00:00+00:00"}
```

### Limits and caveats

- Asking prices, areas and availability can change; verify a listing before making a decision.
- A price that is not an absolute INR is returned as `null` rather than guessed. A per-area rate is stored in `price_per_sqft_inr`, not in `price_inr`.
- Coverage is limited to the mapped cities and to the requested page and result limits. Up to 10 pages per city and 200 results per run.
- Results are collected during the run and are not a publication date or proof of a price change.
- Unavailable fields are `null`, not invented values.
- Listings are saved page by page. A run with saved listings succeeds even if another city is blocked; the status names the blocked cities. This is partial coverage, not a complete search of every requested city.
- Collection stops before the run timeout, with the status `Stopped before the run timeout; saved N listings`. Increase the timeout to collect more.
- A run with no saved listings fails with a plain status message instead of returning a silent empty dataset. An empty search page is checked again on a fresh connection before it is accepted.

### FAQ

**How fresh is the data?** Listings are collected from 99acres.com during each run.

**Which cities are supported?** The mapped Indian cities, with a single-city shorthand for convenience.

**Can I collect rentals?** Yes. Set `operation` to `rent`.

**Why is a price `null`?** The listing either states "price on request" or shows only a per-area rate. In the second case the rate is available in `price_per_sqft_inr`.

**How many pages does a run fetch?** Five per city by default. Lower `maxPages` to 1 to fetch only the first page of each city, or raise it up to 10.

### Price

**$0.0006 per listing** delivered, plus **$0.00005 per run start**. The run-start event is charged per GB of memory; the default 1024 MB run is one start event. The start is charged even if the run saves no results. Platform usage is included in these prices.

For help, contact Apify Support and include the run URL.

# Actor input Schema

## `cities` (type: `array`):

Indian cities to search. Defaults to the six cities; free-text city names are slugified too. A single `city` string input is accepted as an alias.

## `operation` (type: `string`):

Listings to search: buy (property-for-sale-...) or rent (property-for-rent-...).

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

Result pages to fetch per city (about 27-30 entries per page, paged with ?page=N). The runner stops early when a page has no results or maxResults is reached. Default 5, enough for a single-city maxResults=50 run (about 2 pages).

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

Stop after this many listings across all cities.

## `requestDelaySecs` (type: `number`):

Minimum 0.5 seconds.

## `browserFallback` (type: `boolean`):

After bounded connection retries, try interstitial pages in a headless browser. If a city stays blocked, keep any listings already saved and name the blocked cities in the run status.

## `proxyUrl` (type: `string`):

Optional custom proxy URL. Either a full URL with embedded credentials, or a bare host:port together with proxyUsername/proxyPassword. Leave empty to use the Actor's default connection.

## `proxyUsername` (type: `string`):

Username for proxyUrl when the URL does not embed credentials.

## `proxyPassword` (type: `string`):

Password for proxyUrl when the URL does not embed credentials.

## Actor input object example

```json
{
  "cities": [
    "Bangalore"
  ],
  "operation": "buy",
  "maxPages": 5,
  "maxResults": 20,
  "requestDelaySecs": 1,
  "browserFallback": true,
  "proxyPassword": ""
}
```

# Actor output Schema

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

No description

# API

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

## JavaScript example

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

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

// Prepare Actor input
const input = {
    "cities": [
        "Bangalore"
    ],
    "maxResults": 20,
    "proxyPassword": ""
};

// Run the Actor and wait for it to finish
const run = await client.actor("automatic/99acres-in-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 = {
    "cities": ["Bangalore"],
    "maxResults": 20,
    "proxyPassword": "",
}

# Run the Actor and wait for it to finish
run = client.actor("automatic/99acres-in-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 '{
  "cities": [
    "Bangalore"
  ],
  "maxResults": 20,
  "proxyPassword": ""
}' |
apify call automatic/99acres-in-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automatic/99acres-in-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/XfWT0JoRREGYsh8Ax/builds/Kxl70YMmrAHckq5Iu/openapi.json
