Hepsiemlak Scraper - Property Listings, Prices & Contacts
Pricing
from $1.70 / 1,000 listing records
Hepsiemlak Scraper - Property Listings, Prices & Contacts
Scrape hepsiemlak.com sale and rental listings by city or district, or paste listing links. Every row carries price, area, rooms, seller and location. Detail enrichment adds specs, description and contact.
Pricing
from $1.70 / 1,000 listing records
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
0
Bookmarked
5
Total users
4
Monthly active users
14 days ago
Last modified
Categories
Share
Hepsiemlak Scraper: Turkey Property Listings
Hepsiemlak Scraper turns hepsiemlak.com into a clean property data feed for Turkey. Get sale (satilik) and rental (kiralik) listings by city or district, or paste listing and catalogue links directly. Every row carries price, area, room plan, seller and location straight from the search page; turn on detail reads for the full spec table, description and contact. Export to JSON, CSV or Excel, or pull results straight into your app through the API.
Why This Scraper?
- Two ways to find listings. Search by city or district across the country, or paste hepsiemlak.com listing and catalogue links directly.
- Sale and rental, every property kind. Switch between satilik and kiralik, and narrow to apartments, villas or detached houses.
- Price and area filters. Bound a search by minimum and maximum price in lira, or minimum and maximum gross area, before a single page is read.
- Detail reads on demand. Turn on one surcharge per record to add the full spec table, listing number, description and seller contact.
- Turkish location names, handled. City and district names fold their Turkish letters automatically, so "Şişli" and "Sisli" reach the same search.
- Built for schedules. Recurring updates return only new and changed listings on repeat runs, and a refused run fails loudly instead of returning an empty dataset.
Use Cases
- Property investors: compare asking prices, gross area and room plans across Turkish cities and districts to spot value.
- Real estate agencies: watch competing offices' listings and price changes in the districts you work.
- Rental market research: track kiralik asking rents by district to follow how the rental market moves.
- Proptech and listing apps: feed clean listing records with price, area, location and seller into your own product.
- Market monitoring: schedule recurring runs to catch new listings, price edits and listings going off the market.
Data You Get
Sample shape: values are illustrative placeholders, not from a live record.
| Field | Example |
|---|---|
listingId | "1000001-1" |
url | hepsiemlak.com link to the listing |
title | "Sample 2+1 apartment near Sample Square" |
price / currency | 4250000 / "TRY" |
totalPrice | detail-page price line (when Fetch details is on) |
grossSqm / netSqm | 110 / 95 |
rooms / bedrooms | "2+1" / 2 |
propertyTypes | ["Apartment"] |
city / country | "Istanbul" / "TR" |
descriptionSnippet | one-line summary from the search card |
description | full listing description (when Fetch details is on) |
sellerName | "Sample Realty" |
sellerPhone | seller's published contact number (when Fetch details is on) |
sellerOfficeUrl | link to the seller's agency page |
imageUrl | listing photo |
listingNo | the site's own listing number (detail read) |
status / lastUpdated | listing status and last bump date (detail read) |
recordType | "listing" |
sourceType / sourceId | "search" / "istanbul" (which location or link found it) |
position | slot on the source page |
scrapedAt | "2026-09-16T00:00:00Z" |
changeType / changedFields | recurring-updates label and what changed (when Recurring updates is on) |
Detail reads also add the floor, building floors, property age, heating, title deed status, furnishing, usage, facade and swap fields to the spec table.
Each listing also carries the site's own search-state attributes when present: county and district, all photos, map coordinates, create, update and start dates, categories, room plan, seller type, owner and firm blocks with their public phones, video link, 360 photo count, price range and the listing flags. Extra fields appear on search rows; none of them add a charge.
How to Use
- Pick a mode:
search(walk listings by location) orurl(paste hepsiemlak.com links). - For search mode, choose Listing type (sale or rent), a property kind, and one or more locations, with optional price and area bounds.
- Turn on Fetch details for the full spec table, description and contact, and set Max items to control run size and cost.
- Click Start, then download the dataset as JSON, CSV or Excel, or read it through the API.
Search apartments for sale in a city:
{"mode": "search","listingType": "sale","propertyType": "apartment","locations": ["Istanbul"],"maxItems": 20}
Rentals in a district, with a price bound and detail reads:
{"mode": "search","listingType": "rent","locations": ["Izmir Karsiyaka"],"maxPrice": 30000,"fetchDetails": true,"maxItems": 50}
Paste listing or catalogue links:
{"mode": "url","urls": ["https://www.hepsiemlak.com/en/istanbul-satilik/daire/1000001-1","https://www.hepsiemlak.com/en/izmir-kiralik"],"fetchDetails": true}
A nationwide sweep bounded by area, no location filter:
{"mode": "search","listingType": "sale","minArea": 100,"maxArea": 200,"maxItems": 200}
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/hepsiemlak-com-scraper").call(run_input={"mode": "search", "locations": ["Istanbul"]})for listing in client.dataset(run["defaultDatasetId"]).iterate_items():print(listing["title"], listing["price"], listing["currency"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/hepsiemlak-com-scraper').call({ mode: 'search', locations: ['Istanbul'] });const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
Resume and recurring updates
- Resume (
resumeFromRunId) continues one interrupted run: paste its run or dataset ID and the actor skips listings already collected there, so you don't pay twice. - Recurring updates (
incrementalMode) is for scheduled runs over the same search or links. Each row is classifiedNEW,UPDATED(withchangedFields),UNCHANGED(suppressed and not billed unless Return unchanged rows is on),REAPPEAREDorEXPIRED(only after a run that scanned the whole scope, and only with Return gone rows).stateKeynames or shares the stored state.ignoreFieldsForChangeslists extra fields (top-level names or dotted paths such asagent.name) to leave out of change detection; they are still returned. Run-describing fields (scrapedAt,sourceType,sourceId,sourceUrl,position) are always excluded.EXPIREDis never produced after a capped run, a Max pages limit, or a pasted link that starts atpage=2or later. The two options cannot be combined on one run. With recurring updates off, output is exactly as before.
Modes and Supported Links
- search mode builds listing paths from Listing type (sale, rent, seasonal-rent), Property kind, Locations and the filters, and walks result pages until Max items.
- url mode reads each pasted link. Supported shapes: search pages in English or Turkish, city and district catalogues, rent and seasonal-rent catalogues, property-type catalogues, paginated and sorted pages, room-count and owner-listed pages, single listing pages (English or Turkish). Agency directory and developer-firm pages list offices rather than properties, so a pasted one is skipped with a warning naming the link. Filters, sort and property kind in the form are ignored for pasted links.
Filters and sorts (search mode): listing type, property kind, room plan, owner-listed only, price and gross-area bounds, and sort by price or area ascending or descending.
Dataset views
The dataset has three views: Overview (price, area, rooms, location, seller), Details (spec table, description and contact from detail reads) and Changes (change label and changed fields in recurring updates). A run summary is written to the key-value store under OUTPUT.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | search | search walks listing pages for each location; url reads each pasted link. |
listingType | string | sale | sale reads satilik paths, rent reads kiralik paths, seasonal-rent reads sezonluk-kiralik (holiday let) paths. Search mode only. |
propertyType | string | any | Narrow search walks to apartment, villa or detached-house. Pasted links ignore this. |
roomCount | string | (none) | Keep only listings with this room plan, for example 2+1. Search mode only. |
ownerOnly | boolean | false | Keep only listings posted by the owner, not by an agency. Search mode only. |
sortBy | string | default | price-asc, price-desc, area-asc or area-desc; default keeps the site order. Search mode only. |
locations | array | ["Istanbul"] | City or district names such as Istanbul, Kadıköy or Izmir Karsiyaka. Each name is resolved through the site's own location lookup (Turkish letters are fine). A name the site does not know stops the run with a message naming it. Districts use the form /en/kadikoy-satilik, not istanbul-kadikoy. Empty means the nationwide listing path. |
minPrice | integer | (none) | Keep only listings at or above this price in lira. |
maxPrice | integer | (none) | Keep only listings at or below this price in lira. |
minArea | integer | (none) | Keep only listings with at least this gross area (m2). |
maxArea | integer | (none) | Keep only listings with at most this gross area (m2). |
urls | array | one example of each supported link shape | hepsiemlak.com listing or catalogue links (url mode). Leaving the field untouched in url mode scrapes one example of each shape (search EN/TR, city, rent, seasonal rent, property type, paginated, sorted, room count, owner-listed, detail EN/TR), capped by Max items. Clear the field and paste your own links to scrape only those. |
fetchDetails | boolean | false | Read each listing page for the spec table, description and contact. Adds one detail-enrichment charge per record, at most. |
maxItems | integer | 20 | Stop after collecting this many records across all locations and links. 0 means unlimited. |
maxPages | integer | 0 | Safety bound on result pages walked per location or link. 0 means no page limit; the run stops at Max items instead. |
maxItemsPerSource | integer | (none) | Optional extra cap per location or pasted link, on top of Max items. 0 or empty means no per-source cap. |
resumeFromRunId | string | (none) | Continue one interrupted run by its run or dataset ID. Cannot be combined with recurring updates on one run. |
incrementalMode | boolean | false | Remember this setup's listings between runs and label every row NEW, UPDATED, UNCHANGED, REAPPEARED or EXPIRED. Cannot be combined with resume on one run. |
stateKey | string | (none) | Name this monitoring setup. Runs with the same key share one baseline; leave empty to derive it from the search setup. |
emitUnchanged | boolean | false | Also return (and bill) UNCHANGED rows. Off by default: unchanged rows are suppressed. |
emitExpired | boolean | false | Also return (and bill) EXPIRED rows for listings missing from a complete scan. Only on complete scans. |
ignoreFieldsForChanges | array | [] | Incremental mode only. Extra field names (top-level or dotted path) to exclude from UPDATED change detection; still returned. |
proxyConfiguration | object | Residential, Turkey | Connection settings. Leave the default unless a run reports that it could not read any results. |
mcpConnectors | array | (none) | Optional. Send a condensed summary of each scraped item to an app you authorized under Apify Integrations (Notion, Linear, Airtable, Apify). The full record stays in the dataset. |
notionParentPageUrl | string | (none) | Notion connector only: the page under which item pages are created. |
maxNotifyListings | integer | 50 | Cap on items written to each connector per run. Does not affect the dataset. |
Output Example
Sample shape: values are illustrative placeholders, not from a live record.
{"listingId": "1000001-1","url": "https://www.hepsiemlak.com/en/istanbul-satilik/daire/1000001-1","title": "Sample 2+1 apartment near Sample Square","price": 4250000,"currency": "TRY","grossSqm": 110,"netSqm": 95,"rooms": "2+1","bedrooms": 2,"propertyTypes": ["Apartment"],"city": "Istanbul","country": "TR","descriptionSnippet": "Bright 2+1 apartment close to the square, ready to move in.","sellerName": "Sample Realty","sellerOfficeUrl": "https://www.hepsiemlak.com/emlak-ofisi/sample-realty","imageUrl": "https://hecdn01.hemlak.com/sample/listing-photo.jpg","recordType": "listing","sourceType": "search","sourceId": "istanbul","position": 3,"scrapedAt": "2026-09-16T00:00:00Z"}
Send results into your apps (MCP connectors)
Optionally send a condensed summary of each scraped item to an app you authorized under Apify Integrations (Notion, Linear, Airtable, Apify) with mcpConnectors. For Notion, notionParentPageUrl names the page under which item pages are created, and maxNotifyListings caps how many items are written to each connector per run. The full record always stays in the dataset.
Plan Requirement
hepsiemlak.com sits behind an anti-bot challenge: plain and datacenter connections get an HTTP 403 "Just a moment..." page, so this actor needs a paid Apify plan with residential proxy access. The default (residential exits in Turkey) is the lane to use, and the actor takes a fresh exit when one is refused, then falls back to a page-rendering backup connection only when needed. Individual exits can still be refused, so a run may retry before pages load. A rate of failed runs is normal on this site; schedule with retries.
What you will see in the log:
- On a plan without residential access: "The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable." If the backup serves pages you also see "Served over the backup connection (the default connection was unavailable)."
- If the proxy is refused for the account, the run fails with "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required."
- If residential is unavailable and the backup cannot load pages: "No results: residential proxies are unavailable on this run ... and the backup connection could not load the requested hepsiemlak pages."
- If nothing loaded for another reason: "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search. Please try again later."
A refused run always fails loudly, never as an empty dataset.
FAQ
How much does it cost?
You pay for the actor start, one listing-record charge per row returned, and one detail-enrichment charge per enriched row when Fetch details is on. Rows that are suppressed (unchanged), dropped or failed are not charged. A pasted listing link is read from its own listing page, so it always carries that detail-enrichment charge, even with Fetch details off. The Pricing tab shows the current rates. Use Max items to cap the cost of any run.
Is it legal to scrape hepsiemlak.com?
This actor collects only publicly available listing data. You are responsible for how you use it: follow hepsiemlak.com's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Seller names and phone numbers shown on a listing are published by the lister as the contact channel; they are normal marketplace fields, not discovered personal data.
Can I get only new or changed listings on a schedule?
Yes. Schedule the actor from the Schedules tab and turn on Recurring updates. Each run then returns only new and updated listings, and unchanged ones are not billed.
Why did my run fail instead of returning an empty dataset?
If the site refuses every request, the run stops with a clear message so "no listings for this search" is never confused with "nothing could be read". Run it again in a few minutes. If only some locations or links could be read, you still get what was found.
Can I use Resume and Recurring updates together?
No. Pick one per run: Resume from run ID continues a single interrupted crawl, while Recurring updates tracks a setup across scheduled runs. Clear one field to use the other.
🔗 More scrapers you might like
Pair this actor with these related scrapers from the same team:
| 🏠 PropertyGuru SG Scraper Scrape PropertyGuru.com.sg sale and rental listings with 30+ structured fields, including... | 🏠 Cian RU Collect property listings from Cian.ru by search filters or direct URLs. Returns... |
| 🏠 ImmobilienScout24.de Scraper Extract property listings from immobilienscout24.de, Germany's #1 real estate platform... | 🧩 Suumo.jp Scraper Scrape Suumo, Japan's largest property portal: rental rooms with rent, management fees... |
| 🏠 Otodom.pl Scraper Scrape Otodom.pl properties for sale and rent, including apartments, houses, plots... | 🏠 RealestateCo NZ Search and extract all real estate properties for sale, rent, and sold across New Zealand... |
💬 Support
- 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
- 🛠️ Need another site, extra fields or a private build? Email contact@abotapi.com.
- ⭐ Enjoying it? A quick review on the actor page helps other users find it.