Funda in Business Scraper - Commercial Property & Agents
Pricing
from $1.00 / 1,000 property scrapeds
Funda in Business Scraper - Commercial Property & Agents
Scrape Funda in Business commercial listings: offices, retail, industrial, catering and more. Full detail pages, pricing, floor areas, energy labels, coordinates, images, and agent/agency contacts with ratings. Dual search and URL modes, deep filters, and forward pagination.
Pricing
from $1.00 / 1,000 property scrapeds
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
0
Bookmarked
7
Total users
3
Monthly active users
5 days ago
Last modified
Categories
Share
Funda in Business Scraper: Commercial Property Listings & Agent Contacts
Funda in Business Scraper turns fundainbusiness.nl into structured data for the Dutch commercial property market. Get offices, retail units, industrial halls, catering venues, practice spaces, building plots and investment objects, each with pricing, floor area, energy label, map coordinates, a full photo set, and the listing agent and agency, including agency ratings and reviews when you opt in. Search by city and filters, or paste your own listing and search-result URLs, then export to JSON, CSV or Excel, or pull the results straight into your app through the API.
Why This Scraper?
- Dual input modes. Search mode builds listing URLs from location and filter fields. URL mode scrapes the listing or search-result links you already have.
- Deep filters. Property category, buy or rent, price and area ranges, energy label, construction period, surrounding context, building features, parking, publication date, auction and open day windows, and nine sort orders.
- Full detail-page enrichment. Complete description, every characteristic grouped by section (transfer, construction, areas, layout, energy), map coordinates, all photos and the listing agent's phone number.
- Agency profiles with reviews. Opt in to add each listing's agency contact details, address, employee count, average rating and individual reviews.
- Rich nested output. Pricing, location, property characteristics, media and agency relationships come back as one structured record, not a flat row.
- Built for schedules. Incremental mode tracks a search over time and returns only new and changed listings on recurring runs, and a rejected run fails loudly instead of returning an empty dataset.
- Optional app export. Pipe results into Notion, Linear, Airtable or Apify through MCP connectors after the scrape.
Use Cases
- Commercial brokers and agencies: track competing listings and new inventory across Dutch cities to spot pricing and availability trends.
- Investors and asset managers: build a searchable database of office, retail, industrial and investment properties with pricing, area and location for deal sourcing.
- Market research and PropTech: monitor supply, energy labels and construction periods across the commercial market for reports and models.
- Lead generation: collect listing agent and agency contact details, plus agency ratings, for outreach.
- Recurring monitoring: schedule the actor with Incremental mode to catch only new and updated listings in a target market without re-downloading everything each run.
Data You Get
Sample shape: values are illustrative placeholders, not from a live listing.
| Field | Example |
|---|---|
record_id | "00000001" |
entity.title | "Sample Street 1, Amsterdam" |
entity.description | "Full listing description..." |
entity.category | "Office" |
listing.deal_type | "rent" |
listing.offer_type | "rent" |
listing.is_featured | false |
listing.available_from | "Per direct beschikbaar" |
pricing.price / pricing.price_text | 3250 / "EUR 3.250 /mnd" |
pricing.currency | "EUR" |
property.property_type | "office" |
property.usage_types | ["Office", "Retail"] |
property.floor_area | 98 |
property.energy_label | "A" |
property.construction_year | "2020" |
location.city / location.region | "Amsterdam" / "Noord-Holland" |
location.latitude / location.longitude | 52.0000 / 4.0000 |
media.image_count | 6 |
media.image_urls | array of photo URLs |
relationships.agency.name | "Sample Brokers" |
relationships.agency.phone | "+310000000000" |
relationships.agency.email | "agent@example.com" (agency enrichment on) |
relationships.agency.reviews.average_score | 8.6 (agency enrichment on) |
source_context.source_url | listing detail page URL |
source_context.enrichment_status | "enriched" or "basic" |
Every record also carries a property block with construction_type and features (the full grouped characteristics from the detail page), an availability block with status and available_from, and an attributes.kenmerken_summary with the raw search-card characteristics. With agency enrichment on, relationships.agency also carries address, description, employee_count, and the full employees and reviews.latest lists.
How to Use
- Pick a mode:
search(filters) orurl(paste links). - Fill in the fields for that mode: locations and filters for search, or listing and search-result URLs for URL mode.
- Turn on Fetch full detail pages and Include agency profile and reviews for full enrichment, or leave them off for a lighter run.
- Set Max items to control run size and cost, then click Start.
Search a city for offices for rent, cards only:
{"mode": "search","locations": ["Amsterdam"],"propertyType": "office","dealType": "rent","maxItems": 50,"fetchDetails": false}
Search with deep filters and full enrichment:
{"mode": "search","locations": ["Rotterdam", "Utrecht"],"propertyType": "retail","dealType": "buy","minPrice": 100000,"maxPrice": 2000000,"minArea": 80,"energyLabel": ["a", "b", "c"],"sortBy": "sale_price","fetchDetails": true,"includeAgencyDetails": true,"maxItems": 100}
Scrape from URLs (multiple, paginated):
{"mode": "url","urls": ["https://www.fundainbusiness.nl/kantoor/amsterdam/","https://www.fundainbusiness.nl/winkel/rotterdam/"],"maxPages": 5,"maxItems": 100,"fetchDetails": true}
Track a search on a schedule, only changes each run:
{"mode": "search","locations": ["Amsterdam"],"propertyType": "office","incrementalMode": true}
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/fundainbusiness-nl-commercial-property-scraper").call(run_input={"mode": "search", "locations": ["Amsterdam"], "propertyType": "office"})for listing in client.dataset(run["defaultDatasetId"]).iterate_items():print(listing["entity"]["title"], listing["pricing"]["price_text"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/fundainbusiness-nl-commercial-property-scraper').call({mode: 'search',locations: ['Amsterdam'],propertyType: 'office',});const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
Which filters apply in which mode
Every filter from Property type down to Sort by is posted to the Funda in Business search form, so it only narrows results in search mode. URL mode never sends that form and ignores them. Only highlighted listings is the exception: Funda in Business has no server-side option for it, so the run applies it to every result page it walks, which means it also narrows a search-result URL you pasted in URL mode. A pasted single-listing URL is scraped directly and is never dropped by this filter.
Property type and Buy or rent do one extra thing in both modes: their values are copied onto every record as labels (property.property_type, source_context.search.property_type, source_context.search.deal_type, and the listing.deal_type fallback used when a listing's price text does not say whether it is for sale or for rent). In URL mode they label the output without filtering it, so leave them at their defaults there unless you want that label.
Resume and recurring updates
There are two different things here, pick the one that matches what you are doing:
- A crawl stopped partway through and should continue: use Resume from a previous run (
resumeFromRunId). Paste a run or dataset ID and this run skips listings already collected there, returning only what is left, so you never pay twice for the same crawl. An unreadable ID fails the run with a clear message instead of silently re-scraping and re-billing everything. - Run the same search on a schedule and get only the changes: turn on Incremental changes for scheduled runs (
incrementalMode). The actor remembers the previous run of the same search by itself, so you never paste an ID. The first run for a search returns every listing asNEW. Later runs return onlyNEWandUPDATEDlistings by default, withchangedFieldslisting what changed on anUPDATEDrow. Listings that are unchanged are suppressed and not charged unless you turn on Emit unchanged listings. Classification happens after any detail-page enrichment, so a change visible only on the detail page still gets caught. - What counts as a change: every output field is compared (price, status, location, property and detail data and so on) except the scrape time and the listing's position and page number in the results, which are always left out.
changedFieldsnames the top-level groups that moved. To also leave out a field that changes without meaning anything to you, list it under Ignore fields when detecting changes (incremental mode only) (ignoreFieldsForChanges), by name or dotted path such asentity.statusorpricing.price. Ignored fields stay in the output. Changing this list never causes a one-time wave ofUPDATEDrows. REAPPEAREDandEXPIREDare a pair, and neither is part of the everyday default flow.EXPIREDis only produced once a run has fully scanned the tracked search with Emit expired listings turned on, never when Max items capped the run and never on a resumed run. A listing only comes back asREAPPEAREDon a later run if one of those complete scans had already marked itEXPIREDand it then shows up again. If you never turn on Emit expired listings, a listing that temporarily disappears and reappears is simply classifiedUNCHANGEDorUPDATEDlike any other listing, neverREAPPEARED.- State is kept separately per mode, location or URL set, and filter setup, automatically. Set State key to name or deliberately share a monitoring campaign; leave it empty and the actor derives one for you.
URL shapes (URL mode)
URLs are processed in order. When more than one URL is given, Max items is split evenly across them (rounded up), so every URL gets a share of the budget and the first one cannot use it all. If you leave URLs untouched in URL mode, the run scrapes the built-in example list (one example per shape below), a few listings from each, instead of a single Amsterdam page. Clear or replace the list to scrape only your own links. A URL that finishes under its share does not hand the unused part to the next URL. The www, apex and m. hosts are treated as the same site, and duplicates are dropped. The built-in list also holds an apex-host example (fundainbusiness.nl, without www) and a broker-directory example; the directory example is skipped with a warning in the log.
| Shape | Example | Behaviour |
|---|---|---|
| Category and location search | https://www.fundainbusiness.nl/kantoor/amsterdam/ | Paginated forward |
| All offerings in a location | https://www.fundainbusiness.nl/alle-bedrijfsaanbod/amsterdam/ | Paginated forward |
| Category, whole country | https://www.fundainbusiness.nl/kantoor/ | Paginated forward |
| Start on page N | https://www.fundainbusiness.nl/kantoor/amsterdam/p2/ | Starts at that page; counts as partial scope, so incremental runs never mark listings expired |
| Rent only or buy only | .../kantoor/amsterdam/huur/, .../koop/ | Paginated forward |
| Sold or let archive | .../kantoor/amsterdam/verhuurd/huur/ | Paginated forward |
| English pages | https://www.fundainbusiness.nl/en/kantoor/amsterdam/ | Paginated forward |
| Listing | .../kantoor/amsterdam/object-44533351-herengracht-575/ | Scraped directly |
| Project | .../alle-bedrijfsaanbod/amsterdam/project-89938260-kantoorgebouw-delphi/ | Fetches the detail page of each listing the project page links, so each returned listing is billed a detail enrichment as well. With Fetch full detail pages turned off, project pages are skipped with a warning and nothing is fetched or billed for them |
| Agency profile | https://www.fundainbusiness.nl/makelaar/24607-krk-bedrijfsmakelaars-amsterdam/ | Follows the profile's own "all listings" page (the agency's whole offering) and paginates it forward like a search; detail pages follow the Fetch full detail pages setting |
Broker directory, id-only /object-<id>/ | .../bedrijfsmakelaars/amsterdam/ | Not supported: skipped with a warning in the log |
Send results into your apps (MCP connectors)
You can optionally pipe each run's results into the apps you already use through Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape: the Apify dataset is never changed.
What gets written to the connector: a condensed, human-readable summary of each listing, a title plus its key fields (price, area, energy label, city, agency and phone) flattened to plain text, not the full JSON. The complete record always stays in the Apify dataset.
- Authorize a connector once under Apify then Settings then API & Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the Pipe results into your apps input field. If the picker is empty, you have not authorized a connector yet.
- For Notion, also set Notion parent page (
notionParentPageUrl) to the page where item pages should be created.
Leave the field empty to skip; it never changes the dataset output.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | search | search builds URLs from filters; url scrapes pasted links. |
locations | array | none (prefill: Amsterdam) | Cities, towns or areas to search (search mode). Each becomes a separate search; left empty, the run searches all of the Netherlands. |
propertyType | string | all | Commercial category, for example office, retail, catering. Filters only in search mode; also copied onto every record as a label in both modes. |
dealType | string | both | Buy, rent, or both. Filters only in search mode; also copied onto every record as a label, and as the price-text fallback, in both modes. |
keyword | string | none | Optional free-text term to narrow results (search mode only). |
minPrice | integer | none | Lowest price or rent (search mode). For rent, read together with Rent price basis. |
maxPrice | integer | none | Highest price or rent (search mode). |
rentPriceBasis | string | per_month | How rent price bounds are applied: per month, per year, or per square metre. |
minArea | integer | none | Smallest floor area in square metres (search mode). |
maxArea | integer | none | Largest floor area in square metres (search mode). |
publicationDate | string | any | Only listings published within this window (search mode). |
surrounding | array | none | Filter by the type of area the property sits in, for example town centre or office park (search mode). |
amenities | array | none | Require one or more building features (search mode). |
availability | array | none | Filter by availability signals, for example available now or turnkey (search mode). |
parking | string | any | Minimum parking capacity (search mode). |
constructionType | string | any | Existing or new construction (search mode). |
propertyAge | array | none | Construction period buckets (search mode). |
energyLabel | array | none | Energy labels A to G (search mode). |
auctionDate | string | any | Upcoming auction window (search mode). |
openDay | string | any | Open day availability (search mode). |
includeSoldRented | boolean | false | Also include historical listings already sold or rented (search mode). |
sortBy | string | relevance | Result ordering; nine options (search mode). |
urls | array | the built-in example URLs, one per supported shape (see the URL shapes table) | Funda in Business listing, project, agency or search-result URLs to scrape (URL mode). |
highlighted | boolean | false | Only featured listings. Applies in both search mode and URL mode, except a pasted single-listing URL. |
resumeFromRunId | string | none | Continue one specific interrupted or previous run; listings already collected there are skipped. |
incrementalMode | boolean | false | Turn on for scheduled runs of the same search; classifies each listing as NEW, UPDATED, UNCHANGED, REAPPEARED or EXPIRED. |
stateKey | string | none | Optional name for a monitoring campaign; auto-derived from your mode, location or URL and filter settings when left empty. |
emitUnchanged | boolean | false | Incremental mode only. Also return, and bill, listings unchanged since the last run. |
emitExpired | boolean | false | Incremental mode only. Also return, and bill, listings from a previous run no longer found, once a run fully scans the tracked search. |
ignoreFieldsForChanges | array | none | Incremental mode only. Fields (names or dotted paths) left out of change detection and changedFields; they stay in the output. |
language | string | nl | Language of the listing pages, Dutch or English. |
fetchDetails | boolean | true | Include the full description, all characteristics, coordinates, all images and the agent phone for each listing. |
includeAgencyDetails | boolean | false | Enrich each listing's agency with contact details, address, ratings and reviews from its profile page. |
maxItems | integer | 20 | Maximum listings to return across the whole run; the single cap; set 0 for unlimited. |
maxPages | integer | 0 | Safety ceiling on result pages walked per search; leave at 0, the run stops at Max items and a hard cap of 200 pages per search applies (values above 1000 are clamped). |
proxy | object | Apify Proxy (automatic) | Connection settings. The default works automatically; you can pin a specific country if needed. |
mcpConnectors | array | none | Optional: send a summary of each listing to apps you authorized under Integrations. |
notionParentPageUrl | string | none | Notion connector only: 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 listing.
{"record_type": "property_listing","record_id": "00000001","source_context": {"source_domain": "fundainbusiness.nl","source_url": "https://www.fundainbusiness.nl/kantoor/amsterdam/object-00000001-sample-street-1/","enrichment_status": "enriched","page_number": 1,"position": 1,"scraped_at": "2026-01-01T00:00:00+00:00","language": "nl","country": "NL"},"entity": {"title": "Sample Street 1, Amsterdam","description": "Full listing description appears here when Fetch full detail pages is on.","url": "https://www.fundainbusiness.nl/kantoor/amsterdam/object-00000001-sample-street-1/","category": "Office"},"listing": {"deal_type": "rent","offer_type": "rent","is_featured": false,"source_category": "Kantoor","available_from": "Per direct beschikbaar"},"pricing": { "price": 3250, "currency": "EUR", "price_text": "EUR 3.250 /mnd" },"location": { "city": "Amsterdam", "region": "Noord-Holland", "latitude": 52.0000, "longitude": 4.0000 },"property": { "property_type": "office", "floor_area": 98, "energy_label": "A", "construction_year": "2020" },"media": { "image_count": 6, "image_urls": ["https://cloud.funda.nl/valentina_media/000/000/000_1440x960.jpg"] },"relationships": {"agency": {"name": "Sample Brokers","url": "https://www.fundainbusiness.nl/makelaar/0000-sample-brokers/","phone": "+310000000000","email": "agent@example.com","reviews": { "total": 2, "average_score": 8.6, "formatted_average_score": "8,6" }}}}
Output views
The dataset has four views, listed on the run's Output tab, plus a run summary:
- Listings: one row per listing with title, price, area, city and agency.
- Listing details: the full detail data (description, characteristics, coordinates, photos).
- Agencies: agency contact details, ratings and reviews.
- Changes: the incremental status and
changedFieldsof each row. - Run summary: the
OUTPUTrecord of the run's key-value store.
Plan Requirement
Funda in Business only serves its real pages to browser-like connections. A plain request is answered with a "you are almost on the page" holding page, and the actor detects that page by its content (a 200 status alone proves nothing). The actor reads pages as a browser would, and this works through datacenter connections, so a residential proxy is not required. The default connection is Apify Proxy, and a run falls back on its own to a backup connection when the plan's proxy tunnel is refused.
What you see when a connection is refused:
- On a plan without proxy access, the log shows "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." and the run carries on.
- If no page can be loaded at all, the run fails with a clear status message instead of an empty dataset: "The proxy connection was refused for this account ... a paid plan is required", or "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested Funda in business pages", or "Could not load any results page (the requests failed) ... This is a connection problem, not an empty search."
- Datacenter access was measured on a short run only. For long or frequent monitoring, you can pin the residential proxy group under Connection.
FAQ
How much does it cost?
Billing uses three events: the actor start (apify-actor-start, once per run), each listing returned to the dataset (apify-default-dataset-item, per listing), and the detail enrichment (detail-enrichment, per listing whose full detail page was actually fetched and returned). Unchanged, suppressed and failed listings are not charged. The Pricing tab shows the current rates. Use Max items to cap the cost of any run.
Is it legal to scrape Funda in Business?
This actor collects only publicly available property listing data. You are responsible for how you use it: follow Funda in Business's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Listing photos and agency information may be subject to third-party rights.
Can I get only new or changed listings on a schedule?
Yes. Schedule the actor from the Schedules tab and turn on Incremental changes for scheduled runs. Each run then returns only new and updated listings by default, and unchanged ones are not billed.
What is the difference between Resume and Incremental mode?
Resume from a previous run continues one specific interrupted crawl from a pasted run or dataset ID and never repeats what that run already collected. Incremental changes for scheduled runs is for running the same search again and again, for example daily, without pasting anything: the actor remembers the previous run itself and returns only what changed. Use Resume after an interruption, and Incremental mode for ongoing monitoring.
Why did my run fail instead of returning an empty dataset?
If every page fetch for a search was rejected, the run stops with a clear message instead of quietly returning zero listings, so "no listings match this search" is never confused with "nothing could be read." Run it again in a few minutes. In URL mode, a pasted listing URL that comes back as a clean 404 is treated as a normal outcome, the listing was removed or sold, not a failure; only fetches that exhaust every retry count toward that failure message.
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client, and use the connector field to push listing summaries into Notion, Linear or Airtable.
๐ Want more real estate data?
Pair this actor with these related scrapers from the same team:
| ๐ ImmobilienScout24.de Scraper Extract property listings from immobilienscout24.de, Germany's #1 real estate platform... | ๐ CommercialGuru SG Property Listings & Agent Contacts Scraper Scrape commercialguru.com.sg Singapore listings at scale. Extract prices, PSF, floor... |
| ๐ Immowelt.de Scraper Scrape detailed property listings from Immowelt.de into clean structured data. Extract... | ๐ Commercial Property AU Scraper Scrape Australian commercial property listings with full detail data. Extract... |
| ๐จ LoopNet Scraper Scrape LoopNet commercial listings across the US, including office, retail, industrial... | ๐ผ Werk.nl Scraper Scrape job vacancies from werk.nl, the official Dutch government job board by UWV. Uses... |
๐ Browse all abotapi scrapers
๐ฌ 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.