{
  "openapi": "3.0.1",
  "info": {
    "title": "Mentions Legales Scraper - French SIREN, RCS, TVA & Contacts",
    "description": "Turn French business domains into registry-grade B2B leads from each site's legally-mandated mentions legales page: SIREN, SIRET, RCS + greffe, TVA, capital social, APE, directeur de la publication, hebergeur, email, phone and address. No login, no API key.",
    "version": "0.1",
    "x-build-id": "rYJJsdnICN48NSTup"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/scrapersdelight~mentions-legales-contact-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-scrapersdelight-mentions-legales-contact-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/scrapersdelight~mentions-legales-contact-scraper/runs": {
      "post": {
        "operationId": "runs-sync-scrapersdelight-mentions-legales-contact-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/scrapersdelight~mentions-legales-contact-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-scrapersdelight-mentions-legales-contact-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": {
          "domains": {
            "title": "French domains (or mentions-légales URLs)",
            "type": "array",
            "description": "One entry per company. Accepts all three shapes: a bare domain (\"donatek.fr\"), a homepage URL (\"https://www.sapio-arts.fr\") or a direct mentions-légales URL (\"https://x.fr/mentions-legales\"). The Actor finds each site's legal-notice page and parses the statutory fields out of it — one lead per domain. Leave empty to run the built-in French demo batch.",
            "items": {
              "type": "string"
            }
          },
          "startUrls": {
            "title": "Start URLs (list source)",
            "type": "array",
            "description": "The same domain list handed over as URLs, so Make, Zapier, Clay or a Google Sheet can pass it natively (a link to a text/CSV file of URLs also works). Merged with \"French domains\".",
            "items": {
              "type": "object",
              "required": [
                "url"
              ],
              "properties": {
                "url": {
                  "type": "string",
                  "title": "URL of a web page",
                  "format": "uri"
                }
              }
            }
          },
          "sourceDatasetId": {
            "title": "Enrich an existing dataset",
            "type": "string",
            "description": "Dataset ID of a previous Actor run. Each item's domain/website column is read and enriched — the real agency workflow: run a directory or Maps scraper first, then pipe its output here."
          },
          "domainFieldName": {
            "title": "Domain field / CSV column",
            "type": "string",
            "description": "Which field of the source dataset (or which CSV column of the list file) holds the domain. Leave empty to auto-detect domain / website / websiteUrl / url / site / homepage. Dotted paths like \"company.website\" work."
          },
          "domainsFileUrl": {
            "title": "Domain list file URL",
            "type": "string",
            "description": "URL of a CSV, TXT, JSON or JSONL file holding the domains — for 100k-row lists that will not paste into the editor. The column is picked with \"Domain field / CSV column\"."
          },
          "skipDomains": {
            "title": "Suppression list",
            "type": "array",
            "description": "Domains to skip outright — accounts you already own, competitors, do-not-contact entries. Matched on the registrable domain, so \"www.x.fr/page\" and \"x.fr\" are the same entry.",
            "items": {
              "type": "string"
            }
          },
          "previousDatasetId": {
            "title": "Skip rows already delivered (dataset ID)",
            "type": "string",
            "description": "Dataset ID of an earlier run of THIS Actor. Its stableId / siren / domain values are loaded as a suppression list, so a monthly re-run never re-delivers — and never re-charges you for — a company you already bought."
          },
          "maxItems": {
            "title": "Max results (billed rows)",
            "minimum": 0,
            "type": "integer",
            "description": "Hard cap on rows DELIVERED AND BILLED this run. Distinct from \"Max domains\": roughly 3 of 4 reachable French sites publish a legal notice, so 1,000 domains yield ~740 billed rows. 0 = unlimited.",
            "default": 1000
          },
          "maxDomains": {
            "title": "Max domains attempted",
            "minimum": 0,
            "type": "integer",
            "description": "Cap on domains ATTEMPTED, applied before any request. Use it to sample a big list cheaply. 0 = attempt every domain supplied.",
            "default": 0
          },
          "maxDiscoveryRequestsPerDomain": {
            "title": "Max discovery requests per domain",
            "minimum": 1,
            "maximum": 40,
            "type": "integer",
            "description": "Hard cap on discovery + confirmation requests per domain, counted after the homepage. This is the knob that stops one slow host burning two minutes of a run. Anchor discovery alone costs 2 requests; the full cascade can reach 14. Recon measured the yield plateau at about 10.",
            "default": 12
          },
          "perDomainTimeoutSecs": {
            "title": "Per-domain timeout (seconds)",
            "minimum": 5,
            "maximum": 600,
            "type": "integer",
            "description": "Wall-clock deadline for one domain, discovery included. Measured latency on delivered rows: p50 5.6 s, p90 14.0 s, max 28.8 s — 60 s leaves headroom without letting a pathological host stall the pool.",
            "default": 60
          },
          "requestConcurrency": {
            "title": "Request concurrency",
            "minimum": 1,
            "maximum": 30,
            "type": "integer",
            "description": "How many domains are worked in parallel. Higher is faster; keep it modest to stay polite to small business sites.",
            "default": 10
          },
          "requestTimeoutSecs": {
            "title": "Request timeout (seconds)",
            "minimum": 5,
            "maximum": 120,
            "type": "integer",
            "description": "Timeout for a single HTTP request.",
            "default": 30
          },
          "maxRequestRetries": {
            "title": "Max request retries",
            "minimum": 0,
            "maximum": 6,
            "type": "integer",
            "description": "Retries per request, each on a FRESH proxy IP (got-scraping's own retry reuses the flagged IP, which is useless against a soft block).",
            "default": 2
          },
          "discoveryChannels": {
            "title": "Discovery channels",
            "type": "array",
            "description": "Which channels may be used to locate the legal-notice page, tried in this order. Anchor ranking alone won 25 of 26 successful discoveries in recon, so turning the rest off is a real cost lever on a big list. wp-json is what finds WordPress sites whose footer carries no legal link at all.",
            "items": {
              "type": "string",
              "enum": [
                "anchor",
                "sitemap",
                "wpJson",
                "pathGuess"
              ],
              "enumTitles": [
                "Homepage footer anchors (ranked)",
                "XML sitemap",
                "WordPress REST page index",
                "Guessed paths"
              ]
            },
            "default": [
              "anchor",
              "sitemap",
              "wpJson",
              "pathGuess"
            ]
          },
          "followWwwAndRootVariants": {
            "title": "Try www and apex variants",
            "type": "boolean",
            "description": "If the homepage fails, retry the other host form (www.x.fr ↔ x.fr) before declaring the domain unreachable.",
            "default": true
          },
          "deepJsDiscovery": {
            "title": "Deep JS fallback (JS-only footers)",
            "type": "boolean",
            "description": "Last-resort channel for a footer that only exists after client-side render. No Chromium is launched — this build is deliberately browser-free, because a browser image would quadruple the compute cost of every run for a case recon measured as rare. Instead the Actor reads what a render would have read FROM: the page's inline JSON payloads and its external JavaScript bundles, searching them for a legal-notice URL. Costs up to 6 extra requests per domain and is never charged separately. (Renamed from useBrowserFallback, which read as if it launched a browser; the old key is still accepted.)",
            "default": false
          },
          "respectRobotsTxt": {
            "title": "Respect robots.txt",
            "type": "boolean",
            "description": "Fetch and honour each site's robots.txt before requesting anything. Off by default because it costs one extra request per domain and measured 0 of 10 French sites (SME and enterprise) disallow the legal-notice path — those pages exist to be indexed.",
            "default": false
          },
          "proxyConfiguration": {
            "title": "Proxy",
            "type": "object",
            "description": "Proxy settings. Apify DATACENTER is the default and is the measured winner: 98.4% usable transport on the French SME corpus, zero 403s, zero CAPTCHAs. Residential changed nothing there, so it is not the default.",
            "default": {
              "useApifyProxy": true
            }
          },
          "proxyCountry": {
            "title": "Proxy country",
            "enum": [
              "none",
              "FR",
              "BE",
              "CH",
              "LU",
              "MC",
              "CA"
            ],
            "type": "string",
            "description": "Pin the exit country for francophone sites outside France that geo-tailor their content. MEASURED: Apify datacenter proxies cannot be pinned to a country (they answer HTTP 407), so choosing a country here switches the run to RESIDENTIAL exit nodes in that country. Leave on None for the cheaper, faster datacenter path that the French SME corpus does not need a country for.",
            "default": "none"
          },
          "escalateToResidentialOnBlock": {
            "title": "Escalate to residential on a block",
            "type": "boolean",
            "description": "On a 403 / challenge (never on a dead host or a 404), retry the request once on RESIDENTIAL + country-FR. Measured: this is what recovers CDN-fronted French brands such as blablacar.fr. Small SME lists never trigger it.",
            "default": true
          },
          "customUserAgent": {
            "title": "Custom User-Agent",
            "type": "string",
            "description": "Override the browser User-Agent sent on every request. Leave empty for the built-in Chrome 124 fingerprint."
          },
          "extraHttpHeaders": {
            "title": "Extra HTTP headers",
            "type": "object",
            "description": "Additional request headers, merged over the defaults (e.g. a From: header identifying your crawler)."
          },
          "tldFilterMode": {
            "title": "TLD filter mode",
            "enum": [
              "none",
              "include",
              "exclude"
            ],
            "type": "string",
            "description": "Many French SMEs are not on .fr (.com, .paris, .bzh, .eu, .alsace). Choose whether the TLD list below includes or excludes.",
            "default": "none"
          },
          "tldFilter": {
            "title": "TLDs",
            "type": "array",
            "description": "The TLD list the mode above applies to, without the dot: fr, com, paris, bzh, eu.",
            "items": {
              "type": "string"
            }
          },
          "requireRegistryId": {
            "title": "Only rows with a registry ID",
            "type": "boolean",
            "description": "Deliver (and bill) only companies whose legal notice carries a SIREN, SIRET or TVA number, and whose SIREN passes the Luhn checksum. A page whose registry id fails the checksum is filtered out here, never billed. Measured: 83% of parsed pages carry at least one id.",
            "default": false
          },
          "requireContact": {
            "title": "Only rows with a contact",
            "type": "boolean",
            "description": "Deliver (and bill) only companies with an email or a phone number. Measured: 89% of parsed pages carry at least one.",
            "default": false
          },
          "minFieldsRequired": {
            "title": "Minimum populated fields",
            "minimum": 0,
            "maximum": 17,
            "type": "integer",
            "description": "Quality floor: a row must carry at least this many of the 17 value fields (company, legal form, SIREN, SIRET, TVA, RCS city + number, capital, APE, director, representative, host, address, postcode, city, email, phone) before it is delivered and billed. 0 = no floor.",
            "default": 0
          },
          "emailPolicy": {
            "title": "Email policy",
            "enum": [
              "all",
              "role-only",
              "exclude-role"
            ],
            "type": "string",
            "description": "Agencies split hard on whether contact@ / info@ counts as a lead. \"Role only\" keeps just those; \"Exclude role\" keeps only named mailboxes.",
            "default": "all"
          },
          "dedupeBy": {
            "title": "Deduplicate by",
            "enum": [
              "siren-then-domain",
              "domain",
              "siren",
              "none"
            ],
            "type": "string",
            "description": "Which key collapses duplicates BEFORE anything is pushed or charged. The default uses a Luhn-valid SIREN when the page carries one and the registrable domain otherwise, so two domains owned by the same company collapse to one billed row — and a repeated input line is dropped at queue time, before it costs a request. \"No deduplication\" genuinely means none: you get, and pay for, one row per input line.",
            "default": "siren-then-domain"
          },
          "validateSiren": {
            "title": "Luhn-check the SIREN",
            "type": "boolean",
            "description": "Verify every SIREN against the Luhn checksum and emit sirenValid. A failure is FLAGGED, never dropped — La Poste (356000000) is a legitimate national exception.",
            "default": true
          },
          "deriveSirenFromVat": {
            "title": "Derive the SIREN from the TVA number",
            "type": "boolean",
            "description": "A French TVA number is FR + a 2-character key + the 9-digit SIREN, so the TVA is an exact container for it. Keeping this on fixes a real cross-field bug: on one probe site an RCS regex pulled a DIFFERENT 9-digit window out of the TVA string and produced a wrong SIREN.",
            "default": true
          },
          "extractHostingProvider": {
            "title": "Extract the hébergeur",
            "type": "boolean",
            "description": "The site's host (OVH, o2switch, Wix, Shopify, Cloudflare…) plus its address. 87% fill — a technographic signal hosting resellers and agencies buy on its own.",
            "default": true
          },
          "extractDpo": {
            "title": "Extract the DPO contact",
            "type": "boolean",
            "description": "Délégué à la protection des données / data protection officer contact, for GDPR and KYB buyers.",
            "default": true
          },
          "extractSocials": {
            "title": "Extract social profiles",
            "type": "boolean",
            "description": "LinkedIn, Facebook, Instagram, X, YouTube and TikTok links present on the legal-notice page.",
            "default": true
          },
          "extractPolicyUrls": {
            "title": "Extract CGV & privacy-policy URLs",
            "type": "boolean",
            "description": "The site's terms (CGV/CGU) and privacy-policy URLs, linked from the legal notice.",
            "default": true
          },
          "includeRawText": {
            "title": "Include the cleaned legal-notice text",
            "type": "boolean",
            "description": "Attach the parsed page as plain text (capped at 40,000 characters) so you can audit a field or re-parse it with your own LLM.",
            "default": false
          },
          "includeRawHtml": {
            "title": "Include the raw HTML",
            "type": "boolean",
            "description": "Attach the legal-notice page's raw HTML (capped at 200,000 characters). Makes the dataset much larger.",
            "default": false
          },
          "includeMissRows": {
            "title": "Include unbilled miss rows",
            "type": "boolean",
            "description": "Emit a row for every domain that produced no lead — dead host, blocked, publishes no legal notice, filtered out, a duplicate, or suppressed by skipDomains / previousDatasetId — with its status and missReason, so you can do coverage accounting. These rows are NEVER charged.",
            "default": false
          },
          "flattenOutput": {
            "title": "Flat columns",
            "type": "boolean",
            "description": "On: one flat row, ready for Google Sheets, Clay or a CSV export. Off: fields grouped into registry {}, people {}, contact {}, hosting {} and policies {} objects.",
            "default": 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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}