{
  "openapi": "3.0.1",
  "info": {
    "title": "Pige Immo FR: 20 Portals, Address + Price Tracking",
    "description": "Scrape 20 French real estate portals in one run: Leboncoin, SeLoger, PAP, iad, Orpi, Century 21, Foncia, Capifrance, SAFTI and more. Resolve the exact address of anonymized listings, track price drops and days on market, flag under-priced sellers. For agents, pige and CRM prospecting. $5/1K.",
    "version": "1.0",
    "x-build-id": "Fu8x8uXeP1rcUuy0g"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/dltik~pige-immo-fr-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-dltik-pige-immo-fr-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/dltik~pige-immo-fr-scraper/runs": {
      "post": {
        "operationId": "runs-sync-dltik-pige-immo-fr-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/dltik~pige-immo-fr-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-dltik-pige-immo-fr-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "properties": {
          "sources": {
            "title": "Sources to scrape",
            "type": "array",
            "description": "Any of: pap, bienici (HTTP, no proxy); leboncoin, seloger, logic_immo (Residential proxy). Mandataire + agency networks (HTTP): iad, safti, bsk, capifrance, orpi, century21, laforet, guy_hoquet, foncia, nestenn, arthurimmo, human, square_habitat — plus proprietes_privees, stephane_plaza (need a FR Residential proxy). Most expose DPE + property GPS, so resolveAddress works on them.",
            "items": {
              "type": "string"
            },
            "default": [
              "pap",
              "bienici"
            ]
          },
          "startUrls": {
            "title": "Leboncoin start URLs (optional)",
            "type": "array",
            "description": "Ready-made Leboncoin search URLs you have already geo-scoped, e.g. https://www.leboncoin.fr/recherche?category=9&locations=Roubaix__50.69_3.18_10000 . When set, the Leboncoin source scrapes these URLs directly and the cities/postalCodes/departments filters below are IGNORED for Leboncoin (you scoped the URL yourself); 'leboncoin' is auto-added to sources. Use the /recherche path — the /c/ path does NOT geo-filter.",
            "default": [],
            "items": {
              "type": "object",
              "required": [
                "url"
              ],
              "properties": {
                "url": {
                  "type": "string",
                  "title": "URL of a web page",
                  "format": "uri"
                }
              }
            }
          },
          "transaction": {
            "title": "Transaction",
            "enum": [
              "buy",
              "rent"
            ],
            "type": "string",
            "description": "buy = vente, rent = location.",
            "default": "buy"
          },
          "cities": {
            "title": "Cities",
            "type": "array",
            "description": "City names. Example: ['Roubaix','Lyon']. Narrowest geo wins: if you also pass departments, results are trimmed to these cities (not the whole department).",
            "items": {
              "type": "string"
            },
            "default": []
          },
          "postalCodes": {
            "title": "Postal codes",
            "type": "array",
            "description": "5-digit postal codes. Example: ['59390','59100']. Narrowest geo wins: if you also pass departments, results are trimmed to exactly these postal codes.",
            "items": {
              "type": "string"
            },
            "default": []
          },
          "departments": {
            "title": "Department codes",
            "type": "array",
            "description": "2-digit department codes. Example: ['75','69','13']. Used alone = the whole department; combined with cities/postalCodes it only broadens the search that is then trimmed to those cities/postal codes.",
            "items": {
              "type": "string"
            },
            "default": []
          },
          "radiusKm": {
            "title": "Search radius around town (km) — Leboncoin",
            "minimum": 1,
            "maximum": 200,
            "type": "integer",
            "description": "Optional. With a single city or postal code, Leboncoin searches within this radius (km) around that town — ideal for 'a handful of communes around a town'. Leave empty to match the exact commune(s) / postal code(s) only."
          },
          "propertyTypes": {
            "title": "Property types",
            "type": "array",
            "description": "Subset of: appartement, maison, terrain, parking, local, loft, chateau.",
            "items": {
              "type": "string"
            },
            "default": [
              "appartement"
            ]
          },
          "priceMin": {
            "title": "Min price (€)",
            "minimum": 0,
            "type": "integer",
            "description": "Minimum listing price in euros."
          },
          "priceMax": {
            "title": "Max price (€)",
            "minimum": 0,
            "type": "integer",
            "description": "Maximum listing price in euros."
          },
          "surfaceMin": {
            "title": "Min surface (m²)",
            "minimum": 0,
            "type": "integer",
            "description": "Minimum living area in m²."
          },
          "surfaceMax": {
            "title": "Max surface (m²)",
            "minimum": 0,
            "type": "integer",
            "description": "Maximum living area in m²."
          },
          "roomsMin": {
            "title": "Min rooms",
            "minimum": 1,
            "type": "integer",
            "description": "Minimum number of rooms."
          },
          "roomsMax": {
            "title": "Max rooms",
            "minimum": 1,
            "type": "integer",
            "description": "Maximum number of rooms."
          },
          "bedroomsMin": {
            "title": "Min bedrooms",
            "minimum": 0,
            "type": "integer",
            "description": "Minimum number of bedrooms."
          },
          "onlyOwner": {
            "title": "Owner-only listings (no agency)",
            "type": "boolean",
            "description": "Restrict to particulier (PAP all owner; LBC private filter).",
            "default": false
          },
          "newOnly": {
            "title": "New builds only",
            "type": "boolean",
            "description": "Restrict to programmes neufs (new construction).",
            "default": false
          },
          "onlyWithPhotos": {
            "title": "Only listings with photos",
            "type": "boolean",
            "description": "Skip listings without photos.",
            "default": false
          },
          "publishedWithinHours": {
            "title": "Only listings published in the last N hours",
            "minimum": 1,
            "type": "integer",
            "description": "Keep only listings FIRST published within the last N hours (filters on the original publication date, not the re-index/bump date). Great for daily/2x-day monitoring and to avoid paying for republished agency listings. Leave empty for no time filter."
          },
          "fetchDescriptions": {
            "title": "Fetch full descriptions + photos (Leboncoin, SeLoger)",
            "type": "boolean",
            "description": "Fetch the full ad description text and photo URLs for Leboncoin and SeLoger listings. Both expose the full description only on the detail page, so this adds one extra request per listing (slower + more bandwidth) — enable it when you need the description (e.g. to infer the street/area with an LLM) or the photos. Bien'ici and iad already include description + photos without this. Off by default keeps those sources fast.",
            "default": false
          },
          "enrichDpe": {
            "title": "Enrich with ADEME DPE",
            "type": "boolean",
            "description": "When source doesn't return DPE, look up the official ADEME database by address+postal+surface match.",
            "default": true
          },
          "enrichDvf": {
            "title": "DVF price benchmark (find under-priced listings)",
            "type": "boolean",
            "description": "Benchmark each listing against real recorded sale prices (DVF open data) for the same commune and property type. Adds the local median €/m² (dvf_market_price_per_m2), how far the listing sits above/below it (dvf_price_vs_market_pct), and a deal flag (underpriced / market / overpriced). An owner listing priced below market is the hottest prospecting lead. Billed per listing benchmarked.",
            "default": false
          },
          "resolveAddress": {
            "title": "Resolve the exact / street address (premium)",
            "type": "boolean",
            "description": "Recover the anonymized property address. Combines a GPS reverse-geocode (official BAN) with a match against the ADEME energy-diagnosis registry (which carries the exact address) using the listing's surface, DPE/GES class and consumption. Adds resolved_address, resolved_street, address_confidence (0-1) and address_method (dpe-ademe / gps-ban / both). Exact number on a strong share of listings, street/building level on the rest — always with a confidence score. Billed per listing resolved.",
            "default": false
          },
          "dedupAcrossSources": {
            "title": "Dedup across sources",
            "type": "boolean",
            "description": "Same property listed on LBC + SeLoger? Merge into one record (keep most-complete).",
            "default": true
          },
          "incremental": {
            "title": "Incremental mode (only new / price-changed listings)",
            "type": "boolean",
            "description": "Daily-monitoring mode. The actor remembers every listing it returned in your previous runs (in its own persistent storage) and returns ONLY listings that are new since last run, or whose price changed. Listings that sat online unchanged are skipped and NOT charged — no need to feed anything back. Works on ALL sources (matches the stable listing_key, not the publication date), so it catches even PAP / re-bumped agency mandates that publishedWithinHours misses. Leave off for a full one-shot scrape.",
            "default": false
          },
          "detectPriceChanges": {
            "title": "Return price-changed listings (incremental mode)",
            "type": "boolean",
            "description": "In incremental mode, still return a previously-seen listing when its price moved since last run — flagged with price_changed=true and previous_price. Turn off to get strictly-new listings only. Ignored when incremental is off.",
            "default": true
          },
          "trackPriceHistory": {
            "title": "Track price history + motivated-seller signals (incremental)",
            "type": "boolean",
            "description": "In incremental mode, attach the full price timeline and derived signals to each returned listing: price_history (dated points), price_drop_pct (total % below the first-seen price), price_drop_count, days_on_market and days_since_last_change. These are the 'motivated seller' signals for real-estate prospecting. Ignored when incremental is off.",
            "default": true
          },
          "detectDelisted": {
            "title": "Detect delisted listings (sold / withdrawn)",
            "type": "boolean",
            "description": "In incremental mode, emit a record for each listing that was online in a previous run but has now disappeared from the portal — a strong sold/withdrawn signal. Each delisted record has status='delisted', last_price, days_on_market and last_seen_at. Reliable when you run the SAME search scope every run (the prospecting use case). Ignored when incremental is off.",
            "default": false
          },
          "minDaysOnMarket": {
            "title": "Min days on market (motivated sellers)",
            "minimum": 0,
            "maximum": 3650,
            "type": "integer",
            "description": "Keep only listings that have been on the market at least this many days (e.g. 90). This is the strongest motivated-seller signal — a long-unsold listing means the owner is more likely to sign a new mandate. Every record carries days_on_market and a long_on_market flag (true at >= 90 days). Requires a known publication date, so listings from portals that don't expose one are dropped when this filter is on. Automatically enables oldest-first fetching. 0 = no filter.",
            "default": 0
          },
          "oldestFirst": {
            "title": "Oldest first (longest on market)",
            "type": "boolean",
            "description": "Return the longest-on-market listings first instead of the newest. Sources that sort newest-first (e.g. iad) then walk their result pages from the end, so the +90-day listings you actually want become reachable instead of being buried on the last pages. Auto-enabled when minDaysOnMarket is set.",
            "default": false
          },
          "excludeIds": {
            "title": "Exclude already-seen listings (IDs / URLs)",
            "type": "array",
            "description": "Stateless alternative to incremental mode: paste the listing_key values (or URLs) you already retrieved in a previous run — matching listings are skipped and NOT charged. Every output record includes a listing_key field; collect them and feed them back here next run. Use this if you keep your own database instead of relying on the actor's built-in memory.",
            "items": {
              "type": "string"
            },
            "default": []
          },
          "maxResultsPerSource": {
            "title": "Max results per source",
            "minimum": 1,
            "maximum": 5000,
            "type": "integer",
            "description": "Each enabled source will return up to this many listings. Aliases accepted: 'limit', 'max', 'maxResults'. Each listing is charged at the per-event price — start small (e.g. 20) to validate output before scaling up.",
            "default": 20
          },
          "customProxyUrl": {
            "title": "Custom proxy URL (optional — overrides Apify proxy)",
            "type": "string",
            "description": "Optional external residential/mobile FR proxy URL (http://user:pass@host:port). Overrides the Apify proxy for ALL sources. Use this when Apify's shared RESIDENTIAL pool gets DataDome-blocked on Leboncoin — a dedicated/cleaner FR pool passes far more reliably. Sticky-session providers (a session-<token> in the credentials) are auto-rotated on a block. Example: http://user:pass_country-FR_session-abc_lifetime-1440@proxies.example.com:1000"
          },
          "proxyConfig": {
            "title": "Proxy (Residential required for LBC/SeLoger/Logic-immo)",
            "type": "object",
            "description": "Apify proxy. Default datacenter works for HTTP sources (PAP, Bien'ici). Switch to RESIDENTIAL with apifyProxyCountry='FR' for DataDome/PerimeterX bypass on LBC, SeLoger, Logic-immo. Ignored when customProxyUrl is set.",
            "default": {
              "useApifyProxy": true
            }
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}