Pap.fr $1💰| URL Search | Keyword Filters | Rich Output
Pricing
from $1.00 / 1,000 results
Pap.fr $1💰| URL Search | Keyword Filters | Rich Output
From $1/1k. Extract property listings from pap.fr. Get fully-detailed listings with price, address, rooms, area, photo gallery, description, and arrondissement/department/region geography.
Pricing
from $1.00 / 1,000 results
Rating
5.0
(1)
Developer
Abot API
Maintained by CommunityActor stats
0
Bookmarked
25
Total users
9
Monthly active users
18 hours ago
Last modified
Categories
Share
PAP.fr Scraper
Extract property listings from pap.fr: De Particulier à Particulier, France's largest owner-to-owner real-estate platform. Get fully-detailed listings with price, address, rooms, area, photo gallery, description, and arrondissement/department/region geography.
Why This Scraper?
- 20+ fields per listing. Price (numeric + formatted), rooms (pièces), living area, street address, postcode, city, arrondissement, department, region, full photo gallery, description, PAP reference number, and more.
- Two input modes. Pick a French city and department code from the form, or paste any pap.fr search URL you already refined in your browser.
- Rich filter set. Buy or rent, property type, minimum rooms (pièces), sort order - all passed straight to pap.fr so the results are exactly what the site would show.
- Auto pagination. Walks every result page forward to the limit you set.
- Both Buy (Vente) and Rent (Location). Apartments, houses, plots, parking, and commercial property.
- Owner-to-owner exclusive. PAP forbids agent listings - every record is a private-owner listing you won't find on the agent-heavy aggregators.
Data You Get
| Field | Example |
|---|---|
| Title | Vente appartement 3 pièces 57 m² Paris 18E |
| Price | 590 000 (numeric) + 590.000 € (formatted) |
| Rooms (pièces) | 3 |
| Living area | 57 m² |
| Street address | Rue Muller, 75018 Paris |
| Postcode | 75018 |
| City | Paris 18e |
| Arrondissement | Paris 18e |
| Department | Paris |
| Region | Île-de-France |
| Reference | G17/2664 (PAP internal reference) |
| Latitude / Longitude | 48.8358, 2.2899 (when fetchDetails=true) |
| Energy class (DPE) | A-G (when fetchDetails=true) |
| GHG class (GES) | A-G (when fetchDetails=true) |
| Active status | true live / false delisted (when fetchDetails=true) |
| Images | Full gallery, high-res URLs |
| Description | Full seller description (when fetchDetails=true) |
| Scraped at | ISO timestamp |
How to Use
Search by Location
Enter a French city and its 2-digit department code. The scraper resolves the pap.fr geo-id automatically.
{"mode": "filter","locations": [{ "city": "Paris", "department": "75" }],"listingType": "Buy","propertyType": "Apartment","maxPages": 10}
Search with Filters
{"mode": "filter","locations": [{ "city": "Lyon", "department": "69" }],"listingType": "Rent","propertyType": "Apartment","roomsMin": 3,"sortBy": "PriceAsc","maxPages": 10}
Multiple Cities
{"mode": "filter","locations": [{ "city": "Paris", "department": "75" },{ "city": "Lyon", "department": "69" },{ "city": "Marseille", "department": "13" },{ "city": "Bordeaux", "department": "33" },{ "city": "Nice", "department": "06" }],"listingType": "Buy","propertyType": "House","maxPages": 5}
Direct URLs with auto pagination
Refine a search in your browser (including price / surface filters which aren't exposed in the form here), copy the URL, paste it here. If the URL ends with -N (page number), scraping starts from page N and walks forward.
{"mode": "url","urls": ["https://www.pap.fr/annonce/vente-appartements-paris-75-g439-3-pieces-tri-prix-asc"],"maxPages": 20}
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | filter | filter for city + department inputs, or url for direct URLs |
locations | array | Paris / 75 | City + 2-digit department pairs (Filter mode only) |
urls | string[] | example URL | pap.fr search URLs (URL mode only); honors trailing -N as start page |
listingType | string | Buy | Buy (Vente) or Rent (Location) - Filter mode only |
propertyType | string | Apartment | Apartment, House, Plot, Parking, or CommercialProperty (Filter mode only) |
roomsMin | number | no limit | Minimum pièces (1-5, where 5 means 5+) - Filter mode only |
sortBy | string | Default | Default, PriceAsc, PriceDesc, SurfaceAsc, SurfaceDesc, DateAsc (Filter mode only) |
maxPages | number | (empty) | Max pages per search. Leave empty to walk every result page until pap.fr has no next page for that search. |
maxListings | number | 20 | Hard cap across the whole run. 0 = unlimited (still bounded by maxPages). |
fetchDetails | boolean | false | Visit each listing's detail page for full description + street address + reference + high-res photo gallery |
resumeFromRunId | string | (empty) | A previous run ID or dataset ID to continue a full-catalogue walk across separate runs. Listings already collected there (matched by id) are skipped, so this run only saves the new ones. |
incrementalMode | boolean | false | Turn on for recurring (e.g. daily/weekly) monitoring of the SAME search. Off by default — no existing user's behavior or bill changes until you opt in. |
stateKey | string | (empty) | Optional name for the monitoring campaign. Leave empty to auto-derive a key from every search input (mode, locations/URLs, listing/property type, rooms, sort, fetchDetails) — different searches then never mix state. |
emitUnchanged | boolean | false | Incremental mode only. Also return listings unchanged since the last run, marked UNCHANGED. Returns and bills extra rows you already have. |
emitExpired | boolean | false | Incremental mode only. Also return listings that disappeared since the last run, marked EXPIRED. Only produced after a run fully scans the tracked search (not when maxPages/maxListings capped it, a block stopped it early, or resumeFromRunId was used). Returns and bills extra synthetic rows. |
proxy | object | Apify RES + FR | Proxy config, French residential recommended |
Resuming a full-catalogue walk
For an unbounded pull (maxPages empty, maxListings: 0), two things protect a long run:
resumeFromRunIdlets you continue in a brand-new run: paste the ID of a previous run (or its dataset), and this run skips every listingidalready saved there, appending only new listings.- A checkpoint is saved automatically as the run progresses. If the run is interrupted by an Apify platform migration or you click Resurrect on a failed run, it picks back up from where it left off in the same run, without re-saving (or re-charging for) listings it already collected. No input is needed for this; it's automatic.
Recurring / incremental monitoring
resumeFromRunId continues one specific interrupted run. For running the same search again and again (e.g. daily/weekly) and getting only what changed, use incrementalMode instead:
- Turn on
incrementalMode. The first run returns everything asNEW. Every later run against the same search (samestateKey, or the same auto-derived scope) classifies each listing asNEW,UPDATED,UNCHANGED,REAPPEARED, orEXPIREDand — by default — only returnsNEW/UPDATED/REAPPEAREDrows, so you're billed only for what actually changed. - Set
stateKeyto name the campaign explicitly (or to deliberately share state across two differently-configured runs). Leave it empty to let the actor derive one automatically from the search itself. emitUnchangedandemitExpiredare both off by default because turning them on bills extra rows — a full re-snapshot (emitUnchanged) or synthetic delisting rows (emitExpired) on top of the real changes.emitExpiredonly fires after a run proves it scanned the entire tracked search with nothing capping it short (nomaxPages/maxListingsceiling reached, no block, noresumeFromRunId). Otherwise the run can't tell "this listing is gone" from "I just didn't get to its page" — the log line says why EXPIRED detection was skipped that run.- Incremental state is kept in Apify's key-value store, separate from
resumeFromRunId's checkpoint, and layered over the same listing-id dedup the checkpoint already uses — the two features don't duplicate any tracking.
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 record — not the full JSON. Each item becomes one entry with a title (the listing's name / address) and its key fields flattened to plain text. Nested objects are collapsed to their main value and long lists are trimmed. 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 haven't 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.
Output Example
{"id": "<listing-id>","url": "<listing-url>","listingType": "Buy","propertyType": "Apartment","title": "<listing-title>","priceValue": 590000,"priceCurrency": "EUR","priceDisplay": "590.000 €","rooms": 3,"livingSpace": 57,"description": "<full seller description>","streetAddress": "<street, postcode city>","city": "<city>","postcode": "<zip>","arrondissement": "<arrondissement>","department": "<department>","region": "<region>","latitude": 48.84,"longitude": 2.29,"energyClass": "D","ghgClass": "D","isActive": true,"reference": "<PAP reference>","imageCount": 18,"images": ["<image-url-1>", "<image-url-2>"],"additionalProperties": {"Type de bien": "Appartement","Ascenseur": "Oui"},"scrapedAt": "2026-04-22T10:00:00.000Z"}
With incrementalMode on, each row also carries:
{"changeType": "UPDATED","changedFields": ["priceValue", "priceDisplay"],"firstSeenAt": "2026-04-01T09:00:00.000Z","lastSeenAt": "2026-04-22T10:00:00.000Z"}
These four fields are absent entirely when incrementalMode is off — the default output shape is unchanged.
Plan requirement
This actor needs a residential proxy. Two options:
- Paid Apify plan (Starter and above). Keep the default. EU residential is preferred; FR may have issues, so the default pins IT.
- Bring your own proxy (any plan, including Free). Uncheck "Use Apify proxy" and paste your own residential URLs (Bright Data, Oxylabs, SmartProxy, IPRoyal, etc.).
If the primary proxy is unavailable, the scraper auto-falls back to a managed backup pool.
Common Department Codes
| Code | Name | Biggest city |
|---|---|---|
| 75 | Paris | Paris |
| 69 | Rhône | Lyon |
| 13 | Bouches-du-Rhône | Marseille |
| 33 | Gironde | Bordeaux |
| 06 | Alpes-Maritimes | Nice |
| 31 | Haute-Garonne | Toulouse |
| 59 | Nord | Lille |
| 44 | Loire-Atlantique | Nantes |
| 67 | Bas-Rhin | Strasbourg |
| 34 | Hérault | Montpellier |
| 35 | Ille-et-Vilaine | Rennes |
| 38 | Isère | Grenoble |
About
Built and maintained for reliable, cost-effective scraping of PAP.fr. Report any issues or ideas in the Apify Store listing.