# Dachfenstermarkt > German online store (content in German) for roof windows and accessories: VELUX, Roto, Ubbink and Skylux roof windows, flashings, blinds and roller shutters, flat-roof light domes and installation materials. Prices in EUR, incl. VAT. For product discovery, use the JSON Search API below instead of scraping HTML pages. ## Product Search API `GET https://dachfenstermarkt.de/?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://dachfenstermarkt.de/?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. On this site that is the common case — 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. - Neither endpoint is rate limited at present. Please still be considerate: page sequentially rather than in parallel, follow `variants_url` instead of guessing ids, and keep your own cache of responses. Limits may be introduced without notice. - Sustained abuse from one address can lead to a manual block. ## Key pages - [Homepage](https://dachfenstermarkt.de/) - [Sitemap](https://dachfenstermarkt.de/sitemap_index.xml)