Pap.fr 🇫🇷 [$1.5💰] French Private-Seller RE Scraper avatar

Pap.fr 🇫🇷 [$1.5💰] French Private-Seller RE Scraper

Pricing

from $1.50 / 1,000 results

Go to Apify Store
Pap.fr 🇫🇷 [$1.5💰] French Private-Seller RE Scraper

Pap.fr 🇫🇷 [$1.5💰] French Private-Seller RE Scraper

🔥 $1.5/1K · Pap.fr scraper for private French sellers — 30+ fields per row from rich JSON-LD: price · m² · rooms · DPE · lat-lng · seller · breadcrumb hierarchy (région/département/ville). Filter mode + URL mode + price-band split (lift the ~370-result cap). Apify Residential FR. No browser

Pricing

from $1.50 / 1,000 results

Rating

5.0

(1)

Developer

Muhamed Didovic

Muhamed Didovic

Maintained by Community

Actor stats

0

Bookmarked

19

Total users

18

Monthly active users

5 days ago

Last modified

Share

$1.5/1K ❤️ Pap.fr Scraper 🇫🇷 📞 Phones · Metro · DPE ⚡

How it works

How Pap.fr Scraper works

Pap.fr scraper for the French private-seller real-estate portal — no agencies, every listing is a direct buyer-to-seller deal. 30+ data points per property row, fetched through Pap.fr's iOS-app JSON API (api-ios.pap.fr) — same backend the official Pap mobile app uses, so the row shape is rich, structured, and stable. Paste any mix of the 2 URL kinds below and the actor auto-classifies each one:

InputRow(s) emitted
Listing URL — /annonce/{transaction}-{rest}-g{geoId}[-{filter-slug}] (e.g. /annonce/vente-appartements-paris-7e-g37774-studio-a-partir-de-1-chambres-jusqu-a-150000-euros). Filter slug supports studio, a-partir-de-N-{pieces|chambres|m2|euros}, jusqu-a-N-….N property rows — one API call returns up to 300 cards, then we fan out one detail-fetch per card.
Property detail URL — /annonces/{slug}-r{numericId} (e.g. /annonces/appartement-paris-15e-75015-r453301302)1 property row: price, m², rooms, bedrooms, phones (when seller revealed), DPE/GES, lat/lng, metro stations, full photo URLs.

Pap.fr is Particulier à Particulier — every seller is a private individual. The iOS API exposes phones natively when the seller has toggled "show phone" (typically 20–40 % of listings). When the URL has amenity filters (avec-piscine, ascenseur, balcon-terrasse, dernier-etage, …) the actor automatically enriches each candidate with the HTML page's additionalProperty[] list and post-filters the dataset so you only get rows that actually match.

Pure HTTP. No Puppeteer, no Playwright, no headless Chromium, no third-party Cloudflare-bypass service.

Why this is fast

The Pap.fr iOS app talks to api-ios.pap.fr with just five static headers and no auth — no Cloudflare challenge from clean residential IPs. One search call returns up to 300 property cards in one ~300 KB JSON response (vs. nine 100-KB HTML pages with overlap-dedup overhead in the old pipeline). Each detail row is ~2–3 KB of JSON instead of ~70 KB of HTML — roughly a 95 % bandwidth saving.

Fields you get per row

The iOS API surfaces fields that the HTML page doesn't expose structurally:

  • Property core — propertyId, sku, transactionType (vente/location), title, description (full HTML-stripped), headline (texte_accroche), propertyType, livingArea (m²), rooms (nb_pieces), bedrooms (nb_chambres_max)
  • Pricing — price (€ numeric), priceFormatted, currency, priceValid (prix_valide — is the listing's price still current?)
  • Location — city, postalCode (inferred from Paris arrondissement title when not explicit), country, latitude, longitude
  • Energy — dpeRating (A–G) + dpeDescription (full kWh/m² explanation), gesRating + gesDescription
  • Seller contact — phones[] (native telephones from the iOS API), hasEmail (contactable via Pap.fr email gateway), refusesDemarchage (seller opted out of cold marketing), appelVideo (["whatsapp", …] when seller offers video call), virtualTourUrl, sellerPersonalSite
  • Transport — transports[] with station names + line slugs (e.g. {label: "Poissonnière", lines: ["metro-7"]})
  • Media — imageUrls[] (full gallery, high-res -p1.jpg URLs), photosCount
  • Reference — reference_courte (e.g. F86/1075), listedDate (e.g. 21 mai 2026)
  • Raw — rawSource (the full iOS annonce object, so you don't lose any field we didn't promote to a top-level column)

When the URL has amenity tokens (handled via HTML enrichment), rows additionally carry: address (street-level), additionalProperties[] (Ascenseur, Dernier étage, Balcon, Terrasse, Parking, Piscine, …), breadcrumb[] (7-level geographic hierarchy), and the source: "ios-api+html" tag.


Pipeline

Pap.fr's iOS API is the primary fetcher. Pure HTTP via impit (Rust+rustls TLS fingerprint). Each iOS call rotates through Apify Residential FR sessions for IP diversity.

  1. Search call — GET /app/annonces?produit={vente|location}&geo[ids][]=N[&typesbien[]=…&prix[min]=…&prix[max]=…&nb_pieces[min]=…&nb_chambres[min]=…&surface[min]=…] returns up to 300 property cards in one call. Hard cap at 300; use splitByPrice to break past.
  2. Detail call — GET /app/annonces/detail?id={id} returns the rich JSON for one property. 2–3 KB per row.
  3. Filter resolution — when filter mode is used and a city isn't in our 30-entry cache (papPlaces.ts), we fall through to GET /app/gis?q={city} — Pap.fr's own geo autocomplete — so every French commune resolves dynamically.
  4. Amenity post-filter — when the URL contains amenity slugs (avec-piscine, ascenseur, …) that the iOS API silently ignores, the handler fetches the HTML detail page, parses JSON-LD additionalProperty[], and drops rows that don't satisfy ALL requested amenities. Adds 1 HTTP per candidate, only for amenity URLs.
  5. HTML escape hatch — set forceHtmlDetail: true in input to route every row through the HTML JSON-LD parser instead of iOS (slower but provides amenity-rich additionalProperties + streetAddress + breadcrumb on every row). The iOS API contract has been stable through every tested run, but this is your fallback if Pap.fr ever rotates it.

The parser uses JSON natively from the iOS API. No DOM scraping, no fragile selectors. When HTML enrichment is needed, it parses schema.org Product JSON-LD — also structured, not DOM.


Input

FieldTypeRequiredNotes
startUrlsstring[]noAny mix of listing / detail URLs. Auto-classified. Filter slugs (studio, a-partir-de-N-pieces, etc.) are parsed into iOS API params.
locationsstring[]when startUrls emptyCity names (Paris, Lyon, Annecy, …) or raw Pap.fr geo IDs (g439). Cities not in our cache resolve dynamically via /app/gis.
distributionTypeenumnoBuy (vente) / Rent (location). Default Buy.
estateTypesenum[]noApartment / House. Leave empty for both.
priceMin / priceMaxintno€ bounds → prix[min]/prix[max].
roomsMinintnoMinimum number of rooms (pièces) → nb_pieces[min].
splitByPricebooleannoWhen true, splits the search across 5 log-distributed price bands to multiply yield past the 300 cap. Default false.
priceBandsint 1–10noAdvanced override — exact band count. Implies splitByPrice.
forceHtmlDetailbooleannoEscape hatch — fetch every row via HTML JSON-LD instead of iOS API. Slower (~70 KB vs ~3 KB per row) but richer amenities/breadcrumb. Default false.
maxItemsintnoHard cap on rows. Default 1000. Free-tier users capped at 100.
maxConcurrencyintnoDefault 10.
proxyobjectnoDefault Apify Residential FR. The iOS API works fine from any IP region we tested.

Example input

{
"locations": ["Paris", "Lyon"],
"distributionType": "Buy",
"estateTypes": ["Apartment"],
"priceMax": 500000,
"splitByPrice": true,
"maxItems": 500
}

That builds 10 iOS search tasks (2 locations Ă— 5 price bands), each pulling up to 300 cards, with detail-fetches fanned out at maxConcurrency. Expected yield ~500 unique private-seller properties in one billable run.


Output schema

One row per property. Example with phones revealed:

{
"propertyId": "458601075",
"sku": "vente-458601075",
"transactionType":"vente",
"title": "Paris 9e",
"headline": "",
"description": "Rue de Maubeuge, dans un très bel immeuble pierre de taille…",
"propertyType": "Appartement",
"livingArea": 7,
"rooms": 1,
"bedrooms": 1,
"price": 66000,
"priceFormatted": "66.000 €",
"currency": "EUR",
"priceValid": false,
"city": "Paris 9e",
"postalCode": "75009",
"country": "FR",
"latitude": 48.878899,
"longitude": 2.346797,
"dpeRating": "G",
"dpeDescription": "Logement avec une consommation annuelle d'énergie supérieure ou égale à 421 kWh/m²/an.",
"gesRating": "D",
"gesDescription": "Logement avec une émission de gaz à effet de serre comprise entre 31 à 50 kgéqCO2/m²/an.",
"sellerName": "Particulier",
"sellerType": "Person",
"phones": ["06 09 40 56 96", "06 12 77 36 34"],
"hasEmail": true,
"refusesDemarchage": true,
"appelVideo": ["whatsapp"],
"virtualTourUrl": null,
"sellerPersonalSite": null,
"reference": "F86/1075",
"listedDate": "21 mai 2026",
"transports": [
{ "label": "Poissonnière", "lines": ["metro-7"] },
{ "label": "Cadet", "lines": ["metro-7"] },
{ "label": "Anvers", "lines": ["metro-2"] }
],
"imageUrls": ["https://cdn.pap.fr/photos/pap/f1/1e/…/f-p1.jpg", "…"],
"photosCount": 5,
"additionalProperties": [],
"breadcrumb": [],
"url": "https://www.pap.fr/annonces/-r458601075",
"rawSource": { "id": 458601075, "produit": "vente", "telephones": [...], "transports": [...], "...": "..." },
"source": "ios-api",
"scrapedAt": "2026-05-25T15:18:42.331Z"
}

When the input URL contains amenity tokens (handled via HTML enrichment), the row gains address, additionalProperties[], full breadcrumb[], and source: "ios-api+html".


FAQ

Q: Do you actually get phone numbers? Yes — when the seller has revealed their phone, the iOS API returns it natively in telephones[]. We pass that through as phones[]. About 20–40 % of Pap.fr listings have phones visible (it's seller-controlled — sellers who haven't toggled "show phone" simply don't appear with one, and there's no scrape path that bypasses the seller's choice).

Q: How does this compare to the SeLoger scraper?

  • SeLoger covers agency-listed properties; Pap.fr covers private-seller properties. Different inventory, often complementary.
  • SeLoger ships 75 fields per row (uses an undocumented React-app server-state blob). Pap.fr now ships 30+ structured fields including phones, station labels, DPE descriptions — directly from the iOS app's JSON API.
  • Pap.fr is faster and cheaper to scrape (no Cloudflare challenge on the iOS endpoint, JSON not HTML).

Q: Why does the actor stop after ~300 results per location? The iOS API hard-caps each search at 300 results. To break past, set splitByPrice: true (5 narrower searches Ă— 300 = ~1,500 properties per location). For per-location yield beyond that, combine multiple filters (priceMin, priceMax, roomsMin, estateTypes).

Q: I used avec-piscine in my URL but the count dropped a lot — why? The iOS API silently ignores criteres[] filters (amenities). When we detect amenity tokens in the URL, the handler fetches the HTML detail page for each candidate, parses the JSON-LD amenity flags, and drops rows that don't match. So your final dataset is strictly filtered — accuracy is preserved at the cost of one extra HTTP per candidate.

Q: Will this break if Pap.fr changes their iOS app contract? The five static headers we use are stable across Pap iOS app versions (currently 4.17.6). If Pap.fr ever signs requests or rotates the contract, set forceHtmlDetail: true in input — the actor falls back to HTML JSON-LD per row (slower, but resilient to API churn). Each individual iOS detail failure also auto-falls-back to HTML, so single-property flakes are handled silently.


⚠️ Disclaimer

This scraper is provided for lawful, ethical use. Pap.fr is operated by SE Loger / Aviv Group and the listings are owned by the individual sellers who posted them. Before scraping:

  1. Review Pap.fr's Terms of Service (https://www.pap.fr/info/mentions-legales) and your local laws (GDPR in EU, CCPA in California, etc.)
  2. Use the data responsibly — don't republish raw listings, don't mass-contact sellers, don't compete unfairly
  3. Rate-limit your runs and respect Pap.fr's infrastructure
  4. The seller's personal data (name, address, phone if exposed by the seller) is protected under GDPR — process it lawfully
  5. We are not affiliated with Pap.fr, SE Loger, or Aviv Group. We don't warrant that scraping Pap.fr is permitted under their Terms. By using this actor you accept full responsibility for compliance.

For paid commercial use of Pap.fr data, consider whether a direct data partnership with Pap.fr / SE Loger / Aviv is a better fit than scraping.


SEO Keywords

pap.fr scraper, pap fr scraper, particulier a particulier scraper, french real estate scraper, paris properties scraper, lyon properties scraper, marseille properties scraper, french private seller scraper, immobilier scraper, vente appartement scraper, vente maison scraper, location appartement scraper, french property listings, paris immobilier scraper, pap.fr api, pap.fr ios api, scrape pap.fr, pap.fr extract listings, pap.fr property data, pap.fr phone numbers scraper, telephones pap.fr scraper, french real estate api alternative, private seller real estate france, pap.fr json scraper, pap.fr listings json, pap.fr breadcrumb hierarchy, pap.fr geo id scraper, pap.fr filter scraper, pap.fr price band scraper, ile-de-france real estate scraper, hauts-de-seine real estate scraper, vente immobiliere scraper, pap scraper apify