# 99acres Scraper — India Property for Sale & Rent (`tortuga/99acres-scraper`) Actor

Scrape 99acres listings for sale or rent from search URLs or city/locality + filters: price in INR, price/sqft, BHK, area, locality, society, coordinates, amenities, photos, poster, RERA.

- **URL**: https://apify.com/tortuga/99acres-scraper.md
- **Developed by:** [Trevor Ortega](https://apify.com/tortuga) (community)
- **Categories:** Real estate
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 listing scrapeds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 Scraper — India Property for Sale & Rent

Scrape property listings for sale and for rent from **99acres.com**, one of India's largest property portals. Paste any 99acres search URL, or type cities and localities like "Pune", "Andheri West, Mumbai" or "Baner, Pune" and pick filters (BHK, budget, area, property type, owner/dealer/builder, furnishing, ready to move). You get the price in INR, price per sq ft, deposit and brokerage, BHK, bathrooms, carpet / built-up / super built-up / plot area, floor, furnishing, facing, possession or age, society and project, locality, city, map point, amenities, description, photos, posted date, whether an owner, dealer or builder posted it, and RERA numbers. Export as JSON, CSV or Excel.

Residential and commercial listings are both supported (flats, independent houses, builder floors, plots, offices, shops, showrooms, warehouses, land). With **Only new or changed items**, a scheduled run outputs only listings that are new or whose price changed.

### What data does 99acres Scraper extract?

| Field | Example | Notes |
|---|---|---|
| `id`, `propertyId`, `url` | `91686652`, `F91686652`, `https://www.99acres.com/...-spid-F91686652` | 99acres listing id (SPID) and listing URL |
| `title`, `listingType`, `category`, `propertyType` | `2 BHK Flat in Wakad, Pune`, `sale`, `residential`, `Residential Apartment` | `listingType`: `sale`, `rent` or `lease` (commercial) |
| `transactionType`, `availability`, `ageOrPossession`, `availableFrom` | `Resale`, `Ready to move`, `5 to 10 years old` | Under-construction listings give the possession year |
| `price`, `priceText`, `priceMax` | `8700000`, `87 Lac` | Price in rupees (monthly rent for rentals). `priceMax` for ranges |
| `pricePerSqft`, `pricePerSqftBasis` | `12428`, `carpet area` | As 99acres computes it |
| `deposit`, `depositMonths`, `brokerage`, `brokerageRate`, `bookingAmount` | `600000`, `6`, `120000`, `2` | Deposit as rupees or months of rent; brokerage as rupees or a rate |
| `bedrooms`, `bathrooms`, `balconies` | `2`, `2`, `1` | |
| `carpetArea`, `builtUpArea`, `superBuiltUpArea`, `plotArea` | `700` | Each with `...Unit` (as the advertiser entered it) and `...Sqft` |
| `floor`, `totalFloors`, `furnishing`, `facing`, `ownershipType` | `6`, `24`, `Furnished`, `East`, `Freehold` | |
| `society`, `projectId`, `projectUrl` | `ANP Royal Grande` | Society / project the listing belongs to |
| `locality`, `localityId`, `city`, `cityId`, `address` | `Wakad`, `1283`, `Pune West`, `1171162`, `Wakad, Pune` | 99acres splits big cities into zones (Pune West, Mumbai Andheri-Dahisar, ...) |
| `latitude`, `longitude`, `locationAccuracy` | `18.5996253`, `73.7457522`, `verified` | `building` / `locality` point, or `approximate` (rounded to ~1 km for owners' own pins) |
| `amenities`, `amenityIds`, `highlights`, `tags` | `["Gymnasium", "Lift"]`, `["6th out of 24 Floors"]`, `["RERA"]` | |
| `description`, `photos`, `photoCount`, `videos` | | Full description text and photo URLs in every result |
| `postedAt`, `updatedAt`, `postedAgo`, `expiresAt` | `2026-06-06T05:03:55Z`, `2 days ago` | |
| `postedBy`, `advertiserName`, `contactName`, `verified`, `featured` | `Dealer`, `Propline.in`, `Mohini`, `true` | `postedBy`: Owner, Dealer or Builder. Names are given for dealers and builders only, never for private owners. No phone numbers or e-mails |
| `reraId`, `posterReraId`, `posterReraRegistered`, `newLaunch` | `P52100015350`, `A52100043900`, `true` | Project and agent RERA registration numbers when published |
| With **Include listing page details**: `reraStatus`, `homeAmenities`, `societyAmenities`, `flooring`, `overlooking`, `powerBackup`, `widthOfFacingRoad`, `priceNegotiable`, `gatedCommunity`, `additionalRooms`, `tenantPreference`, `lastUpdatedLabel`, `advertiserMemberSince`, `advertiserListings` | `REGISTERED`, `["Private Garden"]`, `Vitrified`, `Partial`, `true`, `Family Only` | From the listing's own page |
| `search`, `scrapedAt`, `detailsFetched` | | Which start URL or location found the listing |
| `changeType`, `previousValues` | `changed`, `{"price": 9000000}` | Only with monitoring on |

### How to scrape 99acres listings

1. Open [99acres.com](https://www.99acres.com), search with the filters you want and copy the address bar into **Start URLs**. Both kinds of URLs work: `https://www.99acres.com/property-for-rent-in-pune-ffid` and `https://www.99acres.com/search/property/buy/pune?city=19&bedroom_num=2&preference=S&res_com=R`.
2. Or leave Start URLs empty and type **Locations**, one per line, the way 99acres names them: "Pune", "Mumbai", "Bangalore", "Gurgaon", "Baner, Pune", "Andheri West, Mumbai". Then pick **For sale or for rent**, residential or commercial, and the filters you need: property types, BHK, budget, area, posted by owner / dealer / builder, ready to move or under construction, furnishing.
3. Set **Max items** and start the run. To get one listing with every detail, paste its URL (`...-spid-V92517588`).

### Input example

```json
{
  "locations": ["Baner, Pune", "Wakad, Pune"],
  "listingType": "sale",
  "propertyTypes": ["apartment"],
  "bedrooms": ["2", "3"],
  "minPrice": 5000000,
  "maxPrice": 15000000,
  "postedBy": ["owner"],
  "sort": "newest",
  "maxItems": 500
}
```

Start URLs and single listings can be mixed in one run:

```json
{
  "startUrls": [
    { "url": "https://www.99acres.com/property-for-rent-in-pune-ffid" },
    { "url": "https://www.99acres.com/search/property/buy/mumbai?city=12&preference=S&res_com=C" },
    { "url": "https://www.99acres.com/2-bhk-bedroom-apartment-flat-for-rent-in-ganga-platino-kharadi-pune-1059-sqft-spid-J93563578" }
  ],
  "maxItems": 2000
}
```

### Output example

```json
{
  "id": "91686652",
  "propertyId": "F91686652",
  "url": "https://www.99acres.com/2-bhk-bedroom-apartment-flat-for-sale-in-anp-royal-grande-wakad-pune-700-sqft-r1-spid-F91686652",
  "title": "2 BHK Flat in Wakad, Pune",
  "listingType": "sale",
  "category": "residential",
  "propertyType": "Residential Apartment",
  "transactionType": "Resale",
  "price": 8700000,
  "priceText": "87 Lac",
  "pricePerSqft": 12428,
  "pricePerSqftBasis": "carpet area",
  "brokerageRate": 2,
  "bookingAmount": 200000,
  "bedrooms": 2,
  "bathrooms": 2,
  "balconies": 1,
  "carpetArea": 700,
  "carpetAreaUnit": "sq.ft.",
  "carpetAreaSqft": 700,
  "floor": "6",
  "totalFloors": 24,
  "furnishing": "Furnished",
  "facing": "East",
  "availability": "Ready to move",
  "ageOrPossession": "5 to 10 years old",
  "ownershipType": "Freehold",
  "society": "ANP Royal Grande",
  "projectUrl": "https://www.99acres.com/anp-royal-grande-wakad-pune-npxid-r274153",
  "locality": "Wakad",
  "city": "Pune West",
  "address": "Wakad, Pune",
  "latitude": 18.5996253,
  "longitude": 73.7457522,
  "locationAccuracy": "verified",
  "amenities": ["Gymnasium", "Club house", "Vaastu Compliant", "Park", "Gas Pipeline", "Lift"],
  "highlights": ["6th out of 24 Floors", "Overlooking Main Road", "East Facing", "1 Covered Parking"],
  "tags": ["READY TO MOVE", "RESALE", "RERA"],
  "description": "This property faces the east direction. Containing 2 bedroom(s), 2 bathrooms and 1 balcony, ...",
  "photos": ["https://imagecdn.99acres.com/media1/42160/2/843202311M-1789896144357.jpg"],
  "photoCount": 8,
  "videos": ["https://www.youtube.com/watch?v=wgGQGJOdFK0"],
  "postedAt": "2026-06-06T05:03:55Z",
  "updatedAt": "2026-10-08T14:31:32Z",
  "postedAgo": "2 days ago",
  "expiresAt": "2026-12-07",
  "postedBy": "Dealer",
  "advertiserName": "Propline.in",
  "contactName": "Mohini",
  "verified": true,
  "featured": false,
  "reraId": "P52100015350",
  "posterReraId": "A52100043900",
  "posterReraRegistered": true,
  "detailsFetched": false,
  "search": "Pune (sale, residential)",
  "scrapedAt": "2026-10-10T07:52:30Z"
}
```

### How many listings can I get per search?

All of them. 99acres shows 25 listings per result page and does not stop at a page limit: in tests, page 1,590 of a 39,914-listing search (every flat, house and plot for sale in Pune) still returned 25 listings. One run can therefore collect a whole city; **Max items** decides where it stops. "New project" groupings are turned off, so every result is an individual listing, and a listing that moves between pages while the run reads them is output and charged once.

### Does it work without login? Does it collect phone numbers?

No login is needed: the actor reads the same public pages any visitor sees. It never collects phone numbers or e-mail addresses. 99acres shows them only after a click ("View Number" / "Contact"), often only to logged-in users, and the actor does not click or log in. It exports the advertiser type (owner, dealer, builder), the dealer or agency name, the contact name that 99acres publishes with the listing, and RERA registration numbers. Map points that an individual owner placed on their own property are rounded to about 1 km; society, building and locality points are kept as published.

### Do I need a proxy?

Yes. 99acres answers datacenter IPs with its own bot check, so the default is Apify **RESIDENTIAL** proxy in India. Some residential requests get the check too; the actor retries them on a new IP. Residential traffic is billed by Apify per GB: about 0.16 MB per result page of 25 listings (about 6.5 MB, roughly $0.05-0.06, per 1,000 listings) and about 0.1 MB (about $0.0008) per listing page when **Include listing page details** is on.

### Monitor changes on a schedule

Turn on **Only new or changed items** to watch a search, for example rentals in one locality sorted newest first. Each run remembers what earlier runs of the same search returned and outputs only new listings and listings whose price changed; unchanged listings are dropped before any listing page request, so they are not charged. **Max items** then sets how many of the newest listings are checked per run (e.g. 50-300 for a locality). Save the input as a task in Apify and add a Schedule (for example daily); give each watch its own **Monitoring key** if several schedules use the same search. Every output item gets `changeType` (`new` or `changed`), and changed items get `previousValues` with the old price, e.g. `{"price": 9000000}`.

### Pricing

Pay per result: each listing saved to the dataset is one `result` event. With **Include listing page details** on (and for single listing URLs), each listing whose own page is loaded adds one `detail` event. Listings repeated on a later page and unchanged listings in monitoring mode are not charged. Platform usage (mostly residential proxy traffic, see above) is paid by you: a 300-listing run used $0.023 of platform usage in tests, about $0.08 per 1,000 listings.

### Integrations and API

Run it from the Apify API, a schedule, Make, Zapier, n8n or any language with the Apify client; results are available as JSON, CSV, Excel or XML from the dataset. Each listing's `search` field tells you which start URL or location found it.

### Is it legal to scrape 99acres?

This actor collects listing data that anyone can see on 99acres without logging in. It does not log in and does not collect phone numbers, e-mail addresses or anything else shown only after a click. Advertiser and contact names (including individual owners' names published with their listings) are personal data under India's Digital Personal Data Protection Act, 2023: you are responsible for having a legitimate purpose for processing them and for respecting 99acres' terms of use. If in doubt, ask a lawyer.

### Support

Found a bug or need a field that is not there? Open an issue on the Issues tab with the input you used and the run link.

# Actor input Schema

## `startUrls` (type: `array`):

99acres search pages with any filters set in the browser (copy the address bar, e.g. https://www.99acres.com/property-for-rent-in-pune-ffid or https://www.99acres.com/search/property/buy/pune?city=19\&bedroom_num=2\&preference=S\&res_com=R), or single listing URLs (...-spid-V92517588), which return that listing with full details. Leave empty if you use Locations below.

## `locations` (type: `array`):

Cities or localities, one per line, as 99acres names them: 'Pune', 'Mumbai', 'Bangalore', 'Gurgaon', 'Baner, Pune', 'Andheri West, Mumbai'. Each is opened on 99acres (one page) to find its city/locality id and then searched with the filters below.

## `listingType` (type: `string`):

Listings for sale or for rent (commercial: lease). Applies to Locations; start URLs keep their own.

## `propertyCategory` (type: `string`):

Residential properties (flats, houses, plots) or commercial ones (offices, shops, land, warehouses). Applies to Locations.

## `propertyTypes` (type: `array`):

Only these property types (pick residential types for residential searches, commercial types for commercial ones). Leave empty for all.

## `bedrooms` (type: `array`):

Number of bedrooms; pick one or more. '5+' means 5 or more. Leave empty for any.

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

Minimum price in rupees (sale price, or monthly rent for rentals), e.g. 5000000 for 50 lakh. 99acres widens budget filters slightly, so a few listings just outside the range can appear.

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

Maximum price in rupees (sale price, or monthly rent for rentals), e.g. 10000000 for 1 crore.

## `minArea` (type: `integer`):

Minimum area in square feet.

## `maxArea` (type: `integer`):

Maximum area in square feet.

## `postedBy` (type: `array`):

Only listings posted by these advertiser types. Leave empty for all.

## `constructionStatus` (type: `string`):

Ready to move or under construction. Leave empty for both.

## `furnishing` (type: `array`):

Furnishing status. Leave empty for any.

## `sort` (type: `string`):

Order of the results. Applies to Locations and to start URLs without a sort of their own. 'Newest first' is best for monitoring; 'Relevance' is the site's default order (paid featured listings first).

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

Stop after this many listings in total (across all searches). Keeps cost predictable. 99acres shows 25 listings per page; deep pages work (tested to 39,000+ listings in one search).

## `includeDetails` (type: `boolean`):

Also open each listing's own page: project RERA registration number and status, full amenity lists (flat and society), flooring, overlooking, power backup, road width, price negotiable, gated community, tenant preference. One extra page per listing; charged as an extra 'detail' event.

## `onlyNewOrChanged` (type: `boolean`):

Remember what earlier runs of this search returned and output only new listings or listings whose price changed. Use with a Schedule; unchanged items are not charged. With this on, Max items is the number of listings checked per run (e.g. the newest 200).

## `monitoringKey` (type: `string`):

Name for this watch, so several schedules on the same search stay separate; defaults to a key derived from the search input

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

99acres shows its own bot check to datacenter IPs, so Apify RESIDENTIAL proxy in India is the default (some residential requests also get the check; they are retried on a new IP). Residential traffic is billed by Apify per GB: about 0.15 MB per result page of 25 listings (about 6 MB per 1,000 listings) and about 0.1 MB per listing page with details.

## Actor input object example

```json
{
  "locations": [
    "Pune"
  ],
  "listingType": "sale",
  "propertyCategory": "residential",
  "sort": "newest",
  "maxItems": 100,
  "includeDetails": false,
  "onlyNewOrChanged": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "IN"
  }
}
```

# 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 = {
    "locations": [
        "Pune"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "IN"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("tortuga/99acres-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 = {
    "locations": ["Pune"],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "IN",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("tortuga/99acres-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 '{
  "locations": [
    "Pune"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "IN"
  }
}' |
apify call tortuga/99acres-scraper --silent --output-dataset

```

## MCP server setup

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