PAP.fr Scraper - France Properties, Prices & Listings
Pricing
from $1.00 / 1,000 results
PAP.fr Scraper - France Properties, Prices & Listings
Scrape PAP.fr real estate listings across France. Extract property prices, addresses, room counts, floor areas, full photo galleries, descriptions and location details including arrondissement, department and region. Ideal for property research, price comparison and market analysis.
Pricing
from $1.00 / 1,000 results
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
1
Bookmarked
38
Total users
5
Monthly active users
a day ago
Last modified
Categories
Share
PAP.fr Scraper: Owner Listed Property Data for France
PAP.fr, De Particulier a Particulier, is France's largest site for owner to owner property listings, with no estate agencies involved. This scraper turns it into structured data: price, rooms, living area, full address, arrondissement, department and region, energy ratings, and the complete photo gallery for sale and rental listings. Pick a city and property type from the form, or paste a PAP.fr search URL for finer filters, then export to JSON, CSV or Excel, or read the results through the API.
Why This Scraper?
- Owner listed only. PAP.fr does not accept agency listings, so every record is a private seller or landlord, not an agent relisting the same property everywhere.
- Two ways to search. Type a French city or district name and pick filters from the form (price, living area, land area, rooms, bedrooms, property categories, features, sort), or paste any PAP.fr search, city or listing URL you already opened in your browser.
- Detail depth on demand. Turn on detail fetching for coordinates, energy and greenhouse gas ratings, street address, seller phone, reference number, bedrooms, floor, charges, property tax and the full photo gallery. Turn it off for a faster run with just the results page fields.
- French geography, resolved. Type a place the way you would on PAP.fr ("Paris", "Lyon 3e", "Saint-Malo"): it is looked up in PAP.fr's own location search, the department is derived when you leave it blank, a name shared by several departments is flagged in the log with the alternatives, and a name PAP.fr does not know is reported with its suggestions instead of being guessed.
- Auto pagination. Walks every result page forward to the limit you set, whether you start from the form or from a pasted URL that already points at page 10.
- Built for monitoring. Incremental mode returns only new, updated and reappeared listings on repeat runs of the same search, and resume support lets a large one time pull continue across separate runs without re collecting anything.
Use Cases
- Buyers and renters: track new owner listed apartments or houses in specific cities without agency markups or duplicate listings.
- Market research: build a dataset of asking prices, living area and rooms across French cities to study pricing trends.
- Investors and analysts: monitor a set of departments for new listings, price drops or delisted properties over time.
- Proptech and data teams: feed owner listed inventory into a search tool, valuation model or lead pipeline.
Data You Get
Sample shape: values are illustrative placeholders, not from a live record.
| Field | Example |
|---|---|
id | "400000001" |
url | "https://www.pap.fr/annonces/appartement-paris-18e-75018-r400000001" |
listingType | "Buy" (also "Rent") |
propertyType | "Apartment" |
title | "Vente appartement 3 pieces 57 m2 Paris 18e" |
priceValue | 590000 |
priceDisplay | "590 000 €" |
rooms | 3 |
livingSpace | 57 |
streetAddress | "Rue Muller, 75018 Paris" |
city | "Paris 18e" |
arrondissement | "Paris 18e" |
postcode | "75018" |
department | "Paris" |
region | "Ile-de-France" |
departmentCode | "75" (from the postcode; "2A"/"2B" for Corsica, 3 digits overseas) |
latitude / longitude | 48.8358 / 2.2899 (when fetchDetails is on) |
energyClass / ghgClass | "D" / "D", A to G (when fetchDetails is on) |
reference | "G20/2718" |
imageCount | 18 |
scrapedAt | "2026-04-22T10:00:00.000Z" |
The same record also carries bedrooms, floor, hoaChargesYearly, propertyTaxYearly and heatingType (parsed from the seller's description, so they can be missing), isActive (the listing is still on offer, from PAP.fr's own offer status), sellerPhone and seller (the "Particulier" tag PAP uses for private listings), refusesCommercialSolicitation, isFurnished (rentals: true for a furnished let, false for an unfurnished one, null for sales), the full images array and additionalProperties (PAP's own French key and value pairs, such as "Type de bien"). With incrementalMode on, each record also gets changeType, changedFields, firstSeenAt and lastSeenAt.
How to Use
- Pick a search mode:
filter(city, department and property type) orurl(paste PAP.fr search links). - Fill in the fields for that mode. In filter mode you can leave the department blank for well known cities, it is derived automatically.
- Turn detail fetching on or off, and set Max pages and Max listings to control run size and cost.
- Click Start, then download the dataset as JSON, CSV or Excel, or read it through the API.
Search one city:
{"mode": "filter","locations": [{ "city": "Paris", "department": "75" }],"listingType": "Buy","propertyType": "Apartment","maxPages": 10}
Rentals with a rooms and sort filter:
{"mode": "filter","locations": [{ "city": "Lyon", "department": "69" }],"listingType": "Rent","propertyType": "Apartment","roomsMin": 3,"sortBy": "PriceAsc","maxPages": 10}
Several cities at once, department left to auto detect:
{"mode": "filter","locations": [{ "city": "Paris" },{ "city": "Marseille" },{ "city": "Bordeaux" }],"listingType": "Buy","propertyType": "House","maxPages": 5}
A pasted URL with a rooms filter and price sort, resuming from page 3:
{"mode": "url","urls": ["https://www.pap.fr/annonce/vente-appartements-paris-75-g439-3-pieces-tri-prix-asc-3"],"maxPages": 20}
Supported URL shapes (URL mode)
Any URL copied straight from pap.fr passes through unchanged: PAP's own server accepts price, surface and bedroom filters as extra path segments, multi-type and generic searches, department/region level and nationwide searches, and the alternative spellings PAP.fr itself uses on its page links. A pasted listing/detail page is fetched as a single record, never run through the search/pagination path. A link missing https:// is accepted, and an HTML-escaped & is decoded.
| Shape | Example | Notes |
|---|---|---|
| Default search | https://www.pap.fr/annonce/vente-appartements-paris-75-g439 | City + department + PAP's own location id |
| Rent (singular estate slug) | https://www.pap.fr/annonce/locations-appartement-paris-75-g439 | locations-appartement, not -appartements |
| Rooms filter | .../vente-appartements-paris-75-g439-3-pieces | N-pieces suffix |
| Sort order | .../vente-appartements-paris-75-g439-tri-prix-asc | tri-<dim>-asc|desc suffix |
| Start page | .../vente-appartements-paris-75-g439-2 | Trailing -N; scraping starts at page N |
| Multiple property types | https://www.pap.fr/annonce/vente-maisons-appartements-paris-75-g439 | Each listing keeps its own correct type |
| Generic (all types) | https://www.pap.fr/annonce/vente-immobilier-paris-75-g439 | PAP's catch all search |
| Price / surface / bedroom filters | .../vente-appartements-paris-7e-g37774-studio-a-partir-de-1-chambres-jusqu-a-150000-euros | The same filters filter mode builds from its typed inputs |
| Alternative spelling (PAP.fr's own page links) | https://www.pap.fr/annonce/appartement-a-louer-paris-75-g439-2 | Also appartement-a-vendre-…, ventes-appart-…, offre-location-… and the like; sale or rent is read from the wording |
| Department only (no city) | https://www.pap.fr/annonce/vente-appartements-75 | Whole department |
| Region | https://www.pap.fr/annonce/vente-appartements-ile-de-france | Whole region |
| Nationwide | https://www.pap.fr/annonce/vente-appartements | All of France |
| City page | https://www.pap.fr/immobilier/paris-75-g439 | Converted to the full all-types search for that place, sale or rent per listingType |
| Tracking parameters | ...-g439?utm_source=newsletter | Query string is ignored, not an error |
| Listing / detail page | https://www.pap.fr/annonces/appartement-paris-18e-75018-r464203311 | Fetched directly as one record, keyed on its r<id> |
Default behaviour (changed): in URL mode, leaving the URLs field untouched scrapes the built-in sample list (one URL per supported shape above, 11 in all) in order, up to Max total listings (default 20; each search is walked page by page until the cap, so the default usually returns 20 listings from the first sample search only, page 1 and part of page 2), instead of nothing. To sample every shape, set a small maxPages (for example 1) together with a higher maxListings, because without maxPages the first search alone can use up any cap. Replace the list with your own URLs, or set maxListings/maxPages low for a cheap trial. The m.pap.fr mobile host is accepted and rewritten to www.pap.fr.
Anything else, the bare pap.fr homepage, any other PAP page, or a non PAP.fr URL, is skipped with a warning naming the URL, never silently scraped as an empty result. If none of the pasted URLs is usable, the run ends with an error saying so.
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/pap-fr-scraper").call(run_input={"mode": "filter","locations": [{"city": "Paris", "department": "75"}],"listingType": "Buy","propertyType": "Apartment","maxPages": 5,})for listing in client.dataset(run["defaultDatasetId"]).iterate_items():print(listing["title"], listing["priceDisplay"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/pap-fr-scraper').call({mode: 'filter',locations: [{ city: 'Paris', department: '75' }],listingType: 'Buy',propertyType: 'Apartment',maxPages: 5,});const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
Filter mode's full filter set
Filter mode builds price, living-area, land-area, rooms, bedrooms, property-type and feature filters directly from typed inputs, and can search sales and rentals together (listingTypes) (priceMin/priceMax, surfaceMin/surfaceMax, landSurfaceMin/landSurfaceMax, roomsMin/roomsMax, bedroomsMin/bedroomsMax, propertyTypes, features, excludeNewBuild, strictLocation), no need to paste a URL just to apply those. URL mode is still the right tool for a department-, region- or France-wide search with no single city, PAP's own "all property types" catch-all search, or any search you already refined and copied straight from your browser.
Resume and recurring updates
- Resume (
resumeFromRunId) continues one interrupted, large, one time pull: paste its run or dataset ID and the actor skips every listing id already saved there, so you only pay for the new ones. The actor also checkpoints its own progress automatically, so a platform restart or a manual resurrect of a failed run picks back up without re saving what it already collected. - Incremental mode (
incrementalMode) is for scheduling the same search again and again. Each listing is classifiedNEW,UPDATED(withchangedFields),UNCHANGED,REAPPEARED, orEXPIRED. By default onlyNEW,UPDATEDandREAPPEAREDrows are returned and billed. Turn onemitUnchangedoremitExpiredonly if you also want those rows.EXPIREDrows only appear after a run that scanned the entire tracked search, not one capped bymaxPagesormaxListings, blocked partway through, run withresumeFromRunId, or a URL-mode search that started mid-catalogue (a pasted URL whose trailing page number is greater than 1). Even then, EXPIRED rows never exceed the run's own remainingmaxListingsroom; the rest are retried on a later run.REAPPEAREDcan only ever be produced for a listing that was previously markedEXPIRED, so it is only reachable onceemitExpiredhas been turned on for at least one earlier run.stateKeynames or deliberately shares the tracked state; left blank, a key is derived from the search itself, so two different searches never mix state.ignoreFieldsForChangeslets you exclude extra fields (beyond the built-inscrapedAt,searchPageandsearchPosition, which change on every run or with PAP.fr's result order) from change detection, using a bare name, a nested path (seller.phone), or a nested wildcard (seller.*,additionalProperties.*); ignored fields are still returned in every row, only the change/diff logic ignores them. When an update of this actor corrects how an existing field is read (for example coordinates,isActiveandtitlein October 2026), a monitoring campaign started before it does not see a one-time wave ofUPDATEDrows for that correction; new fields such asdepartmentCodeandisFurnishedlikewise never count as a change on their first run.
Send results into your apps (MCP connectors)
Optionally pipe the scraped results into the apps you already use, via 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, not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.
- Authorize a connector once under Apify -> Settings -> 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
notionParentPageUrlto the page where items should be created.
The connection is mediated by Apify's MCP proxy, so this actor never sees your third party credentials. Leave the field empty to skip.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | filter | filter for city and department inputs, or url for pasted PAP.fr search URLs. |
locations | array | prefill: Paris | Places to search (filter mode only): a city, town or district name as on PAP.fr (Paris, Lyon 3e), optionally with its department code. Looked up in PAP.fr's own location search; the department is auto derived if left blank, ambiguous names are flagged in the log, unknown names are reported with PAP.fr's suggestions. Falls back to Paris, department 75, if the array is left empty. |
urls | array | the built-in sample list, one URL per supported shape (11 URLs), scraped in order up to Max total listings | PAP.fr search, city or listing URLs (url mode only). A trailing -N page number is honored as the starting page. |
references | array | (none) | Optional, both modes. PAP.fr listing reference codes as printed on the listing ("Réf. : G35/3579"). Each one is looked up and returned as a single listing. Leave Locations empty in filter mode to look up references only. |
listingType | string | Buy | Buy (Vente) or Rent (Location). Filter mode only. |
listingTypes | array | (none) | Optional. Tick both Buy and Rent to search sales and rentals for every location in one run; each listing keeps its own listingType. When set, this replaces listingType. Filter mode only. |
propertyType | string | Apartment | Apartment, House, Plot, Parking, or CommercialProperty. Filter mode only. |
roomsMin | integer | (none) | Minimum number of rooms (pieces), e.g. 3 returns 3, 4, 5... rooms. 6 means "6 or more". Filter mode only. |
roomsMax | integer | (none) | Maximum number of rooms (pieces). Combine with Min rooms for a range, e.g. 2-3. 6 or more means no upper limit. Filter mode only. |
bedroomsMin | integer | (none) | Minimum number of bedrooms (chambres), applied by PAP.fr's own search. Combine with Max bedrooms for a range. Filter mode only. |
bedroomsMax | integer | (none) | Maximum number of bedrooms (chambres). PAP.fr's own search has no working maximum, so this is checked on each result instead: a listing without a printed bedroom count is left out, and a 1-room studio counts as 0 bedrooms. Filter mode only. |
priceMin | integer | (none) | Minimum price in euros (sale price, or monthly rent for rentals). Filter mode only. |
priceMax | integer | (none) | Maximum price in euros (sale price, or monthly rent for rentals). Filter mode only. |
surfaceMin | integer | (none) | Minimum living area in m². Filter mode only. |
surfaceMax | integer | (none) | Maximum living area in m². Filter mode only. |
landSurfaceMin | integer | (none) | Minimum land/garden area in m² (houses and plots). Filter mode only. |
landSurfaceMax | integer | (none) | Maximum land/garden area in m² (houses and plots). Filter mode only. |
propertyTypes | array | (none) | Optional. One or more of PAP.fr's own property categories, including ones propertyType doesn't have (whole buildings, life-annuity sales, houseboats, offices...). When set, this replaces propertyType. Values PAP.fr doesn't offer for the chosen Buy/Rent are ignored with a warning. Filter mode only. |
features | array | (none) | Optional. Only return listings tagged with ALL of these PAP.fr features (lift, balcony/terrace, pool, furnished...). Some apply to sales only or rentals only; ones that don't fit the chosen Buy/Rent are ignored with a warning. Filter mode only. |
excludeNewBuild | boolean | false | Hide new-build developer programmes from sale results. Sales only. Filter mode only. |
strictLocation | boolean | false | Keep only listings inside the exact place you searched. Off by default: PAP.fr also includes nearby places when the exact one has few listings, and each of those carries its distance in distanceFromSearchKm. Filter mode only. |
sortBy | string | Default | Default, PriceAsc, PriceDesc, SurfaceAsc, SurfaceDesc, or DateAsc. Filter mode only, ignored in url mode. |
maxPages | integer | (empty, unlimited) | Maximum result pages to walk per search. Leave empty to walk every result page. |
maxListings | integer | 20 | Hard cap on listings across the whole run. 0 means unlimited, still bounded by maxPages. |
fetchDetails | boolean | true | Visit each listing's page for coordinates, energy ratings, street address, full description, reference number, bedrooms, floor, charges, property tax, heating type and the full photo gallery. Each detail page that loads bills the detail-enrichment event (Pricing tab). |
resumeFromRunId | string | (none) | A previous run ID or dataset ID to continue a large walk across separate runs. Listings already saved there are skipped. |
incrementalMode | boolean | false | Turn on for recurring monitoring of the same search. Later runs return only new, updated and reappeared listings by default. |
stateKey | string | (none) | Optional name for an incremental mode monitoring campaign. Left blank, a key is derived automatically from the search itself. |
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 no longer found, after a run that fully scanned the tracked search. Capped at the run's own maxListings headroom; any excess is retried on a later run. |
ignoreFieldsForChanges | array | [] | Incremental mode only. Extra field names/paths to exclude from change detection, on top of the built-in defaults (scrapedAt, searchPage, searchPosition). Supports a nested leaf (seller.phone) or a nested wildcard (seller.*). Ignored fields are still returned in the output. |
proxy | object | Apify Proxy, RESIDENTIAL (prefill pins IT) | Connection settings. A residential proxy is needed for this site. |
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 listing pages are created. |
maxNotifyListings | integer | 50 | Cap on listings exported to each connector per run. Does not affect the dataset. |
Output Example
Sample shape: values are illustrative placeholders, not from a live record.
{"id": "400000001","url": "https://www.pap.fr/annonces/appartement-paris-18e-75018-r400000001","listingType": "Buy","propertyType": "Apartment","title": "Vente appartement 3 pieces 57 m2 Paris 18e","priceValue": 590000,"priceCurrency": "EUR","priceDisplay": "590 000 €","rooms": 3,"livingSpace": 57,"bedrooms": 2,"floor": 4,"hoaChargesYearly": 2766,"propertyTaxYearly": 1450,"heatingType": "individuel gaz","description": "Bel appartement lumineux au 4e etage avec ascenseur, proche commerces et metro.","streetAddress": "Rue Muller, 75018 Paris","city": "Paris 18e","postcode": "75018","arrondissement": "Paris 18e","department": "Paris","region": "Ile-de-France","departmentCode": "75","latitude": 48.8358,"longitude": 2.2899,"energyClass": "D","ghgClass": "D","isActive": true,"reference": "G20/2718","sellerPhone": "01 99 00 12 34","seller": { "@type": "Person", "name": "Particulier" },"refusesCommercialSolicitation": false,"isFurnished": null,"imageCount": 18,"images": ["https://cdn.pap.fr/photos/example-1.jpg", "https://cdn.pap.fr/photos/example-2.jpg"],"additionalProperties": { "Type de bien": "Appartement", "Ascenseur": "Oui" },"scrapedAt": "2026-04-22T10:00:00.000Z"}
With incrementalMode on, each record also carries changeType (NEW, UPDATED, UNCHANGED, REAPPEARED or EXPIRED), changedFields, firstSeenAt and lastSeenAt. These four fields are absent entirely when incrementalMode is off, the default output shape is unchanged.
Output views
Next to the default Listings table (every field in one row), two narrower views give you the same rows with just the columns for one job: Listing detail (the fields fetchDetails adds, plus location and seller contact) and Changes since the last run (id, url, title, priceValue, changeType, changedFields, firstSeenAt/lastSeenAt for incremental runs). Switch between them on the run's Dataset tab, or fetch one directly through the API with ?view=detail or ?view=changes.
Plan Requirement
PAP.fr sits behind a bot-protection challenge. In our latest checks, plain datacenter-style connections were always refused, so a datacenter proxy does not work for this site. We have not measured that a residential connection alone is enough: the scraper relies on its built-in challenge handling on top of the residential connection, and acceptance varies per exit, so some attempts are retried on fresh exits. Keep the default connection setting, it already points at a residential proxy group. For a run that supplies its own proxy under Connection, use a residential provider. Apify free plans do not include residential proxies: a free run automatically continues on a backup connection instead, with a one-time warning that the selected connection is unavailable and some pages may still be missed, and a note in the log whenever a page is actually served over that backup connection. If the backup connection cannot load any page for the search either, a free run ends with an error pointing at upgrading or supplying your own proxy URLs, rather than finishing silently empty. The built-in challenge handling itself depends on a session service on our side; when that service is temporarily unavailable, the run switches to the backup connection for its remaining reads and says so in the log, instead of failing every page on a service that cannot answer.
FAQ
How much does it cost?
You pay per listing returned. The Pricing tab shows the current rates. Use Max listings and Max pages to cap the cost of any run.
With Visit each listing's detail page on, every listing whose detail page loads also bills the actor's detail-enrichment event — its price is on the Pricing tab. Listings whose detail page could not be read ship with just the SERP-level fields and are never billed for it, and rows suppressed as unchanged by an incremental (scheduled) run are not billed for it either.
Is it legal to scrape PAP.fr?
This actor collects only publicly available listing data. You are responsible for how you use it: follow PAP.fr's terms and the laws that apply to you, and get legal advice before any commercial redistribution. Seller phone numbers and descriptions are personal data in some jurisdictions, handle exported records accordingly.
Can I get only new or changed listings on a schedule?
Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new, updated and reappeared listings, and unchanged ones are not returned or billed unless you turn emitUnchanged on.
Can I filter by price, living area or bedrooms?
Yes, in filter mode: priceMin/priceMax, surfaceMin/surfaceMax, landSurfaceMin/landSurfaceMax, roomsMin/roomsMax and bedroomsMin/bedroomsMax are applied by PAP.fr's own search (bedroomsMax is checked on each result). A search you already refined on PAP.fr can also be pasted as-is in url mode.
Why did my run fail instead of returning an empty dataset?
The run fails for a configuration problem, such as a resumeFromRunId that does not match a run or dataset you can read, or combining resumeFromRunId with an incremental campaign that already has saved state. It also fails when it could not load a single usable results page for the whole run: either every connection lane was refused outright (your plan or your own proxy credentials don't give this run proxy access at all), or every lane it could reach never got back a page it could read. It also fails when none of the places or URLs you gave could be used (every location unknown to PAP.fr, or every pasted URL unsupported); the warnings above that error name each one. Each case ends with a clear message naming which happened and what to do next, instead of a silent empty dataset. A genuinely empty search is different: once at least one results page loaded and PAP.fr itself reported no matching listings, the run still finishes normally with an empty dataset, that means nothing matched, not that the run malfunctioned. Check the run log either way, it explains which case happened.
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:
| 🏷️ Leboncoin FR Pull public leboncoin.fr listings across cars, property, jobs, electronics, furniture... | 🏠 SeLoger Scraper Scrape SeLoger.com properties for sale and rent. Search by location with price, room and... |
| 📇 PagesJaunes FR From $0.8/1K. Scrapes business listings from PagesJaunes.fr (French Yellow Pages)... | 💼 France Travail Scraper Scrape job offers from France Travail with titles, companies, locations, salaries... |
| 🥦 Auchan France Scraper Scrape Auchan.fr products: grocery, drinks, household and general merchandise. Real price... | 🛒 Castorama.fr Scrape Castorama.fr DIY and home-improvement products by keyword, category, or URL... |
💬 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.