Sign inTry it

Find local businesses by trade and city.

Finds businesses in a German city on Gelbe Seiten, the German business directory: name, trade, address, distance from the city centre, phone number, rating and number of reviews, whether it is open now with the opening text, and a link to the entry. Up to 50 results, in Gelbe Seiten's order. The trade must be one Gelbe Seiten knows.

No reports yetRead-only, no login
Site
Gelbe Seiten, Germany
Version
v1
Runs, last 7 days
No reports yet
sha256
152e78788f50…

Run it with your agent

after installing Wield

Ask in plain words. Your agent finds this recipe and calls it:

“Find local businesses by trade and city.”

Read-only: it changes nothing on the site. No login needed.

{
  "tool": "run",
  "arguments": {
    "recipe_id": "gelbeseiten.de/search-businesses",
    "params": {
      "what": "zahnarzt",
      "where": "berlin"
    }
  }
}

Requests

sent from your browser, in order
  1. 1GET/branchen/{{params.what | trim | lower | urlencode}}/{{params.where | trim | lower | urlencode}}Gelbe Seiten's list page for a trade in a city (/branchen/<trade>/<city>, the page its sitemaps list), server-rendered with the first 50 entries.

Parameters

from your agent
limitenum 5 | 10 | 20 | 50 = 20
whatstringrequiredTrade or business type in German as Gelbe Seiten names it, e.g. 'zahnarzt', 'elektriker', 'schlüsseldienst', 'friseur', 'pizza lieferservice'. A term it does not know sends the page to its full-text search, which this recipe does not use (the run then fails with a redirect).
wherestringrequiredGerman city, e.g. 'berlin', 'köln', 'frankfurt am main'

Returns

to your agent
businessesobject[]address, distance_km, name, open_now, opening, phone, rating, reviews, trade, url
totalinteger
whatstring
wherestring

Where it can send

  • www.gelbeseiten.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`
  • output.total > `500`
  • length(output.businesses[?starts_with(url, 'https://www.gelbeseiten.de/gsbiz/')]) == length(output.businesses)
  • length(output.businesses[?phone != `null`]) > `15`
  • length(output.businesses[?contains(address, 'Berlin')]) == length(output.businesses)
  • length(output.businesses[?rating != `null` && (rating > `5` || rating < `0`)]) == `0`

Raw recipe

Served as canonical JSON from /api/v1/recipes/gelbeseiten.de/search-businesses, so its sha256 matches the ETag and the index.

Check it yourself
curl -s https://wield.run/api/v1/recipes/gelbeseiten.de/search-businesses | shasum -a 256
# 152e78788f5075e94ae79090ac1ecc1058cca649c4ec25a76095816439b3795c
gelbeseiten.de/search-businesses.json, v1
{
  "description": "Finds businesses in a German city on Gelbe Seiten, the German business directory: name, trade, address, distance from the city centre, phone number, rating and number of reviews, whether it is open now with the opening text, and a link to the entry. Up to 50 results, in Gelbe Seiten's order. The trade must be one Gelbe Seiten knows.",
  "effect": "read",
  "id": "gelbeseiten.de/search-businesses",
  "keywords": [
    "gelbe seiten",
    "branchenbuch",
    "firmen",
    "handwerker",
    "zahnarzt",
    "arzt",
    "elektriker",
    "schlüsseldienst",
    "friseur",
    "öffnungszeiten",
    "telefonnummer",
    "local business",
    "directory",
    "yellow pages",
    "opening hours",
    "near me"
  ],
  "output": {
    "schema": {
      "properties": {
        "businesses": {
          "items": {
            "properties": {
              "address": {
                "description": "Street, postcode, city (district)",
                "type": [
                  "string",
                  "null"
                ]
              },
              "distance_km": {
                "description": "From the city centre",
                "type": [
                  "number",
                  "null"
                ]
              },
              "name": {
                "type": "string"
              },
              "open_now": {
                "description": "null when the entry has no opening hours",
                "type": [
                  "boolean",
                  "null"
                ]
              },
              "opening": {
                "description": "As Gelbe Seiten shows it, e.g. 'Geschlossen – Öffnet Montag um 12:00'",
                "type": [
                  "string",
                  "null"
                ]
              },
              "phone": {
                "description": "The business's public number",
                "type": [
                  "string",
                  "null"
                ]
              },
              "rating": {
                "description": "0–5, null without reviews",
                "type": [
                  "number",
                  "null"
                ]
              },
              "reviews": {
                "type": "integer"
              },
              "trade": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "url": {
                "type": "string"
              }
            },
            "type": "object"
          },
          "type": "array"
        },
        "total": {
          "type": [
            "integer",
            "null"
          ]
        },
        "what": {
          "type": "string"
        },
        "where": {
          "type": "string"
        }
      },
      "required": [
        "what",
        "where",
        "businesses"
      ],
      "type": "object"
    },
    "value": "let $s = steps.search, $l = params.limit || `20` in {what: $s.what_label || params.what, where: $s.where_label || params.where, total: ($s.total && to_number(replace($s.total, '.', ''))) || `null`, businesses: map(&{name: name, trade: trade || `null`, address: (address && distance && trim(replace(address, distance, ''))) || address || `null`, distance_km: (distance && to_number(replace(replace(distance, ' km', ''), ',', '.'))) || `null`, phone: phone || `null`, rating: (rating && to_number(replace(rating, ',', '.'))) || `null`, reviews: (reviews && to_number(split(reviews, ' ')[0])) || `0`, open_now: state_class && !contains(state_class, 'geschlossen'), opening: opening || `null`, url: href}, ($l == `50` && $s.entries) || ($l == `10` && $s.entries[:10]) || ($l == `5` && $s.entries[:5]) || $s.entries[:20])}"
  },
  "params": {
    "additionalProperties": false,
    "properties": {
      "limit": {
        "default": 20,
        "enum": [
          5,
          10,
          20,
          50
        ],
        "type": "integer"
      },
      "what": {
        "description": "Trade or business type in German as Gelbe Seiten names it, e.g. 'zahnarzt', 'elektriker', 'schlüsseldienst', 'friseur', 'pizza lieferservice'. A term it does not know sends the page to its full-text search, which this recipe does not use (the run then fails with a redirect).",
        "maxLength": 60,
        "minLength": 2,
        "type": "string"
      },
      "where": {
        "description": "German city, e.g. 'berlin', 'köln', 'frankfurt am main'",
        "maxLength": 60,
        "minLength": 2,
        "type": "string"
      }
    },
    "required": [
      "what",
      "where"
    ],
    "type": "object"
  },
  "provenance": {
    "method": "browser-inspect",
    "notes": "Gelbe Seiten's search form leads to /suche/<trade>/<city>, which robots.txt disallows; the list page /branchen/<trade>/<city> (in its sitemaps, the breadcrumb target of the search page) shows the same server-rendered entries (checked: identical first 50 for zahnarzt/berlin, 1342 hits). Umlauts and spaces work URL-encoded (schlüsseldienst/frankfurt am main, elektriker/köln, pizza lieferservice/berlin). A trade the directory does not know answers 301 to /suche/<term>/<city>; the executor does not follow redirects, so such a run fails instead of returning an empty list. Each entry is article.mod-Treffer: h2 name, link to /gsbiz/<id>, best trade, rating number and count, address with distance from the search centre, an opening-state span whose class ends in --geschlossen when closed, and the phone number. Websites are base64-encoded click targets and are left out. limit is 5, 10, 20 or 50: the first entries in Gelbe Seiten's order (JMESPath slices need fixed bounds). No paid placements were marked in the list.",
    "recorded_at": "2026-11-07T21:47:00+01:00",
    "recorded_by": "C4"
  },
  "requires_login": false,
  "schema": "https://wield.run/schema/recipe/v1.json",
  "site": {
    "country": "DE",
    "id": "gelbeseiten.de",
    "language": "de",
    "name": "Gelbe Seiten",
    "origin": "https://www.gelbeseiten.de",
    "origins": [
      "https://www.gelbeseiten.de"
    ]
  },
  "steps": [
    {
      "description": "Gelbe Seiten's list page for a trade in a city (/branchen/<trade>/<city>, the page its sitemaps list), server-rendered with the first 50 entries.",
      "expect": {
        "content_type": "html",
        "status": [
          200
        ]
      },
      "extract": {
        "html": {
          "total": {
            "selector": "#mod-TrefferlisteInfo"
          },
          "what_label": {
            "selector": "#wasBegriff"
          },
          "where_label": {
            "selector": "#woBegriff"
          }
        },
        "html_items": {
          "entries": {
            "fields": {
              "address": {
                "selector": ".mod-AdresseKompakt__adress-text"
              },
              "distance": {
                "selector": ".mod-AdresseKompakt__entfernung"
              },
              "href": {
                "attr": "href",
                "selector": "a[href*='/gsbiz/']"
              },
              "name": {
                "selector": "h2.mod-Treffer__name"
              },
              "opening": {
                "selector": ".oeffnungszeitKompakt__text"
              },
              "phone": {
                "selector": ".mod-TelefonnummerKompakt__phoneNumber"
              },
              "rating": {
                "selector": ".mod-BewertungKompakt__number"
              },
              "reviews": {
                "selector": ".mod-BewertungKompakt__text"
              },
              "state_class": {
                "attr": "class",
                "selector": "[class*='zustandsinfo--']"
              },
              "trade": {
                "selector": ".mod-Treffer--besteBranche"
              }
            },
            "max": 50,
            "selector": "article.mod-Treffer"
          }
        }
      },
      "id": "search",
      "request": {
        "headers": {
          "Accept": "text/html"
        },
        "method": "GET",
        "url": "/branchen/{{params.what | trim | lower | urlencode}}/{{params.where | trim | lower | urlencode}}"
      }
    }
  ],
  "task": "Find local businesses by trade and city",
  "tos": {
    "notes": "Uses the /branchen list pages robots.txt allows, not the disallowed /suche path. One request per call; no e-mail, chat or call actions. Business phone numbers and addresses are the public entries Gelbe Seiten publishes.",
    "reviewed": false
  },
  "verify": {
    "checks": [
      "length(output.businesses) == `20`",
      "output.total > `500`",
      "length(output.businesses[?starts_with(url, 'https://www.gelbeseiten.de/gsbiz/')]) == length(output.businesses)",
      "length(output.businesses[?phone != `null`]) > `15`",
      "length(output.businesses[?contains(address, 'Berlin')]) == length(output.businesses)",
      "length(output.businesses[?rating != `null` && (rating > `5` || rating < `0`)]) == `0`"
    ],
    "example_params": {
      "what": "zahnarzt",
      "where": "berlin"
    },
    "max_ms": 4000
  },
  "version": 1
}