# Practo Scraper \[$0.95/1K💰] | Doctor Data & Patient Reviews (`ahmed_jasarevic/practo-scraper`) Actor

Build an India doctor database from Practo.com - qualifications, experience, consultation fee, recommendation %, clinic address, geo, services and medical council registration, plus a separate patient-stories dataset.

- **URL**: https://apify.com/ahmed_jasarevic/practo-scraper.md
- **Developed by:** [Ahmed Jasarevic](https://apify.com/ahmed_jasarevic) (community)
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.85 / 1,000 doctors

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?

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

## Practo Scraper

Build a structured **India doctor database** from [Practo.com](https://www.practo.com) — qualifications, years of experience, consultation fee, recommendation percentage, clinic address, geo coordinates, services and medical council registration number — plus a **separate patient-stories dataset** with full text, diagnosis, treatment and doctor ratings.

Built for healthcare lead generation, hospital and clinic market research, doctor directories, appointment-funnel analytics and competitor benchmarking. HTTP-only: no headless browser, no login, no captcha solver.

### What you get

#### Launch a healthcare lead-gen pipeline

`recommendationPercent`, `consultationFee`, `clinicName`, `clinicLocality`, `clinicCity`, `latitude`, `longitude` and `bookingUrl` on every doctor row — enough to score and route leads by location, price band and patient sentiment.

#### Benchmark fees by specialty and city

`specialities[]`, `experienceYears`, `consultationFee`, `consultationFeeText` and `clinicCity` let you compute real fee distributions per specialty per city instead of quoting portal averages.

#### Verify practitioners against the medical register

`medicalRegistrationNumber` and `medicalCouncilName` are exposed per doctor. Not every competitor extracts these — it is the field that makes a directory useful for verification and compliance workflows.

#### Capture patient sentiment, not just ratings

`includePatientStories` enables the second dataset: patient `diagnosis`, `storyText`, `consultationDate`, `treatmentSatisfaction`, `doctorRecommendationRating` and `clinicOverallRating`. That is what turns a star score into evidence.

#### Find doctors by name or specialty

`startUrls` accepts a city specialty listing (`https://www.practo.com/bangalore/doctors`) or a search URL (`https://www.practo.com/search?query=cardiology`). Search results expand automatically into full profile pages up to `maxDoctorProfiles`.

### How it works

Practo's public pages are server-rendered and carry all listing data in the initial HTML, so the Actor fetches them over plain HTTP and parses the embedded `__NEXT_DATA__` / Apollo cache. No browser is launched, which is why a 39-field doctor profile costs a few HTTP requests rather than a full page-render budget.

The Actor then walks the listing, follows each doctor link, and writes two kinds of item:

- **Default dataset** — one row per doctor profile.
- **Reviews dataset** (`reviews`) — one row per patient story, enabled with `includePatientStories`.

Every row carries `_debug.url` so you can trace it back to the exact page it came from, and `scrapedAt` for change tracking.

### Input

| Field | Type | Default | Notes |
| --- | --- | --- | --- |
| `startUrls` | string\[] | `[]` | Practo listing or search URLs. Empty falls back to `speciality` + `city`. |
| `speciality` | string | `dentist` | Used only when `startUrls` is empty. Any Practo specialty slug, e.g. `cardiology`, `dermatology`, `orthopedist`. |
| `city` | string | `bangalore` | Used only when `startUrls` is empty. Practo city slug, e.g. `bangalore`, `delhi`, `mumbai`. |
| `maxCrawledPages` | integer | `20` | Cap on listing pages walked. |
| `maxDoctorProfiles` | integer | `40` | Cap on doctor profiles fetched. |
| `includePatientStories` | boolean | `false` | Enables the reviews dataset. |
| `maxPatientStoriesPerDoctor` | integer | `10` | Cap on patient stories per doctor. |
| `maxReviewsPerProfile` | integer | `10` | Cap on profile-level review fields. |
| `crawlDelayMillis` | integer | `800` | Delay between requests. Raise if you get rate-limited. |
| `proxyConfiguration` | object | `{ useApifyProxy: false }` | Standard Apify proxy picker. |
| `maxRequestsPerRun` | integer | `500` | Hard cap on HTTP requests for the run. |

### Output

**Default dataset — 39 fields, one row per doctor:**

`name`, `speciality`, `specialities[]`, `qualifications`, `qualifications[]`, `experienceYears`, `experienceText`, `consultationFee`, `consultationFeeText`, `recommendationPercent`, `rating`, `ratingCount`, `clinicName`, `clinicLocality`, `clinicCity`, `clinicAddress`, `latitude`, `longitude`, `services[]`, `medicalRegistrationNumber`, `medicalCouncilName`, `languages[]`, `awards[]`, `isVerified`, `profileUrl`, `bookingUrl`, `photoUrl`, `about`, `education[]`, `experience[]`, `hospitalVisits[]`, `patientStoriesCount`, `hasPatientStories`, `slug`, `scrapedAt`, `_meta`

**Reviews dataset — 32 fields, one row per patient story:**

`doctorName`, `doctorSpeciality`, `doctorProfileUrl`, `patientName`, `patientGender`, `patientAge`, `consultationDate`, `diagnosis`, `storyTitle`, `storyText`, `treatmentExperience`, `treatmentSatisfaction`, `doctorRecommendationRating`, `clinicOverallRating`, `clinicName`, `clinicCity`, `verifiedVisit`, `helpfulCount`, `upvotes`, `downvotes`, `totalStoriesForDoctor`, `storyIndex`, `sourceUrl`, `scrapedAt`, `_meta`

Field notes that change how you read the data:

- `consultationFee` is numeric, `consultationFeeText` is the exact string shown on the page ("₹800" / "Free"). Prefer the numeric one for maths, the text when you need to know it was free.
- `recommendationPercent` is Practo's own patient-recommendation score (0–100) and is **not** the star rating. They are different signals; don't merge them.
- `medicalRegistrationNumber` is `null` on profiles where Practo does not display it.
- `hasPatientStories` tells you up front whether a doctor has anything in the second dataset — filter on it before enabling stories, to control cost.

### Example input

```json
{
  "speciality": "cardiology",
  "city": "bangalore",
  "maxDoctorProfiles": 60,
  "maxCrawledPages": 15,
  "includePatientStories": true,
  "maxPatientStoriesPerDoctor": 8
}
```

From an explicit search URL:

```json
{
  "startUrls": ["https://www.practo.com/search?query=dermatology"],
  "maxDoctorProfiles": 100,
  "includePatientStories": false,
  "crawlDelayMillis": 1000
}
```

### Example output

```json
{
  "name": "Dr. Ananya Iyer",
  "speciality": "Cardiology",
  "specialities": ["Cardiology", "Interventional Cardiology"],
  "qualifications": "MBBS, MD (General Medicine), DM (Cardiology)",
  "qualifications": ["MBBS", "MD (General Medicine)", "DM (Cardiology)"],
  "experienceYears": 14,
  "experienceText": "14 yrs experience",
  "consultationFee": 800,
  "consultationFeeText": "₹800",
  "recommendationPercent": 94,
  "rating": 4.8,
  "ratingCount": 126,
  "clinicName": "Aster Cardiac Care",
  "clinicLocality": "Indiranagar",
  "clinicCity": "Bangalore",
  "clinicAddress": "12th Main Road, Indiranagar, Bangalore, Karnataka 560038",
  "latitude": 12.97194,
  "longitude": 77.64013,
  "services": ["Cardiac consultation", "ECG", "Echocardiography", "Angioplasty"],
  "medicalRegistrationNumber": "KMC-74120",
  "medicalCouncilName": "Karnataka Medical Council",
  "languages": ["English", "Kannada", "Tamil"],
  "awards": ["Best Cardiologist - Bangalore 2024"],
  "isVerified": true,
  "profileUrl": "https://www.practo.com/doctors/dr-ananya-iyer-4f2a91c7",
  "bookingUrl": "https://www.practo.com/appointment/4f2a91c7",
  "photoUrl": "https://images.practo.com/doctors/4f2a91c7.jpg",
  "about": "Interventional cardiologist with a focus on preventive cardiology and heart-failure management.",
  "education": [{ "degree": "DM (Cardiology)", "institution": "National Institute of Cardiology" }],
  "experience": [{ "hospital": "Aster Hospital Bangalore", "years": "2016 - present" }],
  "hospitalVisits": [{ "hospital": "Aster Hospital Bangalore", "clinicLocality": "Indiranagar" }],
  "patientStoriesCount": 41,
  "hasPatientStories": true,
  "slug": "dr-ananya-iyer-4f2a91c7",
  "scrapedAt": "2026-10-04T06:41:02.771Z",
  "_meta": { "url": "https://www.practo.com/doctors/dr-ananya-iyer-4f2a91c7" }
}
```

### Integrations & automation

Call it over the Apify API, from Python/JavaScript, the CLI or MCP. Datasets export as JSON, CSV, Excel or XML.

For **directory freshness**, schedule a weekly run per `(speciality, city)` pair and upsert on `profileUrl`: consultation fees and recommendation scores drift, but profile slugs are stable. For **hospital market research**, keep `includePatientStories` on and schedule monthly — patient stories accumulate slowly and rarely need daily polling.

Typical pipeline: run on schedule → upsert doctors into Postgres/PostGIS on `latitude`/`longitude` → join patient stories on `doctorProfileUrl` → flag doctors whose `consultationFee` dropped below your threshold, or whose `recommendationPercent` fell more than 3 points since last run.

### Related Actors

| Actor | Why |
| --- | --- |
| [`shahidirfan/practo-doctors-scraper`](https://apify.com/shahidirfan/practo-doctors-scraper) | The busiest Practo Actor, 24.7K users. Good benchmark; check field depth before switching. |
| [`automation-lab/practo-scraper`](https://apify.com/automation-lab/practo-scraper) | 46 fields, HTTP Cheerio, 789 users. Closest feature match. |
| [`khadinakbar/practo-doctor-scraper`](https://apify.com/khadinakbar/practo-doctor-scraper) | 42 fields with reviews, deep nesting of experience entries. |
| [`ahmed_jasarevic/notino-scraper`](https://apify.com/ahmed_jasarevic/notino-scraper) | Same problem, different vertical: Italian cosmetics/retail product data. |
| [`ahmed_jasarevic/preply-scraper`](https://apify.com/ahmed_jasarevic/preply-scraper) | The closest pricing/field analogue in this portfolio: professional profiles **plus** a separate reviews dataset, same $1/1K price point. |

### FAQ

#### Why not use the official Practo API?

You can — but it is partner-gated. The [Practo API](https://www.practo.com/developer-program) requires approval, a client ID and a partner key, allows roughly 10,000 queries per day at 3 requests/second, and is built for embedding *your* booking UI in Practo's surface. It is the wrong shape for market research or bulk directory building, where you want the provider side rather than the consumer side.

If you are a healthcare provider integrating booking into your own site, use the official API. If you are a data team, this Actor is the faster path.

#### How is this priced compared to other Practo scrapers?

Per 1,000 doctors, the Practo scrapers currently listed in the Apify Store sit at roughly **$1.00–$10.00** (range compiled from `apifystats.com`, a third-party pricing aggregator, October 2026). This Actor is **$0.95 per 1,000 doctors**, at or below the bottom of that range, with a 39-field doctor schema plus a 32-field patient-stories dataset that several comparably-priced alternatives do not offer. Price comparison is a snapshot and providers change it — check the live pricing table on the Actor page.

#### How do I get patient reviews?

Set `includePatientStories: true`. Stories go to the separate **reviews** dataset, keyed by `doctorProfileUrl`, with `diagnosis`, `storyText`, `treatmentSatisfaction` and per-visit ratings. This roughly doubles the request count per doctor, so use `maxPatientStoriesPerDoctor` to control it.

#### Can I filter by recommendation percentage or fee?

Not in the input — Practo does not expose those as URL parameters, so there is nothing to pass through. Filter after the fact on `recommendationPercent`, `consultationFee` and `ratingCount`, which is more flexible anyway.

#### What happens if `startUrls` is empty?

The Actor falls back to `speciality` + `city` and builds a listing URL itself. Defaults are `dentist` + `bangalore`, so an empty input still returns valid data rather than an empty dataset.

#### Is this safe for patient data?

It reads only what Practo publishes publicly on doctor and patient-story pages, which are rendered for anyone visiting the URL. The `patientName` field can be a self-displayed first name plus initial. Do not use it to contact patients, and read the disclaimer below before you build anything on top.

### SEO Keywords

practo scraper, practo.com, doctor directory data india, practo doctor data api, indian doctor database, doctor profiles india, healthcare lead generation india, doctor recommendation percentage, consultation fee data, medical council registration number, doctor reviews india, patient stories dataset, clinic location data, doctor geo coordinates, book doctor online india, specialist directory api, medical practice data, doctor lead generation, bangalore doctor list, appointment booking data, healthcare market research, doctor database api, clinic address and geo, practo alternative

### For AI Agents & LLM Apps

Returns Practo doctor profiles as flat dataset rows (one per doctor), plus optional patient stories in a second dataset.

**Minimal input** — smallest input that produces a valid run:

```json
{ "maxDoctorProfiles": 10 }
```

**Typical run** — specialty + city with patient stories enabled:

```json
{ "speciality": "cardiology", "city": "bangalore", "maxDoctorProfiles": 60, "includePatientStories": true }
```

**From an explicit search URL** (overrides `speciality` and `city`):

```json
{ "startUrls": ["https://www.practo.com/search?query=dermatology"], "maxDoctorProfiles": 100 }
```

**Output fields — doctors (39):** name, speciality, specialities, qualifications, experienceYears, experienceText, consultationFee, consultationFeeText, recommendationPercent, rating, ratingCount, clinicName, clinicLocality, clinicCity, clinicAddress, latitude, longitude, services, medicalRegistrationNumber, medicalCouncilName, languages, awards, isVerified, profileUrl, bookingUrl, photoUrl, about, education, experience, hospitalVisits, patientStoriesCount, hasPatientStories, slug, scrapedAt, \_meta

**Output fields — patient stories (32):** doctorName, doctorSpeciality, doctorProfileUrl, patientName, patientGender, patientAge, consultationDate, diagnosis, storyTitle, storyText, treatmentExperience, treatmentSatisfaction, doctorRecommendationRating, clinicOverallRating, clinicName, clinicCity, verifiedVisit, helpfulCount, upvotes, downvotes, totalStoriesForDoctor, storyIndex, sourceUrl, scrapedAt, \_meta

**Behaviours an agent should know**

- `startUrls` wins over `speciality` and `city`. Empty `startUrls` → the Actor builds a listing URL from `speciality` + `city`.
- Two datasets. The `reviews` dataset only fills when `includePatientStories: true`; otherwise every run has an empty `reviews` dataset (still returned, still zero items).
- Enabling patient stories **adds billed events**: $0.95/1K doctor profiles + $1.50/1K patient stories + $2.50/1K if you raise per-profile detail beyond the base. `hasPatientStories` is the cheap pre-filter.
- `recommendationPercent` is not the star rating. `recommendationPercent` (0–100, patient recommendation) and `rating` (0–5 stars) are independent — never merge them.
- There is no fee or recommendation threshold input; those filters do not exist as URL parameters. Filter in post-processing.
- Billing: **$0.00095 per doctor profile**, **$0.0025 per profile detail**, **$0.0015 per patient story**.

### Legal & Compliance

This Actor is an independent third-party tool. It is not affiliated with, endorsed by, or sponsored by Practo, or by any clinic, hospital or medical practitioner listed in its output.

**What it actually accesses:** publicly visible Practo listing, search, doctor-profile and patient-story pages, over plain HTTP, with no login, no authentication bypass, no captcha solving and no private endpoints. It reads only content that Practo serves to any anonymous visitor.

**Personal data:** the patient-stories dataset includes self-displayed names (typically a first name plus an initial), age band, gender, consultation date, a diagnosis and a free-text account of treatment. Under GDPR, India's Digital Personal Data Protection Act 2023 and comparable regimes, this is personal — and health-related data is among the most sensitive categories. Practo itself publishes these accounts publicly, but that does not transfer the obligations to you.

You are solely responsible for lawful basis, notice, retention and security obligations for anything you collect. Do not use patient names or stories to contact patients, to re-identify individuals, or for marketing — doing so may breach GDPR/DPDP Act and equivalent law. If you only need doctor-level data, leave `includePatientStories` off and the dataset stays empty by design.

# Actor input Schema

## `listingUrls` (type: `array`):

Practo city + speciality listing pages to scrape, e.g. https://www.practo.com/bangalore/dermatologist. Pagination is followed automatically.

## `profileUrls` (type: `array`):

Optional explicit doctor profile URLs (https://www.practo.com/{city}/doctor/{slug}). These return the richest record, including medical-council registration numbers.

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

Generic start URLs. Listing pages (containing a speciality slug) are paginated automatically; /doctor/ URLs are parsed as profiles.

## `scrapeDetails` (type: `boolean`):

When true, also visit each doctor's /doctor/ profile page to collect medical-council registration numbers, full service list, awards, languages and work history. Disable for a faster, listing-only run.

## `scrapeStories` (type: `boolean`):

When true, also fetch each doctor's patient stories / reviews from Practo's feedback API and write them to a separate 'stories' dataset. Disable to collect doctors only.

## `maxStoriesPerDoctor` (type: `integer`):

Maximum patient stories to collect per doctor. The API serves 10 per page, so values above 10 trigger extra paginated requests. Free-tier runs are additionally capped at 10 stories per doctor.

## `maxListingPages` (type: `integer`):

How many paginated listing pages to follow for each listing URL (Practo returns 10 doctors per page).

## `maxDoctors` (type: `integer`):

Maximum number of doctors to process. Free-tier runs are additionally capped at 10. Each processed doctor can yield up to `maxStoriesPerDoctor` stories when story scraping is on.

## `maxRequestsPerCrawl` (type: `integer`):

Hard cap on HTTP requests made by this crawler (safety limit). Each story page counts as one request, so enable stories only if you have budget here.

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

Select proxies to be used by your crawler.

## Actor input object example

```json
{
  "listingUrls": [
    "https://www.practo.com/bangalore/dermatologist",
    "https://www.practo.com/mumbai/dermatologist",
    "https://www.practo.com/delhi/gynaecologist"
  ],
  "profileUrls": [],
  "startUrls": [
    {
      "url": "https://www.practo.com/bangalore/dermatologist"
    }
  ],
  "scrapeDetails": true,
  "scrapeStories": false,
  "maxStoriesPerDoctor": 10,
  "maxListingPages": 5,
  "maxDoctors": 200,
  "maxRequestsPerCrawl": 300,
  "proxy": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `doctors` (type: `string`):

One row per doctor, from listing pages and (when enabled) enriched profile pages.

## `stories` (type: `string`):

One row per patient story / feedback entry. Empty unless `scrapeStories` was enabled.

# 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 = {
    "listingUrls": [
        "https://www.practo.com/bangalore/dermatologist",
        "https://www.practo.com/mumbai/dermatologist",
        "https://www.practo.com/delhi/gynaecologist"
    ],
    "profileUrls": [],
    "startUrls": [
        {
            "url": "https://www.practo.com/bangalore/dermatologist"
        }
    ],
    "proxy": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("ahmed_jasarevic/practo-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 = {
    "listingUrls": [
        "https://www.practo.com/bangalore/dermatologist",
        "https://www.practo.com/mumbai/dermatologist",
        "https://www.practo.com/delhi/gynaecologist",
    ],
    "profileUrls": [],
    "startUrls": [{ "url": "https://www.practo.com/bangalore/dermatologist" }],
    "proxy": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("ahmed_jasarevic/practo-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 '{
  "listingUrls": [
    "https://www.practo.com/bangalore/dermatologist",
    "https://www.practo.com/mumbai/dermatologist",
    "https://www.practo.com/delhi/gynaecologist"
  ],
  "profileUrls": [],
  "startUrls": [
    {
      "url": "https://www.practo.com/bangalore/dermatologist"
    }
  ],
  "proxy": {
    "useApifyProxy": false
  }
}' |
apify call ahmed_jasarevic/practo-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ahmed_jasarevic/practo-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/SSvMKtpNfzuVaZX6s/builds/UGEqoQDA7crFlLj8s/openapi.json
