{
  "openapi": "3.0.1",
  "info": {
    "title": "Belgian Ondernemingsnummer Scraper — KBO, BTW & Contacts",
    "description": "Turn Belgian company domains into KYB-grade B2B leads from each site's statutory legal notice (WER Boek XII): ondernemingsnummer checked against its mod-97 checksum, BTW number, RPR court, legal form, address, email and phone. Dutch, French and German pages. $3.50/1,000. No login.",
    "version": "0.1",
    "x-build-id": "8pFbi7FlezJqEgkJe"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/scrapersdelight~be-ondernemingsnummer-website-contact-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-scrapersdelight-be-ondernemingsnummer-website-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~be-ondernemingsnummer-website-contact-scraper/runs": {
      "post": {
        "operationId": "runs-sync-scrapersdelight-be-ondernemingsnummer-website-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~be-ondernemingsnummer-website-contact-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-scrapersdelight-be-ondernemingsnummer-website-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": "Belgian domains (or legal-notice URLs)",
            "type": "array",
            "description": "One entry per company. Accepts all three shapes: a bare domain (\"aktum.be\"), a homepage URL (\"https://www.drukta.be\") or a direct legal-notice URL (\"https://x.be/wettelijke-vermeldingen\"). ANY TLD works — 22% of the Belgian businesses measured trade on .com, .eu or .net, so a .be-only list loses about one lead in five. Leave empty to run the built-in Belgian 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 \"Belgian 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 lists too big to 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.be/page\" and \"x.be\" 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 / enterpriseNumber / 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\": about 54% of reachable Belgian sites publish a findable disclosure, so 1,000 domains yield roughly 480 billed rows. Raise it for a real list. 0 = unlimited.",
            "default": 100
          },
          "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. Ranked footer anchors alone cost 1-2 requests; the full cascade can reach 20. Measured yield plateau: about 8.",
            "default": 10
          },
          "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 7.2 s, p90 33.4 s, max 142.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": 12
          },
          "requestTimeoutSecs": {
            "title": "Request timeout (seconds)",
            "minimum": 5,
            "maximum": 120,
            "type": "integer",
            "description": "Timeout for a single HTTP request.",
            "default": 25
          },
          "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 locate the statutory disclosure, tried in this order. \"homepage\" is the Belgium-specific one: the identity block is often printed in the homepage footer itself, and it won 97 of 329 finds in recon at ZERO extra requests. Ranked anchors won 214. Turning off the guessed paths is a real cost lever on a big list.",
            "items": {
              "type": "string",
              "enum": [
                "anchor",
                "homepage",
                "sitemap",
                "wpJson",
                "pathGuess"
              ],
              "enumTitles": [
                "Ranked footer anchors (NL/FR/DE/EN)",
                "The homepage footer itself (free)",
                "XML sitemap",
                "WordPress REST page index",
                "Guessed paths"
              ]
            },
            "default": [
              "anchor",
              "homepage",
              "sitemap",
              "wpJson",
              "pathGuess"
            ]
          },
          "followWwwAndRootVariants": {
            "title": "Try www and apex variants",
            "type": "boolean",
            "description": "If the homepage fails, retry the other host form (www.x.be ↔ x.be) 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 multiply 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. Costs up to 6 extra requests per domain and is never charged separately.",
            "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 these pages exist to be indexed — a legal notice a search engine cannot read does not meet the WER's \"easily and directly accessible\" test.",
            "default": false
          },
          "proxyConfiguration": {
            "title": "Proxy",
            "type": "object",
            "description": "Proxy settings. Apify DATACENTER is the default and is the measured winner on the Belgian SME corpus: 613 of 734 domains reachable, 52 blocked, and residential recovered only 17 of the 51 it was tried on.",
            "default": {
              "useApifyProxy": true
            }
          },
          "proxyCountry": {
            "title": "Proxy country",
            "enum": [
              "none",
              "BE",
              "NL",
              "FR",
              "DE",
              "LU"
            ],
            "type": "string",
            "description": "Pin the exit country for sites that geo-tailor their content. MEASURED: Apify datacenter proxies cannot be pinned to a country (they answer HTTP 407), so choosing a country switches the run to RESIDENTIAL exit nodes in that country. Leave on None for the cheaper, faster datacenter path the Belgian 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, never on a 404), retry the request once on RESIDENTIAL + country-BE. Measured: tried on 51 domains, recovered 17.",
            "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": "Belgian companies commonly trade on .com and .eu, so there is no TLD filter by default. Turn one on only if your list mixes countries.",
            "default": "none"
          },
          "tldFilter": {
            "title": "TLDs",
            "type": "array",
            "description": "The TLD list the mode above applies to, without the dot: be, com, eu, brussels, vlaanderen, gent.",
            "items": {
              "type": "string"
            }
          },
          "languageFilter": {
            "title": "Only these disclosure languages",
            "type": "array",
            "description": "Deliver (and bill) only rows whose disclosure is written in one of these languages — for a campaign that only targets Flanders, Wallonia or the German-speaking Community. Measured split across 330 real pages: 53% nl, 34% fr, 9% de, 5% en. Leave empty for all.",
            "items": {
              "type": "string",
              "enum": [
                "nl",
                "fr",
                "de",
                "en"
              ],
              "enumTitles": [
                "Dutch",
                "French",
                "German",
                "English"
              ]
            },
            "default": []
          },
          "requireEnterpriseNumber": {
            "title": "Only rows with an ondernemingsnummer",
            "type": "boolean",
            "description": "Deliver (and bill) only companies whose disclosure carries an ondernemingsnummer / numéro d'entreprise. Measured: 80% of parsed pages do.",
            "default": false
          },
          "requireValidChecksum": {
            "title": "Only rows whose ondernemingsnummer passes mod-97",
            "type": "boolean",
            "description": "Stricter still: the number must also pass its mod-97 checksum, so what you buy can be looked up in the KBO/BCE. Measured: 262 of the 264 numbers found were valid; the 2 failures are published typos, flagged rather than dropped when this is off.",
            "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: 94% of parsed pages carry at least one.",
            "default": false
          },
          "minFieldsRequired": {
            "title": "Minimum populated fields",
            "minimum": 0,
            "maximum": 15,
            "type": "integer",
            "description": "Quality floor: a row must carry at least this many of the 15 value fields (company, legal form, enterprise number, VAT, RPR city + number, responsible publisher, director, address, postcode, city, email, phone, IBAN, host) before it is delivered and billed. Measured median is 8. 0 = no floor.",
            "default": 0
          },
          "emailPolicy": {
            "title": "Email policy",
            "enum": [
              "all",
              "role-only",
              "exclude-role"
            ],
            "type": "string",
            "description": "Agencies split hard on whether info@ / contact@ counts as a lead. \"Role only\" keeps just those; \"Exclude role\" keeps only named mailboxes.",
            "default": "all"
          },
          "dedupeBy": {
            "title": "Deduplicate by",
            "enum": [
              "enterprise-then-domain",
              "domain",
              "enterprise-number",
              "none"
            ],
            "type": "string",
            "description": "Which key collapses duplicates BEFORE anything is pushed or charged. The default uses a checksum-valid ondernemingsnummer 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": "enterprise-then-domain"
          },
          "validateChecksum": {
            "title": "Validate the ondernemingsnummer (mod-97)",
            "type": "boolean",
            "description": "Verify every ondernemingsnummer against its mod-97 checksum — the last two digits equal 97 − (first 8 digits mod 97) — and emit enterpriseNumberValid. A failure is FLAGGED, never dropped, because a published typo is a real thing a buyer needs to see.",
            "default": true
          },
          "extractHostingProvider": {
            "title": "Extract the hosting provider",
            "type": "boolean",
            "description": "The site's host (Combell, One.com, OVH, Wix, Shopify, Cloudflare…) plus its address — a technographic signal hosting resellers and agencies buy on its own. Also what keeps the host's OWN address and email out of the lead's fields.",
            "default": true
          },
          "extractDpo": {
            "title": "Extract the DPO contact",
            "type": "boolean",
            "description": "Functionaris voor gegevensbescherming / délégué à la protection des données / Datenschutzbeauftragter, 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 disclosure page.",
            "default": true
          },
          "extractPolicyUrls": {
            "title": "Extract terms & privacy-policy URLs",
            "type": "boolean",
            "description": "The site's algemene voorwaarden / conditions générales / AGB and its privacy-policy URL, linked from the disclosure.",
            "default": true
          },
          "includeRawText": {
            "title": "Include the cleaned disclosure 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 disclosure 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 disclosure, filtered out, a duplicate, or suppressed — 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
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "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
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}