Find businesses and phone numbers by keyword and city.
Searches Das Örtliche, the German phone directory, for businesses by trade or name in a city or postcode: name, category, address, public phone number, whether it is open now with the opening text, and a link to the entry. Returns about 30 entries from the A–Z list (two result pages). The paid 'Premiumtreffer (Anzeigen)' entries are dropped by default; include_ads keeps them, marked sponsored.
- Site
- Das Örtliche, Germany
- Version
- v1
- Runs, last 7 days
- No reports yet
- sha256
- 85f62e29387f…
Run it with your agent
after installing WieldAsk in plain words. Your agent finds this recipe and calls it:
“Find businesses and phone numbers by keyword and city.”
Read-only: it changes nothing on the site. No login needed.
{ "tool": "run", "arguments": { "recipe_id": "dasoertliche.de/search-businesses", "params": { "city": "Berlin", "keyword": "Zahnarzt" } } }
Requests
sent from your browser, in order- 1GET/?ci&form_name&kw&recFromDas Örtliche's search (the homepage form search_nat: kw, ci), results 1–25 and 26–50 (recFrom), as the page's own paging links. Paid premium entries come first under 'Premiumtreffer (Anzeigen)' headings; the A–Z list follows the 'A - Z Trefferliste' heading.
Parameters
from your agentReturns
to your agentWhere it can send
- www.dasoertliche.de
Anything else is blocked by the extension.
Reads from your session
Nothing. Your browser attaches the site's cookies by itself.
Used only in requests to the addresses on the left. Never sent to wield.run or your agent.
Checks
a run passes when- length(output.businesses) >= `20`
- length(output.businesses[?sponsored]) == `0`
- length(output.businesses[?starts_with(url, 'https://www.dasoertliche.de/?form_name=detail')]) == length(output.businesses)
- length(output.businesses[?phone != `null`]) > `15`
- length(output.businesses[?contains(address || '', 'Berlin')]) == length(output.businesses)
Raw recipe
Served as canonical JSON from /api/v1/recipes/dasoertliche.de/search-businesses, so its sha256 matches the ETag and the index.
curl -s https://wield.run/api/v1/recipes/dasoertliche.de/search-businesses | shasum -a 256 # 85f62e29387f48a3a7b22cbc36cabf66e74b2fa47a314ea68fa6aaa7e09d96e7
{ "description": "Searches Das Örtliche, the German phone directory, for businesses by trade or name in a city or postcode: name, category, address, public phone number, whether it is open now with the opening text, and a link to the entry. Returns about 30 entries from the A–Z list (two result pages). The paid 'Premiumtreffer (Anzeigen)' entries are dropped by default; include_ads keeps them, marked sponsored.", "effect": "read", "id": "dasoertliche.de/search-businesses", "keywords": [ "das örtliche", "telefonbuch", "telefonnummer", "branchen", "firmen", "adresse", "öffnungszeiten", "zahnarzt", "handwerker", "phone book", "phone number", "local business", "directory", "opening hours" ], "output": { "schema": { "properties": { "businesses": { "items": { "properties": { "address": { "type": [ "string", "null" ] }, "category": { "type": [ "string", "null" ] }, "name": { "type": "string" }, "open_now": { "description": "null when the entry shows no opening hours", "type": [ "boolean", "null" ] }, "opening": { "description": "As Das Örtliche shows it, e.g. '24 h geöffnet', 'öffnet am Montag'", "type": [ "string", "null" ] }, "phone": { "description": "The business's public number", "type": [ "string", "null" ] }, "sponsored": { "description": "A paid premium entry (only with include_ads)", "type": "boolean" }, "url": { "type": "string" } }, "type": "object" }, "type": "array" }, "city": { "type": "string" }, "keyword": { "type": "string" } }, "required": [ "keyword", "city", "businesses" ], "type": "object" }, "value": "let $ads = params.include_ads == `true`, $org = steps.search[].organic[] || `[]`, $orgUrls = steps.search[].organic[].href || `[]` in {keyword: params.keyword, city: params.city, businesses: [($ads && map(&merge((let $tc = times_class || '' in {name: name, category: category || `null`, address: address || `null`, phone: (phone && trim(replace(phone, 'Tel.', ''))) || `null`, open_now: (`[[\"opnopen\", true], [\"opnclosed\", false]]`[?contains($tc, @[0])] | [0][1]), opening: times || `null`, url: href}), {sponsored: `true`}), (steps.search[0].all || `[]`)[?!contains($orgUrls, href)])) || `[]`, map(&merge((let $tc = times_class || '' in {name: name, category: category || `null`, address: address || `null`, phone: (phone && trim(replace(phone, 'Tel.', ''))) || `null`, open_now: (`[[\"opnopen\", true], [\"opnclosed\", false]]`[?contains($tc, @[0])] | [0][1]), opening: times || `null`, url: href}), {sponsored: `false`}), $org)][]}" }, "params": { "additionalProperties": false, "properties": { "city": { "description": "City or postcode, e.g. 'Berlin', 'Köln', '80331'", "maxLength": 60, "minLength": 2, "type": "string" }, "include_ads": { "default": false, "description": "Also return the paid premium entries (marked sponsored)", "type": "boolean" }, "keyword": { "description": "Trade or business name, e.g. 'Zahnarzt', 'Elektriker', 'Apotheke', 'Bäckerei Müller'", "maxLength": 60, "minLength": 2, "type": "string" } }, "required": [ "keyword", "city" ], "type": "object" }, "provenance": { "method": "browser-inspect", "notes": "The homepage search form (name search_nat: kw, ci, hidden form_name=search_nat) loads /?form_name=search_nat&kw=…&ci=…; its paging links add recFrom=26, 51, … (25 entries per page). Each entry is div.hit with a.hitlnk_name (name and detail link), .category, address, .phoneblock span ('Tel. …'), and .hitlnk_times whose class is opnopen or opnclosed. Page 1 starts with paid entries under div.headline02.filter headings 'Premiumtreffer (Anzeigen)' and 'Weitere Premiumtreffer (Anzeigen)'; the A–Z list follows div.headline02 'A - Z Trefferliste' (for Zahnarzt Berlin: 21 paid, 5 A–Z on page 1, 25 A–Z on page 2). The recipe keeps the entries after that heading from both pages; include_ads adds page 1's paid entries, marked. No hits, or a page past the last one, answers HTTP 410 with an HTML page without entries; the recipe accepts 410 as an empty page. Ratings are drawn by a script (showRatings) and are left out.", "recorded_at": "2026-11-07T21:57:00+01:00", "recorded_by": "C4" }, "requires_login": false, "schema": "https://wield.run/schema/recipe/v1.json", "site": { "country": "DE", "id": "dasoertliche.de", "language": "de", "name": "Das Örtliche", "origin": "https://www.dasoertliche.de", "origins": [ "https://www.dasoertliche.de" ] }, "steps": [ { "delay_ms": 300, "description": "Das Örtliche's search (the homepage form search_nat: kw, ci), results 1–25 and 26–50 (recFrom), as the page's own paging links. Paid premium entries come first under 'Premiumtreffer (Anzeigen)' headings; the A–Z list follows the 'A - Z Trefferliste' heading.", "expect": { "content_type": "html", "status": [ 200, 410 ] }, "extract": { "html_items": { "all": { "fields": { "address": { "selector": "address" }, "category": { "selector": ".category" }, "href": { "attr": "href", "selector": "a.hitlnk_name" }, "name": { "selector": "a.hitlnk_name" }, "phone": { "selector": ".phoneblock span" }, "times": { "selector": ".hitlnk_times" }, "times_class": { "attr": "class", "selector": ".hitlnk_times" } }, "max": 30, "selector": "div.hit" }, "organic": { "fields": { "address": { "selector": "address" }, "category": { "selector": ".category" }, "href": { "attr": "href", "selector": "a.hitlnk_name" }, "name": { "selector": "a.hitlnk_name" }, "phone": { "selector": ".phoneblock span" }, "times": { "selector": ".hitlnk_times" }, "times_class": { "attr": "class", "selector": ".hitlnk_times" } }, "max": 25, "selector": "div.headline02:not(.filter) ~ div.hit" } } }, "foreach": { "as": "from", "in": "[`1`, `26`]", "max": 2 }, "id": "search", "request": { "headers": { "Accept": "text/html" }, "method": "GET", "query": { "ci": "{{params.city}}", "form_name": "search_nat", "kw": "{{params.keyword}}", "recFrom": "{{from}}" }, "url": "/" } } ], "task": "Find businesses and phone numbers by keyword and city", "tos": { "notes": "robots.txt allows everything. Two requests per call (the first two result pages, 300 ms apart); no calls, e-mails or reverse lookups. Business phone numbers and addresses are the public entries the directory publishes.", "reviewed": false }, "verify": { "checks": [ "length(output.businesses) >= `20`", "length(output.businesses[?sponsored]) == `0`", "length(output.businesses[?starts_with(url, 'https://www.dasoertliche.de/?form_name=detail')]) == length(output.businesses)", "length(output.businesses[?phone != `null`]) > `15`", "length(output.businesses[?contains(address || '', 'Berlin')]) == length(output.businesses)" ], "example_params": { "city": "Berlin", "keyword": "Zahnarzt" }, "max_ms": 5000 }, "version": 1 }