# Dakraamplaza > Dutch online store (content in Dutch) for roof windows and accessories: VELUX, Roto, Ubbink, Skylux and Dakraamplaza-brand roof windows, flashings, blinds and roller shutters, flat-roof light domes and installation materials. Ships from own stock in the Netherlands. Prices in EUR, incl. VAT. For product discovery, use the JSON Search API below instead of scraping HTML pages. ## Product Search API `GET https://dakraamplaza.nl/?s=&format=json&page=` Returns machine-readable product search results. - Query: minimum 3 characters (shorter returns an empty result). Words may be separated by spaces or commas; a semicolon is not a separator. A comma between digits is a decimal separator, not a word break ("55,5" is one token). - Pagination: `per_page` is fixed at 24 and cannot be overridden, maximum 2 pages (up to 48 results). Refine the query instead of paging deeper. A page beyond the last returns HTTP 200 with `count: 0`. - Response fields: `query`, `total`, `capped` (true means `total` is the engine window, not the full catalog match count), `page`, `per_page`, `pages`, `count`, `currency`, `results[]`. - Each result: `id`, `type` ("variation" | "model"), `sku` (variation: real orderable SKU; model: null unless the model itself has an orderable SKU), `name`, `url` (canonical product link), `price`, `currency`, `availability` ("InStock" | "OutOfStock"). - Results with `type: "model"` additionally carry: `price_from` (lowest effective price across the model's active variations — sale price if set, otherwise regular), `variants_count`, `variants_url` (endpoint returning the full variation list, see below). - Caution on `name` for `type: "variation"` rows on THIS endpoint: about 7 variations in 10 have no descriptive title of their own, and those report their model's name instead. Two different sizes of one model can therefore come back under an identical `name`. Treat `sku` as the identifier, and use the Model Variations API below when you need a name that tells variations apart. - Empty result is HTTP 200 with `total: 0`. HTTP 503 `{"error": "search_unavailable"}` means the search engine is temporarily down — retry later. ## Model Variations API `GET https://dakraamplaza.nl/?s&format=variants&id=` Returns all orderable variations of one model (follow `variants_url` from a search result). - Parameters: `id` (required), `page`, `per_page` (default 24, max 100), `q`. - Envelope: `id`, `query`, `total`, `page`, `per_page`, `pages`, `count`, `currency`, `results[]`. - Each row: `id`, `type` ("variation"), `sku`, `name`, `url`, `price`, `currency`. - `q` is a case-insensitive substring filter on the SKU or on the variation's own stored title. It does NOT match the composed `name` this endpoint returns: where a variation has no title of its own, `name` is built from the model name plus its attribute summary, and `q` cannot see that text. Filtering by a `name` you just read back will usually return nothing — filter on `sku`. - Accent folding in `q`: accented letters fold to their base letter, so "ä" matches "a" and "u" matches "ü". "ß" matches only "ß" — neither "ss" nor "s" — so use the literal character. - Sorted by SKU ascending; pagination is stable. - HTTP 400 `missing_id` without `id`. HTTP 404 `not_found` for an unknown id — but only when `q` is absent; with `q` present an unknown id returns HTTP 200 with `total: 0`. - `name` is not unique. Across the whole catalogue about one variation in three carries a name that another variation of the same model also carries — concentrated in large models, where a run of hundreds can share a single generic name. Never use `name` as a key or assume two rows with different names are different products; `sku` is the only identifier. ## Rate limits and caching - Neither endpoint is cached. Both send `Cache-Control: no-store, no-cache, must-revalidate, max-age=0, private` and Cloudflare serves them as `DYNAMIC`, so every request reaches the origin. Cache responses on your side if you need to reuse them. - The search endpoint is rate limited to 30 requests per minute per client IP. Over the limit it returns **HTTP 429 with an HTML Cloudflare challenge page, not JSON, and no `Retry-After` header**. There is no header to tell you how long to wait: pause for a few minutes before retrying. Retrying hard does not shorten the block. - The Model Variations endpoint is not rate limited. - Verified crawlers (Googlebot, GPTBot, ClaudeBot, PerplexityBot and similar) are exempt from the search limit. The exemption is granted by Cloudflare bot verification, which checks the requesting IP — sending a crawler User-Agent from another address does not qualify. - Sustained abuse from one address can lead to a manual block. To stay well inside the limit: follow `variants_url` instead of guessing ids, page sequentially rather than in parallel, and keep your own cache of responses. ## Key pages - [Homepage](https://dakraamplaza.nl/) - [Sitemap](https://dakraamplaza.nl/sitemap_index.xml)