Tutti.ch Scraper at a glance
Tutti.ch Scraper is a ready-to-run Apify actor from abotapi for collecting real estate data. Its documented output includes id, title, price, price (num) and cur, which you can export as JSON, CSV or Excel or read through the Apify API. It costs $1.80 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
- Real Estate, Developer Tools, Automation
- Pricing
- $1.80 per 1,000 results (Free plan), from $1.00 on paid plans
- Output
- Documented below · JSON, CSV, Excel
- Inputs
- 23 options
- Run success
- 95.1% of completed public runs in the last 30 days
Scrape listings from tutti.ch across all 23 categories, including vehicles, property, electronics, furniture, fashion, and jobs. Search by keyword, category, city, or use tutti.ch URLs. Returns price, location, canton, seller details, images, and optional GPS, phone, photos, and attributes.
What Tutti.ch Scraper can do
- Two ways to start. Search by keyword, category and city, or paste any tutti.ch search or category URL, multiple links in one run.
- 23 categories. Vehicles, property, electronics, furniture, fashion, jobs and more, plus a free-text keyword filter and min/max price filters in search mode.
- Numeric pricing. Every listing carries both the formatted price string and a parsed numeric value (priceValue), so you can filter and sort without parsing text yourself.
- Full location and seller data. City, postcode, canton code and canton name on every listing, plus seller name and private vs professional status.
- Optional detail enrichment. One setting adds GPS coordinates, every photo, the seller's account ID and member since date, phone when published, street address and structured attributes.
- Three site editions. German, French and Italian, matched automatically to a pasted URL or chosen explicitly in search mode.
Choose what to collect
These inputs let you configure Tutti.ch Scraper - Listings, Prices, Sellers & Details. Open the actor on Apify to enter your values and review the current options.
| Input | What it controls |
|---|---|
modestring · required | How to start the scrape. 'search' builds listing URLs from a keyword, category and locations. 'url' takes tutti.ch search or category URLs you paste. |
keywordstring | Free-text search term, e.g. 'velo', 'iphone', 'wohnung'. Leave empty to browse a whole category or the entire marketplace. Search mode only. |
categorystring | Restrict the search to one tutti.ch category. 'All categories' searches the entire marketplace. Search mode only. |
locationsarray | Swiss cities or cantons by name, e.g. 'Zürich', 'Genève', 'Bern', 'Basel', 'Lausanne', 'Luzern', 'Winterthur', or 'Kanton Wallis'. German, French, Italian and English spellings are accepted. A location that returns no listings is reported as an error in the log. Leave empty to search all of Switzerland. For precise or complex locations, use URL mode instead. Search mode only. |
minPriceinteger | Only keep listings priced at or above this amount (applied to the numeric price). Listings with no price are kept. Search mode only: results from a pasted URL in URL mode are not price filtered. |
maxPriceinteger | Only keep listings priced at or below this amount (applied to the numeric price). Listings with no price are kept. Search mode only: results from a pasted URL in URL mode are not price filtered. |
urlsarray | Paste tutti.ch links: search pages (/de/q/..., /fr/q/..., /it/q/..., or /q/... without a language) and single listing pages (/de/vi/...). Pagination (?page=N) and sorting (?sorting=) in a pasted search link are honoured, apex and mobile hosts are normalised, and listing links are scraped as one listing each. Seller pages (/seller?id=...) and category landing pages (/de/cat/...) are not supported and are skipped with a warning; paste a /q/ search link instead. The search-mode fields above (keyword, category, locations, min price, max price) are ignored here, and so is Site language, because a pasted URL carries its own. The Sorting field below does still apply: it sets the ordering used from the page after the pasted one onward. Leaving this field untouched in URL mode scrapes one example of each distinct supported link shape (language-less and apex/mobile links normalise to one of them). URL mode only. |
Show 16 more inputs
| Input | What it controls |
|---|---|
sortBystring | Result ordering. tutti.ch defaults to newest first. Applies in BOTH search mode and URL mode. In URL mode the page you pasted is fetched exactly as given, and every following result page is then requested with this ordering, so pick the value that matches your pasted URL if you want its own sorting kept. |
languagestring | Which tutti.ch language edition to scrape (affects URL path and localized slugs). Search mode only: in URL mode the language is read from the pasted URL and from the page itself, so this field is ignored there. |
fetchDetailsboolean | Adds GPS coordinates, ALL image URLs, seller account ID, member-since date, phone (when published), street address and structured properties. Costs one extra request per listing. |
maxPagesinteger | How many result pages to walk per search/URL (30 listings per page). Leave empty to walk every result page (tutti.ch itself stops serving results around page 100 = 3000 listings per search). A category search in search mode with a listing cap walks at least 40 pages regardless of this value, because the category filter runs client-side. |
maxAgeHoursinteger | Only keep listings posted within the last N hours. 0 or empty = no age limit. With sort = Newest first the walk stops as soon as a whole page is older than the limit. |
maxListingsinteger | Hard cap on total listings across all searches. 0 means no limit (still bounded by maxPages). Maximum 100000. |
residentialBudgetMbinteger | Approximate residential bandwidth budget for the run, in megabytes. Once the estimated residential transfer exceeds this, the actor downgrades to the cheaper backup pool. 0 = no budget limit. |
resumeFromRunIdstring | Paste a previous run ID or dataset ID to continue a full-catalogue walk across separate runs. Listings already collected there (matched by listing id) are skipped, so this run only saves the new ones. Leave empty for a normal fresh run. |
incrementalModeboolean | Turn this on for daily or recurring monitoring of the same search. The first run returns every matching listing as NEW. Later runs return only NEW, UPDATED and REAPPEARED listings; unchanged listings are neither returned nor billed. Every returned row gets changeType, changedFields, firstSeenAt and lastSeenAt. A listing counts as UPDATED when any field other than these ignored ones changed: scraped_at, timestamp, highlighted (the posted/renewed time and the promoted flag change without any content change). Everything else counts, for example price, title, description, images, seller, location, properties, so a changed price or description returns the listing as UPDATED with the field names in changedFields. Ignored fields are still returned in each row. Add your own with the ignore list below. State is kept per search (mode, keyword, category, locations, price range, language, detail setting, URLs), never per item limit, page limit or maximum listing age. Use State key to name a campaign. To continue one interrupted run instead, use Resume from a previous run above. |
stateKeystring | Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently configured runs. Leave empty to derive a key from the search settings, so different searches never mix state. |
emitUnchangedboolean | Off by default. Turn on to also return listings that have not changed since the last run, marked UNCHANGED. These rows are delivered and billed like any other row, so leave it off unless you want the full snapshot every run. |
emitExpiredboolean | Off by default. Turn on to also return listings seen in a previous run that are no longer found, marked EXPIRED. Only produced after a run that read the whole search to its last page: not when Max listings or Max pages stopped it, not for a pasted single listing or a link that starts after page 1, not with a maximum listing age, not with Resume, and not when a page or detail page failed. These rows are delivered and billed like any other row, and they count toward Max listings: any beyond it are reported on a later run. |
ignoreFieldsForChangesarray | Output field names that should NOT make a listing count as UPDATED, even when they changed. Always ignored: scraped_at, timestamp, highlighted (listed here so you know; you do not need to add them). A name without a dot matches that field at any nesting depth (for example text inside properties); a dotted name matches from the top of the row. Ignored fields are still returned in the output. |
mcpConnectorsarray | Optionally 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). |
notionParentPageUrlstring | URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors. |
maxNotifyListingsinteger | Cap 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.
listingIdIDtitleTitlepricePricepriceValuePrice (num)currencyCurcategoryIdcategoryIdcategoryLabelCategorylocationNameLocationpostcodePostcodecantonCantoncantonNamecantonNamesellerNameSellerShow 21 more fields
sellerTypeSeller typesellerAccountIdsellerAccountIdmemberSinceMember sincehighlightedPromotedphonePhonelatitudeLatlongitudeLngimageUrlImageimageCount# ImagestimestampPostedlanguagelanguageurlURLdescriptionDescriptionimagesImagespropertiesPropertiesstreetStreetexternalUrlExternal URLchangeTypeChangechangedFieldsChanged fieldsfirstSeenAtFirst seenlastSeenAtLast seenExample output
An example from this actor’s documentation. Values are illustrative; this is not a fresh live result.
{
"listingId": "00000001",
"title": "Sample listing title",
"description": "Full listing text appears here.",
"price": "499.-",
"priceValue": 499,
"currency": "CHF",
"categoryId": "bicycles",
"categoryLabel": null,
"parentCategoryId": null,
"parentCategoryLabel": null,
"locationName": "Sample City",
"postcode": "8000",
"canton": "ZH",
"cantonName": "Zürich",
"latitude": null,
"longitude": null,
"street": null,
"sellerName": "Sample Seller",
"sellerType": "private",
"sellerAccountId": null,
"sellerLogoUrl": "https://c.tutti.ch/img/0000000000.jpg",
"sellerLocation": null,
"sellerProfileUrl": null,
"sellerSubscriptionClass": null,
"sellerBadgeUrl": null,
"memberSince": null,
"phone": null,
"externalUrl": null,
"highlighted": false,
"formattedSource": null,
"imageUrl": "https://c.tutti.ch/big/0000000000.jpg",
"imageCount": 3,
"images": [
"https://c.tutti.ch/big/0000000000.jpg"
],
"properties": [],
"timestamp": "2026-01-01T00:00:00+02:00",
"language": "de",
"url": "https://www.tutti.ch/de/vi/zuerich/sport-outdoor/velos/sample/00000001"
}Ways to use this data
- Classifieds market research: track price trends and listing volume for a product category or city over time.
- Price monitoring: watch specific keywords or categories for new or repriced listings.
- Property and vehicle sourcing: pull real estate or vehicle listings with location and price filters to feed a search tool or alert.
- Lead generation: collect seller and listing details, with detail mode on, for outreach tools, subject to tutti.ch's own terms.
- Data science and analytics: build historical datasets of Swiss classifieds pricing and category activity.
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 Tutti.ch Scraper return?
The documented output for Tutti.ch Scraper includes id, title, price, price (num), cur, categoryid, category and location. Availability of each field depends on the source page and the selected mode.
How much does Tutti.ch Scraper cost?
It costs $1.80 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 get only new listings on a schedule?
Yes. Turn on incrementalMode and schedule the actor from the Schedules tab with the same search each time. The first run returns every listing as NEW; later runs return only NEW, UPDATED and REAPPEARED listings, each with changedFields. See "Recurring runs: incremental mode" above for what counts as a change and when EXPIRED is reported. To continue one interrupted large pull instead, use resumeFromRunId.
Why did my run fail instead of returning an empty dataset?
A run stops with a failed status for an invalid resumeFromRunId: an ID that doesn't match a run or dataset your Apify account can read fails immediately with a clear reason, instead of quietly re-scraping everything. If no results page can be loaded at all (see Plan Requirement), the run also fails with a clear reason; if some pages loaded first, you keep whatever was collected. Re-running usually helps, since a fresh run gets a new session, and in URL mode it's worth double-checking the pasted link is a valid tutti.ch search or category page.
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.
