JustDial Scraper [From $2💰] | India Business Leads avatar

JustDial Scraper [From $2💰] | India Business Leads

Pricing

from $1.65 / 1,000 results

Go to Apify Store
JustDial Scraper [From $2💰] | India Business Leads

JustDial Scraper [From $2💰] | India Business Leads

Extract Indian business leads from JustDial: name, category, phone, address, locality, pincode, rating, review count, verified badge, coordinates and profile URL. Uses JustDial's own JSON APIs - no browser needed. Built for lead generation, local SEO and India market research.

Pricing

from $1.65 / 1,000 results

Rating

0.0

(0)

Developer

Ahmed Jasarevic

Ahmed Jasarevic

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

2

Monthly active users

8 days ago

Last modified

Categories

Share

JustDial Scraper — Scrape Indian Business Listings And Phone Numbers

The JustDial Scraper extracts Indian business leads from JustDial (justdial.com) — India's largest local business directory — as lead-ready JSON with 19 fields per business: phone, address, locality, pincode, rating, review count, verified badge, coordinates and canonical profile URL. It reads JustDial's own internal JSON listing APIs over plain HTTP (Crawlee's CheerioCrawler) — no browser, no DOM scraping, no JavaScript rendering.

Main use cases

  • B2B lead generation India — call-ready rows (name, phone, address, pincode), deduped across runs with docId.
  • India company contact database — rebuild a category × city list on a schedule instead of buying a static one.
  • Local business leads India — plumbers, dentists or any JustDial category, one city or twenty.
  • Local SEO India data — NAP-consistent name/address/phone rows plus profileUrl for citations.
  • India market research data — category density, rating, reviewCount, verified per market.
  • SMB prospecting data India — rank prospects by rating, reviews and verified badge.

Build Indian Business Leads Without Official API Access

JustDial serves listings through its own internal JSON APIs — the endpoints the justdial.com frontend calls when you scroll. The Actor walks four endpoint/wap combos in order with a mobile User-Agent, pins the first surface that answers for the rest of the search, sanity-checks every row and writes one dataset item per business. Plain HTTP + Cheerio only: 256 MB RAM, ~30 businesses per request, no per-business requests.

Input Parameters

FieldTypeRequiredDefaultNotes
searchUrlsstring[]No–JustDial category/search URLs — most reliable mode; category id goes straight to the internal API, e.g. https://www.justdial.com/Pune/Stationery-Shops/nct-10453443
queriesstring[]No–Free-text searches used with city, e.g. ["plumbers","dentists"]
citystringNoPuneIndian city for queries (Pune, Mumbai, Bangalore, Delhi, Hyderabad, …)
areastringNo""Optional locality filter, e.g. Koregaon Park; empty = whole city
nationalCatIdstringNo""Optional JustDial national category id (nct-…) for queries
maxItemsPerSearchintegerNo50Businesses per search (1–500). The only limit you need — page count derives at 30 rows/page; the Actor stops on its own
proxyobjectNo{"useApifyProxy": true, "apifyProxyCountry": "IN", "apifyProxyGroups": ["RESIDENTIAL"]}Indian residential is the reliable setting; swap in proxyUrls if the pool is flagged

Nothing is required, but rows appear only with a searchUrls entry or queries string. Everything else is internal and tuned: 25 parallel requests, 400 per run, 6 fresh proxy IPs per page, ≤25 pages per search. Empty run? Almost always the proxy pool — If every IP is refused.

Output Fields

One item per business in the run's default dataset — 19 fields:

FieldTypeDescription
namestringBusiness name as listed
categorystringJustDial category; arrives as type on write-wap* surfaces, normalised here
phonestringContact number incl. +91; absent if none on file
addressstringFull street address with locality, city, pincode as rendered
localitystringNeighbourhood/area without city and pincode
citystringUpstream casing varies (Pune/pune) — compare case-insensitively
pincodestringPostal code as string (leading zeroes survive)
ratingnumberAverage rating 0–5; null when unrated
reviewCountnumberTotal reviews behind rating; null when none
verifiedbooleanJustDial verified badge; unknown/unverified is false
latitude, longitudenumberWGS84 pin coordinates; null when not geocoded
thumbnailstringCatalogue image URL on JustDial's image CDN; null if no photo
profileUrlstringCanonical justdial.com profile URL from city + docId; null only without docId
docIdstringJustDial's stable business id — dedupe key across runs
searchTerm, searchUrlstringThe query/category and the search URL that produced the row
apiSurfacestringEndpoint that answered: read-wap1, read-wap2, write-wap1, write-wap2
scrapedAtstringUTC ISO 8601 collection timestamp

hours, website and review text are not available — no non-JS endpoint serves them. See Why there are no profile pages.

Views. The dataset schema ships with the Actor, so the Output tab renders a table instead of raw JSON:

ViewShowsUse it for
Overview (default)photo, name, category, rating, reviews, verified, city, locality, profile linkskimming results
Contactname, phone, address, locality, city, pincode, profile linklead generation
Map dataname, city, locality, pincode, latitude, longitudemapping and territory analysis

Views change Console display only — JSON, CSV and Excel exports always contain all 19 fields. Each field carries title/description/example in the schema, which is what the API returns in the run's output property and what AI agents read when chaining this Actor.

Example Input And Output

Category URLs (most reliable mode)

{
"searchUrls": [
"https://www.justdial.com/Pune/Stationery-Shops/nct-10453443",
"https://www.justdial.com/Mumbai/General-Physician-Doctors/nct-10892680"
],
"maxItemsPerSearch": 50,
"proxy": { "useApifyProxy": true, "apifyProxyCountry": "IN", "apifyProxyGroups": ["RESIDENTIAL"] }
}

Free-text queries

{ "queries": ["dentists", "plumbers"], "city": "Pune", "maxItemsPerSearch": 50 }

One dataset item

{
"docId": "022PXX22.XX22.191204000717.B8L5",
"name": "Dr Salaah Qureshi",
"category": "General Physician Doctors",
"phone": "07947159457",
"address": "Bon Bon Lane, Juhu Versova Link Road, Andheri West, Mumbai",
"locality": "Juhu",
"city": "Mumbai",
"pincode": "400049",
"rating": 4.5,
"reviewCount": 210,
"verified": true,
"profileUrl": "https://www.justdial.com/Mumbai/Dr-Salaah-Qureshi/022PXX22-XX22-191204000717-B8L5",
"searchTerm": "General Physician Doctors",
"apiSurface": "read-wap1",
"scrapedAt": "2026-10-02T10:30:00.000Z"
}

Scrape JustDial Business Listings And Phone Numbers Without A Browser

The four-surface fallback chain

Four endpoint/wap combos are tried in order; on a blocked, non-JSON or empty response the Actor falls through, and the first surface that answers is pinned for the rest of that search.

#idEndpointResponse shape
1read-wap1/api/india_api_read/26june2015/searchziva.php?wap=1results[] — objects with named keys (docId, name, VNumber, address, NewAddress, city, compRating, totalReviews, verified, …)
2read-wap2same endpoint, wap=2same as #1
3write-wap1/api/india_api_write/20march2020/searchziva.php?wap=1results: { columns[], data[][] } — columnar
4write-wap2same endpoint, wap=2same as #3

All four use the mobile User-Agent; surface 1 answers first in practice, 2–4 are fallbacks, 3/4 differ in payload shape.

The edge keys off User-Agent, not the wap flag

A live probe of all eight combinations (read/write × wap=1|2 × mobile/desktop) proved it:

ClientResult
mobile UA200 JSON on read and write; wap=1 and wap=2 byte-identical
desktop UAthe 14-byte <HTML>\n</HTML> Akamai stub — 4 out of 4

Desktop variants were guaranteed failures — and expensive: each dead surface burns a full maxIpAttempts round of fresh IPs, so three of them meant 18 of every 30 requests went to endpoints that could never answer. With only live surfaces, the same IP budget buys roughly 2× real attempts. /api/resultsPageListing is dead too (its sw8 + search_id/trkid/jdlt signature is not reproduced): POST → stub, GET → {"error":1,"message":"Invalid request"}, searchListJson → 404.

Columnar parsing, decoy arrays and the sanity check

Surfaces 3/4 return the same fields columnar (results.columns / results.data), zipped into named rows — type, not category. Decoy arrays (AlsoListedIn, nearmeDetails, keywordResults) are business-shaped enough to pass for results, so the Actor reads results first. A row is dropped unless it has a real docId, a name that is not a copy of the locality, and a rating in 0–5; a whole page failing that check moves the Actor to the next surface instead of reporting "0 matches".

Why there are no profile pages (no hours, no website)

Profile-fetching was removed — it cannot work and was harmful:

  • The page is a client-rendered React shell (HTTP 200, window.__INITIAL_STATE__ is {}, no ld+json, ~218 KB app bundle, business name absent).
  • It redirects to the homepage, overwriting the correct profileUrl.
  • detailsFetched: true was a lie (set even on empty shells).
  • Cost ~1 request per business (26 of 29 requests in the diagnostic run) and returned nothing.
  • The listing API has 75 fields, none of them hours or website.

Hours/website/review text need JavaScript — out of scope; the Actor stays Cheerio-only (256 MB, no Chrome).

Fingerprint handling: IP reputation, not the fingerprint

got-scraping's default desktop Chrome 147 spoof (TLS profile + generated headers) contradicts a mobile UA + wap=1, so it is disabled in preNavigationHooks — plain HTTP/1.1, Node's default TLS, mobile headers only. IP reputation is what gets refused, not the fingerprint: a plain residential fetch returned 30 listings/page (totalNumberofResults: 149) five times out of five until a request burst flipped it to the stub; a browser-grade TLS/HTTP2 fingerprint (curl-cffi-node) changed nothing and was dropped rather than carried as a 35 MB native dependency. The probe ran from a non-Indian IP (BA, Sarajevo) and got full Pune data — not geo-fenced. Only IP rotation works; maxIpAttempts sets how hard it tries.

JustDial Scraper Limits, Anti-Bot Defence And Troubleshooting

JustDial sits behind Akamai (AkamaiGHost / Bot Manager), serving a stub to datacenter and to flagged residential IPs. Measured decision order:

  1. Desktop User-Agent → stub immediately — 4 of 4 requests, even on a perfect IP.
  2. Flagged IP reputation → stub, regardless of country (an unflagged non-Indian residential IP got full data — not geo-fenced).
  3. /api/resultsPageListing → stub on GET and POST (signature not reproduced).

The Actor detects blocks (403/429/451, Access Denied, errors.edgesuite.net, or the ~14-byte stub) and logs them; runs every request on a fresh proxy IP, retrying up to maxIpAttempts times; and falls through to the next surface.

Blocked runs: set proxy to apifyProxyCountry: "IN" + apifyProxyGroups: ["RESIDENTIAL"] — the most effective setting, because Indian traffic reads as normal local traffic.

The block is IP-level across the whole domain and a flagged IP stays flagged, so a sticky session would lock the run onto it. The Actor retires its Crawlee session after every request so Apify hands out a different IP next time (the pattern working scrapers here use: fresh session per page, up to 6 IPs). No cookie warm-up either — Akamai only issues a usable _abck/bm_sz after JavaScript runs, and replaying a cookie from a previous IP is an IP/cookie mismatch, a stronger bot signal than none.

Platform measurement: datacenter fails, RESIDENTIAL is the setting

From Apify's datacenter IPs every surface returns HTTP 403 and 0 items; apifyProxyCountry: "IN" helps but those are still datacenter IPs, blocked intermittently. "apifyProxyGroups": ["RESIDENTIAL"] is the reliable setting while the pool serves unflagged IPs, which varies. 6 fresh IPs refused everywhere means the pool is flagged now — use proxy.proxyUrls or re-run later. robots.txt disallows /api/, /api/india_api_read, /_next/ — hit deliberately as the site's own data endpoints.

Pricing and run limits

  • CheerioCrawler only — 256 MB default memory (512 MB max), no browser → low compute cost.
  • result $0.0018/business (FREE), $0.00165 at GOLD+, plus Actor Start $0.00005/run.
  • 400 requests/run cap + maxItemsPerSearch = no runaway; concurrency fixed at 25.
  • ~30 businesses/request → run cost ≈ searches × pages; no per-business requests.
  • Free tier: 10-business preview; paying users: unlimited.
  • Residential proxy traffic counts against the account's allowance (a response is tens of KB).

Troubleshooting

SymptomCauseFix
🔁 … retrying on a fresh IP (2/6)Normal — previous IP refusedNothing; up to 6 IPs/page
… 6 fresh IPs refusedPool handed out blocked IPsPool flagged: use proxy.proxyUrls or re-run later
🚫 blocked (HTTP …)Akamai refused your IPproxy with apifyProxyCountry: "IN" + RESIDENTIAL, re-run
🚫 soft-blocked (HTTP 200, empty)200 with an empty envelope — same block, quieterSame fix; commonest failure, looks like "0 matches"
no usable rows (mis-mapped)Rows failed the sanity checkHarmless; next surface. Report if persistent everywhere
Empty dataset, ~0 matchesQuery too narrow vs category URLUse a category searchUrls URL, not free-text queries
hours / website missingNo non-JS endpoint serves themNot fixable — see Why there are no profile pages
Free user sees 10 rowsPreview capExpected — upgrade

If every IP is refused

The edge refuses many ranges and the share varies. Measured Oct 2026: one run had 50 consecutive refusals on 50 different IPs while two egress probes through the same sessions returned HTTP 200 — the proxy worked, JustDial rejected the exit IPs; minutes later, same settings, other runs got clean Indian egress and 10–26 real listings with zero blocks. The pool is unreliable, not dead: maxIpAttempts helps when IPs are flagged, but raising it after repeated refusals only costs time. Part of the refusals was self-inflicted — older builds cycled desktop-UA surfaces that can only return the stub, which is why only JSON-verified surfaces remain.

Each run probes egress via ipwho.is (🌐 Egress IP for this request: … (IN)). apifyProxyCountry: "IN" is a preference, not a guarantee: the pool may serve another country or return 407 … no usable proxies from country 'IN'. Then use your own proxy:

{ "proxy": { "useApifyProxy": false, "proxyUrls": ["http://user:password@gate.provider.com:8000"] } }

proxyUrls overrides apifyProxyGroups/apifyProxyCountry (dropped when set, ProxyConfiguration rejects the combination); the first egress-IP log confirms it works.

Integrations & Automation

  • Apify Console — run manually, or save the input as an Actor task and schedule it daily/weekly to refresh one category per city.
  • Apify API — POST https://api.apify.com/v2/acts/cyha8usQ64pSophWE/runs?token=YOUR_TOKEN with the input JSON as body; poll GET https://api.apify.com/v2/actor-runs/{runId}; read GET https://api.apify.com/v2/datasets/{datasetId}/items?format=json.
  • Webhooks — fire on actor.run.succeeded to push rows into your CRM, Sheet or queue the moment a run ends.
  • Zapier / Make — the Apify integration exposes runs and dataset items as triggers/actions, so each run appends leads automatically.
  • Recurring schedule — weekly monitoring of one category × one city keeps a lead pool fresh.
ActorNotes
lead-extractor---justdialJustDial leads — 737 users, 5.0★, $0.0028/record
justdial-business-scraper459 users, $0.002/result; claims decoded phone, timing, website, photos, "30M+ listings across 1000+ cities"
justdial-business-scraper214 users, 5.0★, $0.03/business; claims WhatsApp, email, GPS, hours, year, turnover
indiamart-supplier-scraperIndiaMART B2B suppliers — 542 users, 4.55★, $0.0017/result
sulekha-scraperSulekha local-service listings, same India lead-gen cluster

FAQ

Is there an official JustDial API?

No public official API exists. RapidAPI wrappers and scraper-API vendors fill the gap; the data actually comes from JustDial's own internal JSON endpoints (searchziva.php and friends), which this Actor calls over plain HTTP.

What's the best way to get Indian business leads with phone numbers?

Scrape a live directory (JustDial for hyperlocal, phone-first listings), scrape a B2B marketplace (IndiaMART for suppliers), or buy a static database. Live scraping stays current and dedupable — this Actor returns phone, address, pincode, rating, reviews and coordinates per business with docId as merge key, browserless.

JustDial vs IndiaMART vs Sulekha — which is better for lead generation?

B2B versus hyperlocal: IndiaMART is supplier/manufacturer discovery (its Store actors target "suppliers"); JustDial and Sulekha are local service and retail directories, JustDial the phone-first one. Local consumer leads → JustDial; factory/trade supply → IndiaMART.

Why is JustDial blocking my scraper?

Akamai Bot Manager decides in order: desktop UA → 14-byte stub immediately (4 of 4); flagged IP reputation → stub regardless of country; datacenter IPs → HTTP 403. Fix: Indian residential proxies (apifyProxyCountry: "IN" + apifyProxyGroups: ["RESIDENTIAL"]), one fresh IP per request (up to 6/page) and no cookie replay across IPs.

How do I extract JustDial phone numbers without a browser?

Call the internal listing JSON APIs with plain HTTP + Cheerio: read-wap* returns objects with named keys (VNumber → phone), write-wap* the same fields columnar. A mobile User-Agent is mandatory — desktop gets the stub on every surface.

JustDial's terms prohibit data mining, scraping and crawling, and robots.txt disallows the API paths used here. The output is public business contact data, but running it may breach the ToS and could attract liability under India's IT Act, 2000. Compliance is the user's responsibility.

What are the alternatives to this JustDial scraper?

thirdwatch/justdial-business-scraper (459 users, $0.002/result), memo23/justdial-business-scraper (214 users, 5.0★, $0.03/business, claims WhatsApp/email/GPS/hours), happitap/lead-extractor---justdial (737 users, $0.0028/record), tugelbay/justdial-leads-extractor ($0.00288/business), iskander/just-dial-extractor-scraper ($0.005/business); adjacent: IndiaMART, Sulekha, AskLaila and Yellow Pages India actors.

SEO Keywords

justdial scraper, scrape justdial, justdial api, justdial business listings, justdial phone number extractor, justdial leads extractor, justdial data extraction, indian business leads, india business phone number database, b2b lead generation india, local business leads india, india company contact database, local business directory data india, local seo india data, india market research data, smb prospecting data india, justdial alternative api, justdial alternatives india, indiamart vs justdial, sulekha vs justdial, apify justdial, justdial scraper mumbai, justdial scraper delhi, justdial scraper bangalore, business leads delhi ncr

For AI Agents & LLM Apps

Purpose: extract structured Indian business listings (name, phone, address, rating, coordinates) from JustDial's internal JSON listing APIs without a browser.

Minimal working input:

{ "searchUrls": ["https://www.justdial.com/Pune/Stationery-Shops/nct-10453443"], "maxItemsPerSearch": 50 }

Variant — free-text mode: { "queries": ["dentists"], "city": "Pune", "maxItemsPerSearch": 50 }

Output fields: name, category, phone, address, locality, city, pincode, rating, reviewCount, verified, latitude, longitude, thumbnail, profileUrl, docId, searchTerm, searchUrl, apiSurface, scrapedAt.

Behaviors an agent should know:

  • No required fields; without searchUrls/queries a run returns no rows. searchUrls is most reliable; queries uses city (default Pune) + optional area/nationalCatId.
  • maxItemsPerSearch (default 50, 1–500) is the only limit to set; page count derives at 30 rows/page. Internals: 400 req/run cap, concurrency 25, ≤25 pages/search, 6 fresh IPs/page (maxIpAttempts).
  • Proxy defaults to Apify residential India; proxy.proxyUrls overrides and drops apifyProxyCountry/apifyProxyGroups (ProxyConfiguration rejects the combination).
  • docId is the dedupe key; profileUrl derives from city + docId; apiSurface names the endpoint; city casing varies — compare case-insensitively.
  • Rows never include hours, website or review text — they need JavaScript, which this Actor never runs.
  • Empty dataset ≈ flagged proxy pool (6 fresh IPs refused / soft-blocked (HTTP 200, empty results)), not a wrong query.

Billing: pay-per-event — result $0.0018/business (FREE; $0.00165 at GOLD+) plus Actor Start $0.00005/run.

  • Independent project, not affiliated with, endorsed by, sponsored by or associated with JustDial / JD Software.
  • It accesses only JustDial's publicly reachable internal listing JSON endpoints over plain HTTP — no login, no account, no CAPTCHA or credential bypass — though those paths (/api/, /api/india_api_read, /_next/) are disallowed by justdial.com robots.txt and JustDial's terms prohibit data mining, scraping and crawling.
  • Users are solely responsible for their own compliance with JustDial's Terms of Service and applicable data-protection law (India's Information Technology Act, 2000; GDPR/CCPA where they apply).
  • The output contains personal contact data (business phone numbers and addresses) and must not be used for unsolicited commercial outreach in violation of applicable law.