# District Stores Scraper: India Store Locator, Malls & Offers (`yugenox/district-stores-scraper`) Actor

Store locator data for 23,500+ branded retail outlets in India from District (by Zomato): every store in a city, of a brand or in a mall, with phone, address, GPS, hours, mall and floor, ratings, live bank and card offers and promo codes. No login.

- **URL**: https://apify.com/yugenox/district-stores-scraper.md
- **Developed by:** [Yugenox Corp](https://apify.com/yugenox) (community)
- **Categories:** E-commerce, Lead generation, 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 store with full details

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

## District Stores Scraper: India Brand Outlets, Malls & Offers

Scrape branded retail stores across India from **District by Zomato**, the app's *Stores* tab. It covers about 23,500 outlets of fashion, footwear, eyewear, jewellery, beauty, salon, electronics and home brands. Each row carries:

- The store's **phone number**, full address, PIN code and **GPS coordinates**
- Weekly opening hours
- The **mall and floor**
- Its categories and product types, with a rating where District shows one
- Every **live offer**: in-store sales, pay-via-District discounts, bank and card offers with their promo codes, minimum bill and full terms, and time-slot discounts

No login, no cookies and no API key are needed.

### What you can scrape

| Mode | You give | You get |
|---|---|---|
| **Every store in a city** | cities (`Mumbai`, `Delhi NCR`, `Pune`…) | every District store in each city: the complete directory plus the live listing (Chandigarh tricity: 335 stores in 21 s) |
| **Every outlet of a brand** | brands (`Lenskart`, `Levi's`, `Tanishq`…), optionally cities | the brand's whole District footprint, for example 591 Levi's outlets or 2,100+ Lenskart stores |
| **Stores near a place** | a city, an area (`Koramangala, Bengaluru`) or coordinates | the nearest outlet of each brand (up to 600 brands), nearest first. Set a radius to get every outlet inside it |
| **Every store in a mall** | mall names, district.in/hub/… links, or places | the mall's tenant list with floors (Ambience Mall Gurgaon: 146 stores). Give a place instead and you get every mall and market with District stores there |
| **Search** | search terms + places | what District's own store search returns |
| **Specific stores** | district.in/stores/… links | full details for each one |
| **All of India** | nothing (or brands / categories as filters) | the whole store directory |

Every mode can be narrowed by **category** (apparel, footwear, accessories, jewellery, beauty, salon & wellness, home & furniture, electronics, eyewear, bags & luggage, watches, sports, kids, gifting) and by **brand**.

### What each row contains

- **Identity:** store id, name, brand, brand id, District link, category, sub-categories and product types (for example "Sneakers", "Loafers", "Backpacks")
- **Location:** full street address, locality, city, state, PIN code, latitude/longitude, Google Maps link, mall name and id, floor
- **Contact and hours:** the store's phone number(s), and opening hours per day plus a compact summary like `Mon-Sun 10:00-21:00`
- **Offers:**
  - A headline offer.
  - Every live offer: section, title, description, promo code, minimum bill and full terms.
  - Promo codes with their rupee savings.
  - The pay-via-District discount (percent, cap, validity).
  - Time-slot discounts.
  - The District payment mode.
  - Accepted payment methods.
- **Rating:** rating and rating count, where District shows one
- **Media:** logo, cover image, photo gallery and brand video. Optional: the product catalogue (named collections with product names and images)
- **Shop-in-shop brands** for department stores (for example the 20+ brands inside a Shoppers Stop)
- `distanceKm` from your point (nearby and search modes), `source` (the query that found the store) and `scrapedAt`

### Input examples

Every store in Pune:

```json
{ "mode": "city", "locations": ["Pune"], "maxItems": 0 }
```

Every Lenskart and Titan Eye+ outlet in Delhi NCR and Mumbai:

```json
{ "mode": "brand", "brands": ["Lenskart", "Titan Eye+"], "locations": ["Delhi NCR", "Mumbai"], "maxItems": 0 }
```

Every Levi's outlet in India:

```json
{ "mode": "brand", "brands": ["Levi's"], "maxItems": 0 }
```

Jewellery and eyewear stores in Bengaluru:

```json
{ "mode": "city", "locations": ["Bengaluru"], "categories": ["jewellery", "eyewear"], "maxItems": 0 }
```

Every footwear store within 5 km of Koramangala:

```json
{ "mode": "nearby", "locations": ["Koramangala, Bengaluru"], "categories": ["footwear"], "maxDistanceKm": 5 }
```

The tenant list of two malls, with floors:

```json
{ "mode": "mall", "malls": ["Ambience Mall Gurgaon", "https://www.district.in/hub/phoenix-marketcity-kurla-central-mumbai"], "maxItems": 0 }
```

Every mall and market with District stores in Hyderabad (19 on 2026-09-24), and their tenants:

```json
{ "mode": "mall", "locations": ["Hyderabad"], "maxItems": 0 }
```

Specific stores:

```json
{ "mode": "storeUrls", "storeUrls": ["https://www.district.in/stores/nike-pacific-mall-tagore-garden-new-delhi-SDP-42186768"] }
```

#### Input reference

| Field | Meaning |
|---|---|
| `mode` | `city` (default), `brand`, `nearby`, `mall`, `search`, `storeUrls`, `allIndia` |
| `locations` | cities, areas or `"lat, lng"`: required for city / nearby / search. It limits brand mode to these cities and makes mall mode cover every mall with District stores in each place |
| `brands` | brand names: required for brand mode, a filter elsewhere |
| `malls` | mall names, district.in/hub/… links, or mall ids |
| `keywords` | search terms (search mode) |
| `storeUrls` | district.in/stores/… links |
| `categories` | only stores in these categories |
| `maxItems` | total stores (default 100, `0` = no limit) |
| `maxItemsPerQuery` | cap per city / brand / place / mall / term (`0` = no limit) |
| `coverWholeCity` | city mode: also read the live listing at every area of the city (default on) |
| `maxDistanceKm` | nearby mode: radius in km. It keeps only stores inside the radius and finds every outlet there |
| `includeDetails` | open each store's page for phone, address, hours, offers and photos (default on; can be turned off in nearby / mall / search for a faster listing) |
| `includeCatalogue` | add the brand's product catalogue (default off) |
| `maxConcurrency` | parallel requests (default 20) |
| `proxyConfiguration` | Apify Proxy (the default automatic group is fine) |

### Output sample

```json
{
  "storeId": "1026788",
  "name": "BERKOWITS",
  "brand": "BERKOWITS",
  "url": "https://www.district.in/stores/berkowits-satra-plaza-vashi-navi-mumbai-SDP-81667032",
  "category": "Wellness",
  "subCategories": ["Clinic"],
  "productTypes": ["Hair Care", "Hair Treatments", "Basic Facial", "Chemical Peel"],
  "address": "Shop No. 38, Ground Floor, Satra Plaza, Vashi, Navi Mumbai, Maharashtra 400703",
  "locality": "Satra Plaza, Vashi",
  "city": "Navi Mumbai",
  "state": "Maharashtra",
  "postalCode": "400703",
  "latitude": 19.082865,
  "longitude": 73.006264,
  "googleMapsUrl": "https://www.google.com/maps?q=19.082865,73.006264",
  "floor": "Ground floor",
  "mallId": "206812",
  "mallName": "Satra Plaza",
  "phone": "+919266738100",
  "openingHoursText": "Mon-Sun 10:00-21:00",
  "rating": 4.5,
  "ratingCount": 282,
  "headlineOffer": "40% OFF up to ₹2,000",
  "offers": [
    {
      "section": "District pay offers",
      "type": "instant",
      "title": "Instant offer",
      "description": "40% OFF up to ₹2,000",
      "promoCode": null,
      "minBillInr": null,
      "terms": ["Offer cannot be clubbed with in-store discounts", "…"]
    },
    {
      "section": "Add on offers",
      "title": "Get 20% off up to ₹5000",
      "description": "Valid on Kotak Bank Solitaire Credit Cards",
      "promoCode": "KOTAKSOLITAIRE",
      "terms": ["Valid twice per user in a calendar month on Platform", "…"]
    }
  ],
  "promoCodes": [{ "code": "DISTRICT700", "text": "Flat ₹700 OFF", "savingsInr": 700 }],
  "payDiscount": { "percent": 40, "amountInr": null, "maxDiscountInr": 2000, "validFrom": "2026-09-24 00:00:00", "validTo": "2026-09-24 23:59:00" },
  "districtPayMode": "Pay bill",
  "paymentMethods": ["Cash", "Credit Card", "Debit Card", "UPI", "District Pay"],
  "photos": ["https://b.zmtcdn.com/data/edition_assets/17823664028413.png"],
  "hasDetails": true,
  "source": { "type": "city", "input": "Mumbai" },
  "scrapedAt": "2026-09-24T09:54:13.461Z"
}
```

The dataset has three views: **Overview**, **Contacts & location** and **Offers**. Export to CSV, Excel or JSON, or read it through the Apify API.

### Use cases

- **Retail footprint tracking:** every outlet of a brand or a competitor, with coordinates and mall. Re-run monthly to see openings and closures.
- **Mall tenant mix:** who is in which mall, on which floor. Useful for retail real estate, leasing teams and mall operators.
- **Card and bank offer intelligence:** which banks fund which offers, at which brands, with what caps and minimum bills.
- **Lead lists for B2B sales:** POS, payments, signage, staffing and logistics vendors selling to branded outlets get a clean list with each store's phone number, address and brand.
- **Location analytics:** geocoded stores by category for catchment, white-space and site-selection studies.
- **Deal and coupon sites:** live promo codes and time-slot discounts per store.

### FAQ

**How complete is it?** City, brand and all-India modes read District's complete public store directory (about 23,500 stores), then add stores from District's live listing that are newer than the directory. Nearby mode returns what District's own app shows around a point: the nearest outlet of each brand. With a radius it returns every outlet inside it.

**Why are rating, floor, mall or promo codes empty on some rows?** District publishes those on its store listings, not on every store page. You get them for every store District shows in its live listing (most stores in malls). Store promo codes are campaigns that District switches on and off during the day, so the list is empty while none is live. Bank and card codes inside `offers` come from each store's own page. Phone, address, coordinates, hours and store-page offers come with every store.

**Why is the state different from the one District shows?** District's own state field often holds the brand's head-office state. The scraper corrects it from the PIN code and the address.

**Which cities are covered?** Every Indian city where District lists stores. The biggest are Bengaluru, Mumbai, Delhi NCR, Hyderabad, Pune, Kolkata and Chennai. Use the city name (`"Gurgaon"`, `"Navi Mumbai"`) or a metro (`"Delhi NCR"`). `"Mumbai"` includes stores District labels Navi Mumbai. List `"Thane"` separately.

**What about closed stores?** Stores whose page no longer exists are skipped and not charged.

**Is it legal to scrape District?** This actor collects only publicly available information that District shows to every visitor without logging in: store names, brands, business addresses, published store phone lines, opening hours, ratings and offers. It does not collect information about individual people. Scraping public data is generally lawful, but how you use the results is up to you: follow the laws that apply to you (for example India's DPDP Act, GDPR, PIPEDA or CCPA) and District's terms, and check with a lawyer if you are unsure. Apify's guide is a good starting point: [Is web scraping legal?](https://blog.apify.com/is-web-scraping-legal/)

**Does it access any private data?** No. It needs no login, account, cookie or API key. It reads only what District's public website serves to any visitor: store pages, the public store listing, mall pages and the public store sitemap. It never touches user accounts, orders, payments, bookings or anyone's personal profile, and ratings come as an average and a count only, with no reviewer names.

**Are phone numbers personal data?** No. They are the published business phone lines of branded stores, shown on each store's public page.

**How fast is it?** 50 stores with full details take about 10 seconds. 1,000 Mumbai stores took 62 seconds, and every store in Pune and Chandigarh (1,389) took 79 seconds. Listing-only runs (nearby / mall / search with details off) are faster still: 216 stores within 4 km of Koramangala took 9 seconds.

**Does it need a proxy?** It uses Apify Proxy's automatic group by default and retries blocked requests on fresh IPs. If a run is ever blocked, it finishes with what it has instead of failing.

**What happens when District is slow, blocked or changes something?** The run still ends successfully with whatever was collected and a status message that says why it stopped (`Partial — stopped at …`). Blocked or timed-out requests are retried on fresh IPs with backoff; a store whose page is broken is skipped; if District's live listing changes its version check, the actor re-reads the version from District's own web app and carries on; if the listing cannot be healed, the city and brand modes continue from the store directory. You are only charged for rows that were actually saved — a run that ends with 0 items costs nothing.

**Can I use `maxResults` or `limit` instead of `maxItems`?** Yes. `maxItems`, `maxResults`, `limit` and `maxStores` all mean the same thing, and `locations` also accepts `cities` or a newline-separated string.

# Actor input Schema

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

<b>Every store in a city</b>: all District stores in the cities you list (District's live listing plus its complete store directory).<br><b>Every outlet of a brand</b>: all outlets of the brands you list, across India or only in the cities you list.<br><b>Stores near a place</b>: the nearest outlet of each brand around a city, area or coordinates, nearest first (set a radius to get every outlet inside it).<br><b>Every store in a mall</b>: the tenant list of malls and markets, with floors.<br><b>Search</b>: District's own store search near each place.<br><b>Specific stores</b>: district.in/stores/… links.<br><b>All of India</b>: the whole store directory (about 23,500 stores).

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

Cities ("Mumbai", "Delhi NCR", "Gurgaon", "Bengaluru", "Pune"), areas ("Koramangala, Bengaluru") or coordinates ("19.0760, 72.8777"). Used by <i>city</i>, <i>nearby</i> and <i>search</i>; optional for <i>brand</i> (limits it to these cities) and <i>mall</i> (every mall and market with District stores in each place).

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

For <i>Every outlet of a brand</i>: brand names as District writes them, e.g. "Lenskart", "Levi's", "Tanishq", "Bata", "H\&M", "Decathlon". In the other modes this is a filter: only these brands are kept.

## `malls` (type: `array`):

For <i>Every store in a mall</i>: mall names ("Ambience Mall Gurgaon", "Phoenix Marketcity Kurla"), district.in/hub/… links, or District mall ids.

## `keywords` (type: `array`):

For <i>Search</i>: what you would type in District's store search, e.g. "sneakers", "eyewear", "Lenskart". District returns up to ~20 stores per term and place.

## `storeUrls` (type: `array`):

For <i>Specific stores</i>: district.in/stores/… links (the part after /stores/ also works).

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

Only stores in any of these categories. Works in every mode except <i>Specific stores</i>.

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

Stop after this many stores in total. 0 = no limit.

## `maxItemsPerQuery` (type: `integer`):

Cap for each city, brand, place, mall or search term, so one big query does not use up the whole run. 0 = no limit.

## `coverWholeCity` (type: `boolean`):

<i>City</i> mode: read District's live listing at every area of the city (Mumbai has 36, Delhi NCR 40) as well as the centre. It adds rating, mall, floor, promo codes and time-slot discounts for more stores, and finds stores newer than District's directory.

## `maxDistanceKm` (type: `integer`):

<i>Nearby</i> mode: only stores within this distance, and every outlet inside the circle (not just the nearest one per brand). Empty = the nearest outlet of each brand, up to 600 brands.

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

Open each store's page for phone, full address, postcode, opening hours, description, photos, payment methods and every live offer with its terms. Turn off in <i>nearby</i>, <i>mall</i> or <i>search</i> for a faster listing-only run. Always on in the other modes.

## `includeCatalogue` (type: `boolean`):

Add the store's catalogue: named collections with product names and images, where the brand publishes one (e.g. Nike's "New Arrivals").

## `maxConcurrency` (type: `integer`):

Parallel requests. 20 is fast and safe.

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

Apify Proxy's automatic (datacenter) group works and is the cheapest. The actor retries blocked requests on fresh IPs on its own.

## Actor input object example

```json
{
  "mode": "city",
  "locations": [
    "Gurgaon"
  ],
  "maxItems": 50,
  "maxItemsPerQuery": 0,
  "coverWholeCity": true,
  "includeDetails": true,
  "includeCatalogue": false,
  "maxConcurrency": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

All scraped stores.

## `run` (type: `string`):

Status and statistics for this run.

# 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": "city",
    "locations": [
        "Gurgaon"
    ],
    "maxItems": 50,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("yugenox/district-stores-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": "city",
    "locations": ["Gurgaon"],
    "maxItems": 50,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("yugenox/district-stores-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": "city",
  "locations": [
    "Gurgaon"
  ],
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call yugenox/district-stores-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,yugenox/district-stores-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/eN4HN72iAqpINnWXs/builds/SQMVNTc5LkIn7oDUF/openapi.json
