Local.ch icon

Local.ch

From $1/1K. Pull structured company data from local.ch, including name, address, GPS, phone, email, website, hours, ratings, social links, photos, and…

Start using this actor on Apify ↗Need a custom scraper?Contact us ↗

Local.ch at a glance

Local.ch is a ready-to-run Apify actor from abotapi for collecting lead generation data. Its documented output includes profile id, name, subtitle, language and entry type, which you can export as JSON, CSV or Excel or read through the Apify API. It costs $2.00 per 1,000 results on the Apify Free plan and from $1.00 on paid plans, with smaller per-run or optional feature charges listed on Apify.

Runs on
Apify cloud: console, API, schedules and integrations
Data type
Lead Generation, Automation, Developer Tools
Pricing
$2.00 per 1,000 results (Free plan), from $1.00 on paid plans
Output
Documented below · JSON, CSV, Excel
Inputs
35 options

From $1/1K. Pull structured company data from local.ch, including name, address, GPS, phone, email, website, hours, ratings, social links, photos, and categories. Search by category and location or use URLs. Supports Deutsch, English, Français, and Italiano.

What Local.ch $1đź’° Listings can do

  • Two ways in, one filtered lane out. Search by category and location, or paste local.ch search/profile links. An optional review-only switch turns either path into one row per review comment instead of one row per company.
  • Four languages, one dataset shape. Pages and category slugs resolve automatically in DE, EN, FR and IT, so coiffeur in French and friseur in German return the same normalized fields.
  • Detail-page enrichment by default. Search-result cards alone only carry name, address and category, so the actor visits every profile page to add phone, email, GPS, opening hours, social links, attributes and photos.
  • Lead-gen filters that do not silently zero out. Keep only rated, premium-listed, or contactable businesses; the actor automatically turns on detail-page fetching whenever one of those filters needs data that only lives on the profile page.
  • Built for recurring monitoring. Incremental mode classifies each row against the previous run for the same search, URL, or review scope, and a search whose pages cannot be read is reported in the run log with a clear warning.
  • Full polymorphic contact data preserved. Every raw contact and attribute object from local.ch is kept alongside the flattened fields, so nothing upstream adds gets lost before the dataset catches up.

Choose what to collect

These inputs let you configure Local.ch $1đź’° Listings | URLs | Reviews Scraper. Open the actor on Apify to enter your values and review the current options.

Supported input fields
InputWhat it controls
modestring · requiredChoose where the actor should start. Search mode builds searches from category and location. URL mode uses pasted local.ch URLs.

Choices: search, url

reviewOnlybooleanWhen ON, output one row per review comment instead of profile listing rows. Works in both search and URL modes.
categorystringUsed in search mode. Examples: restaurant, coiffeur, hairdresser, dentist, garage, hotel.
queriesarrayUsed in search mode. Optional list of categories to search. When set, this overrides the single category field.
wherestringUsed in search mode. Examples: city slug (zurich), postal code (8001), canton code (zh), or switzerland.
locationsarraySearch mode only. Optional list of places (same forms as Location: city slug, postal code, canton code, or switzerland). When set, every category is searched in every place listed here, and the single Location field is not used. Leave empty to search one Location as before.
languagestringUsed for URLs and page content. local.ch supports German, English, French, and Italian.

Choices: de, en, fr, it

Show 28 more inputs
More input fields
InputWhat it controls
urlsarrayUsed in URL mode. Paste one or more local.ch URLs. • Search URL (any shape below): walks result pages • Profile URL: scrapes one profile page (//d////-, the short //d/, or a profile's own //entries//vcard contact-card link) Supported search shapes: //s//, //s/ (nationwide), //q// (local.ch's own SEO link form), and //q?what=&where=. A pasted sorting= and local.ch's own filter params (filter_facet=, filter_tags=, filter_open=) are honoured; other filter-like params (facet=, tags=, open=) are not applied by local.ch and are warned about in the log, never silently dropped. Category/city hub pages (/kategorien/..., /staedte/...) are not search results and are skipped with a warning, paste the /q/... link they link out to instead. With reviewOnly=true, the actor outputs review comments instead of listing rows. Leaving this field untouched (or omitting it in an API call) in URL mode scrapes a short default list of 12 distinct examples (one per genuinely different shape); maxListings is split evenly across them. The short /d/ profile link and the bare local.ch host are also pre-filled as editable examples, but they redirect to a page another default already covers, so they are not in the default list.
maxPagesintegerOptional bound on result pages walked per category/keyword/URL. Leave empty to walk every result page.
maxListingsintegerMaximum dataset rows to output. Listing rows when reviewOnly=false, review rows when reviewOnly=true.
fetchDetailsbooleanWhen ON, enrich listing rows with profile-page fields like phone, email, full address, coordinates, opening hours, social links, and attributes.
includeReviewsbooleanListing output only. Embed review comments (newest first, up to 'Maximum reviews per listing') inside each listing row. Ignored when reviewOnly=true.
maxReviewsPerListingintegerMaximum reviews per business (default 20, up to 200). Listing output: reviews embedded in each listing row when 'Include review comments' is ON; 0 = none. Review-only output: review rows per business; 0 = every review local.ch lists (the 'Maximum review rows' cap still applies). A profile page shows only its 3 newest reviews, so asking for more makes one extra small request per business that has more.
maxReviewRowsintegerReview-only output only. Default review-row cap when reviewOnly=true and maxListings=0.
maxConcurrencyintegerDeprecated and ignored. Detail-fetch concurrency is fixed internally at the reliable default; the input is kept only so saved task inputs keep validating.
ratedOnlybooleanListing output only. Keep only listing rows that have an average rating.
minRatingintegerListing output only. Keep only listing rows at or above this rating.
withPhonebooleanListing output only. Keep only listing rows with a phone number. This data only exists on the profile page, so turning this on also enables Fetch detail pages, regardless of that toggle.
withEmailbooleanListing output only. Keep only listing rows with an email address. This data only exists on the profile page, so turning this on also enables Fetch detail pages, regardless of that toggle.
withWebsitebooleanListing output only. Keep only listing rows with a website. This data only exists on the profile page, so turning this on also enables Fetch detail pages, regardless of that toggle.
premiumOnlybooleanListing output only. Keep only premium-listed profiles. This data only exists on the profile page, so turning this on also enables Fetch detail pages, regardless of that toggle.
skipRegionalFallbackbooleanSearch mode only. When a query has zero exact matches, local.ch silently falls back to a region-wide search and shows unrelated results (e.g. psychologie in 1346). When ON, the actor detects this fallback and emits no output for that query, no detail-page fetch, no rows. Default OFF preserves current behaviour.
sortstringThe order local.ch returns search results in (search mode, and pasted search URLs that don't set their own sorting=). relevance is local.ch's default order. Choosing alphanum (A-Z), rating (best rated first) or distance makes local.ch widen the result set slightly and include some nearby or loosely related businesses: measured for restaurant/Zurich on 2026-10-06, 2,123 results became 2,596. distance lists the businesses nearest to the point in 'Sort by distance from' first; the run stops with an error if that point is empty, because local.ch would otherwise return A-Z order.

Choices: relevance, alphanum, rating, distance

distanceFromstringOnly used with Sort order distance: the point to measure distance from, as latitude,longitude in decimal degrees, e.g. 47.3769,8.5417 (Zurich main station) or 46.9511,7.4386 (Bern). Right-click a spot in any map app to copy its coordinates. A pasted search URL's own coordinates= value takes precedence.
facetFiltersarrayThe same filters local.ch shows next to its search results, applied by local.ch itself. Type a plain name in any site language or English (italian, Glutenfrei, wifi, wheelchair_accessible_bathroom, open now) or the exact filter value (attr_cuisine:italian, attr_diets:glutenfree, entry:bookable). Filters from different groups must all match (e.g. Italian AND gluten-free); filters from the same group match any of them (e.g. Italian OR Swiss). Plain names are looked up on the search's first page, which costs one extra page request per search. If a name matches no filter local.ch offers for that search, that search is skipped and an error explains why, so you never get unfiltered results by mistake. Filter params in a pasted URL (filter_facet=, filter_tags=, filter_open=) are applied as well.
excludeRefusesAdvertisingbooleanListing output only. Drop businesses that have marked a phone, mobile or fax number with local.ch's 'no advertising' flag (the asterisk next to the number; output field refusesAdvertising). Businesses whose profile page could not be read are also dropped, because their flag is unknown. This flag only exists on the profile page, so turning this on also enables Fetch detail pages.
resumeFromRunIdstringOptional: paste a previous run ID (or dataset ID) from this actor. Rows already collected there are skipped, so this run only returns new ones. Use this after an interrupted run, or to continue a large pull in another run. For recurring monitoring of the same search, use Incremental mode below instead.
incrementalModebooleanTurn this on for daily or recurring monitoring of the same search/URL/review scope. The first run returns all matching rows as NEW. Later runs normally return only NEW, UPDATED, and REAPPEARED rows. Turn on "Emit unchanged" or "Emit expired" only when you also want those rows returned (and billed). State is kept separately per search/URL/filter/review setup; use State key when you want to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from run ID above.
stateKeystringOptional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. Leave empty to let the actor derive a key automatically from the search/URL/filter/review setup, different setups then never mix state with each other.
emitUnchangedbooleanOff by default. Turn on to also return rows that have not changed since the last run, marked UNCHANGED. This returns, and bills, extra rows you already have, so leave it off unless you specifically want the full snapshot every run.
emitExpiredbooleanOff by default. Turn on to also return rows that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search, not when Max pages/rows capped it or when Resume was used. This returns, and bills, extra synthetic rows, so leave it off unless you need expiry tracking.
ignoreFieldsForChangesarrayOutput field names that should NOT make a row count as UPDATED. categories/categorySlugs/mainCategory/mainCategorySlug are always ignored, because local.ch's search-result cards return a different (but equally valid) category set on repeated fetches of the same business, the same fields are stable once a profile page is fetched (Fetch detail pages ON, the default). openStatus/opensAt/closesAt (they describe the moment of the fetch) and lastModified (local.ch's own edit timestamp) are always ignored too. Add other field names here (e.g. description, attributes) if changes to them are not relevant to you. Ignored fields are still returned in the output.
mcpConnectorsarrayOptionally send the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify → Settings → Integrations, then select it here. The connector receives a condensed, human-readable summary per item (title + key fields), not the full JSON, the complete record stays in the dataset. Leave empty to skip. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).
notionParentPageUrlstringURL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.
maxNotifyListingsintegerCap on items written to each connector per run. Does not affect the dataset.

Fields in the output

The documented dataset includes the fields below. Availability can depend on the source page and selected mode.

idProfile ID (text)
nameName (text)
subtitleSubtitle (text)
languageLanguage (text)
entryTypeEntry Type (text)
schemaOrgTypeSchema.org Type (text)
isPremiumPremium (boolean)
favoritedFavorited (boolean)
professionalTitleProfessional Title (text)
phonePhone (text)
phoneAltAlt Phone (text)
mobileMobile (text)
Show 100 more fields
faxFax (text)
emailEmail (text)
emailAltAlt Email (text)
websiteWebsite (link)
whatsappWhatsApp (text)
facebookFacebook (link)
instagramInstagram (link)
linkedinLinkedIn (link)
twitterTwitter (link)
tiktokTikTok (link)
pinterestPinterest (link)
youtubeYouTube (link)
xingXING (link)
foursquareFoursquare (link)
phonesAll Phone Numbers (array)
emailsAll Emails (array)
socialMediaUrlsAll Social Media URLs (array)
refusesAdvertisingRefuses Advertising (boolean)
addressAddress (text)
streetStreet (text)
zipCodePostal Code (text)
cityCity (text)
cantonCanton (text)
countryCountry (text)
poBoxZipCodePO Box Postal Code (text)
poBoxCityPO Box City (text)
latitudeLatitude (number)
longitudeLongitude (number)
mainCategoryMain Category (text)
mainCategorySlugMain Category Slug (text)
categoriesCategories (array)
categorySlugsCategory Slugs (array)
ratingRating (number)
ratingCountRating Count (number)
reviewCountReview Count (number)
ratingCountsPerStarRating Counts Per Star (array)
reviewSummaryReview Summary (text)
reviewsFetchedReviews Fetched (number)
reviewsReviews (array)
openingHoursOpening Hours (text)
openingHoursStructuredOpening Hours (Structured) (array)
openingHoursByArrangementBy Arrangement (boolean)
openingHoursInfoLinesOpening Hours Notes (array)
openStatusOpen Status (at fetch time) (text)
opensAtOpens At (date)
closesAtCloses At (date)
descriptionDescription (text)
descriptionHtmlDescription (HTML) (text)
hasMachineTranslatedDescriptionDescription Machine-Translated (boolean)
descriptionTitleDescription Title (text)
descriptionLanguageDescription Language (text)
descriptionOriginalLanguageDescription Original Language (text)
attributesAttributes (array)
attributesByGroupAttributes by Group (object)
spokenLanguagesSpoken Languages (array)
paymentMethodsPayment Methods (array)
breadcrumbBreadcrumb (array)
uniqueSellingPointsHighlights (array)
servicesServices (array)
productslocal.ch Product Codes (array)
faqsFAQs (array)
menuCardUrlMenu (link)
bookingChannelsBooking Channels (array)
claimProfileUrlClaim Profile URL (link)
additionalLocationsAdditional Locations (array)
lastModifiedLast Modified on local.ch (date)
rateableCan Be Rated (boolean)
hasProductsHas local.ch Products (boolean)
providerDirectory Publisher (text)
logoUrlLogo URL (link)
imageUrlsImage URLs (array)
videoUrlsVideo URLs (array)
contactsRawContacts (Raw) (array)
additionalContactsRawAdditional Contacts (Raw) (array)
attributeGroupsRawAttribute Groups (Raw) (array)
detailUrlProfile URL (link)
searchQuerySearch Query (text)
searchWhereSearch Location (text)
scrapedAtScraped At (date)
reviewIdReview ID (text)
businessIdBusiness ID (text)
businessNameBusiness Name (text)
businessUrlBusiness URL (link)
businessCityBusiness City (text)
businessZipCodeBusiness Postal Code (text)
reviewTitleReview Title (text)
reviewTextReview Text (text)
reviewDateReview Date (date)
reviewAuthorReview Author (text)
reviewReplyReview Reply (text)
reviewRatingReview Rating (number)
changeTypeChange Type (incremental) (text)
changedFieldsChanged Fields (incremental) (array)
firstSeenAtFirst Seen (incremental) (date)
lastSeenAtLast Seen (incremental) (date)
titleReview Title (text)
textReview Text (text)
dateReview Date (date)
authorReview Author (text)
replyReview Reply (text)

Example output

An example from this actor’s documentation. Values are illustrative; this is not a fresh live result.

{
  "id": "aaaaaaaaaaaaaaaaaaaaaa",
  "name": "Sample Café Zürich",
  "subtitle": "Sample Café GmbH",
  "language": "de",
  "entryType": "COMPANY",
  "schemaOrgType": "Cafe",
  "isPremium": true,
  "favorited": false,
  "professionalTitle": null,
  "address": "Sample Street 1, 8001 ZĂĽrich",
  "street": "Sample Street 1",
  "zipCode": "8001",
  "city": "ZĂĽrich",
  "canton": "ZH",
  "country": "CH",
  "latitude": 47,
  "longitude": 8,
  "phone": "+41440000000",
  "phoneAlt": null,
  "mobile": "+41760000000",
  "fax": null,
  "email": "info@example.com",
  "emailAlt": null,
  "website": "https://example.com/",
  "whatsapp": "+41760000000",
  "facebook": "https://www.facebook.com/example",
  "instagram": "https://www.instagram.com/example",
  "linkedin": "https://www.linkedin.com/company/example",
  "twitter": null,
  "tiktok": null,
  "pinterest": null,
  "youtube": null,
  "xing": null,
  "foursquare": null,
  "mainCategory": "Café",
  "mainCategorySlug": "cafe",
  "categories": [
    "Café",
    "Tea room",
    "Catering"
  ],
  "categorySlugs": [
    "cafe",
    "tea-room",
    "catering"
  ],
  "rating": 4.5,
  "ratingCount": 120,
  "reviewCount": 60,
  "reviewsFetched": 0,
  "reviews": null,
  "openingHours": "Mon: 09:00-18:00; Tue: 09:00-18:00; Wed: closed; Thu: 09:00-18:00; Fri: 09:00-18:00; Sat: 10:00-17:00; Sun: closed",
  "openingHoursStructured": [
    {
      "day": "MONDAY",
      "status": "open",
      "byArrangement": false,
      "intervals": [
        {
          "type": "OPEN",
          "start": "09:00",
          "end": "18:00"
        }
      ]
    },
    {
      "day": "WEDNESDAY",
      "status": "closed",
      "byArrangement": false,
      "intervals": []
    }
  ],
  "openingHoursByArrangement": false,
  "description": "Sample description paragraph appears here when fetchDetails=true.",
  "descriptionHtml": "<p>Sample description paragraph appears here when fetchDetails=true.</p>",
  "attributes": [
    "Takeaway",
    "Delivery",
    "Vegetarian options"
  ],
  "spokenLanguages": [
    "Deutsch",
    "Englisch"
  ],
  "paymentMethods": [
    "Barzahlung",
    "TWINT",
    "Visa"
  ],
  "refusesAdvertising": false,
  "breadcrumb": [
    "Home",
    "Food",
    "Café"
  ],
  "logoUrl": "https://images.services.local.ch/aaaa/logo.jpg",
  "imageUrls": [
    "https://images.services.local.ch/aaaa/photo-1.jpg",
    "https://images.services.local.ch/aaaa/photo-2.jpg"
  ],
  "contactsRaw": [
    {
      "__typename": "MobileContact",
      "value": "+41760000000",
      "displayValue": "076 000 00 00",
      "whatsapp": true
    },
    {
      "__typename": "EmailContact",
      "value": "info@example.com",
      "displayValue": "info@example.com"
    },
    {
      "__typename": "CustomerProvidedURLContact",
      "value": "https://example.com/",
      "displayValue": "example.com"
    },
    {
      "__typename": "SocialMediaContact",
      "value": "https://www.facebook.com/example",
      "socialMediaProvider": "FACEBOOK"
    }
  ],
  "additionalContactsRaw": null,
  "attributeGroupsRaw": [
    {
      "groupName": "Services",
      "attributes": [
        {
          "id": "takeaway",
          "name": "Takeaway"
        }
      ]
    }
  ],
  "detailUrl": "https://www.local.ch/de/d/zurich/8001/cafe/sample-cafe-zurich-aaaaaaaaaaaaaaaaaaaaaa",
  "searchQuery": "cafe",
  "searchWhere": "zurich"
}

Ways to use this data

  • Lead generation and sales prospecting: pull phone, email and website for every business in a canton, filtered to premium-listed or contactable companies only.
  • Local SEO and directory audits: check how a business's local.ch profile (categories, hours, photos, attributes) compares with its listings elsewhere.
  • Market research and competitor mapping: sweep multiple categories across a canton or all of Switzerland to size a market.
  • Reputation monitoring: collect review comments for a business or category and track sentiment over time.
  • Recurring lead monitoring: schedule the actor with incremental mode to catch new, updated or removed businesses in a market segment.

Before you run

Start with a small input and check the resulting records against your expected fields. Source content and actor options can change; the current Apify listing is the reference for availability and pricing.

Questions about this actor

What data does Local.ch return?

The documented output for Local.ch includes profile id, name, subtitle, language, entry type, schema.org type, premium and favorited. Availability of each field depends on the source page and the selected mode.

How much does Local.ch cost?

It costs $2.00 per 1,000 results on the Apify Free plan and from $1.00 on paid plans, with smaller per-run or optional feature charges listed on Apify. The Apify listing shows the current rate for every plan.

Can I track only new or changed businesses on a schedule?

Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each row is then classified NEW, UPDATED, or UNCHANGED against the previous run for the same search, URL or review scope, and unchanged rows are suppressed and not billed unless you turn emitUnchanged on. EXPIRED (and the REAPPEARED classification that can follow it) only appear once a run has proven it scanned the whole tracked scope, so a capped or resumed run keeps the previous state untouched instead.

Why did my withPhone, withEmail, withWebsite or premiumOnly filter return nothing?

Search-result cards never carry phone, email, website or premium status; only the profile page does. If Fetch detail pages is off, the actor automatically turns it on whenever one of these filters is set, so the filter narrows real data instead of dropping every row. This is logged as a visible warning, not a silent change.

Why did a search return fewer rows than expected?

Two opt-in guards can legitimately shrink a run: Only listings matching the query postal code drops off-target cards before local.ch pads a sparse page with other regions, and Only listings from exact query matches skips a query entirely when local.ch itself silently falls back to a region-wide search. Both default off, so a normal run is unaffected unless you turn them on.

Why did my run return zero rows?

If local.ch cannot be reached for one search among several, or for a later page, the actor logs a warning for that search (for example "page 1: fetch failed, stopping") and moves on, so check the run log before concluding the search has no results. If no results page at all could be loaded, the run ends as failed with a status message explaining why (see Plan Requirement) rather than succeeding with zero rows. Run it again in a few minutes, or use Resume from run ID to continue an interrupted run without re-collecting or re-charging rows you already have.

Ready to collect with Local.ch?

Run it on Apify with your own input and export the results as JSON, CSV or Excel.