# Nettiauto Scraper — Finland Cars, EV Battery & Dealers (`memo23/nettiauto-scraper`) Actor

Scrape nettiauto.com, Finland's largest car marketplace: every listing with VIN, plate, first registration date, battery kWh, electric range and state of health, the full photo gallery, the dealer's own details and the phone number hidden behind the call button. One flat row per car.

- **URL**: https://apify.com/memo23/nettiauto-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** E-commerce, Lead generation, AI
- **Stats:** 3 total users, 2 monthly users, 88.1% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.10 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Nettiauto Scraper — Finland Cars, EV Battery & Dealers

Turn any [Nettiauto](https://www.nettiauto.com) search into clean, structured data. Finland's largest car marketplace, one flat row per car: the full spec record and fitted equipment, battery capacity and range, VIN and registration plate, the dealer's own details with map coordinates, every photo at full size, and the phone number the page only shows as an image.

#### ✨ Why use this scraper

- **Reads the site's own data, not its layout** — each ad carries a typed analytics payload, a schema.org block and a spec table. All three are read, because none is complete on its own: the plate and the dealer ids live in the first, the gallery order in the second, and battery capacity, electric range and the first-registration date only in the third.
- **Battery figures as numbers** — capacity in kWh and electric range in km come through as values you can filter on, not prose. Where a seller publishes a state-of-health percentage, that is captured too, along with who measured it.
- **The dealer on every row** — name, seller type, street, postcode, city, the site's own dealer and parent-dealer ids, the dealer's website and their nettiauto page. No second table to join.
- **Coordinates for every car** — the ad has none in its HTML; its "Kartta" button asks Google Maps to place the dealer's address. The actor makes the same request, once per address per run, and returns latitude and longitude on the row.
- **Phone numbers without a login** — the page renders the number as an image, but the call button answers with a redirect that carries it in plain text. One cheap request per car, no image processing, nothing to solve.
- **Photos that do not expire** — older ads serve their gallery from a pre-signed archive that dies an hour later. Every URL is rewritten to the permanent host, so the links in your dataset still work next month.
- **Whole markets, not the first page** — results paginate without a ceiling, verified across pages 1 to 300 with no repeats. A full sweep needs no splitting and no tricks.
- **Says so when it is short** — every run compares the count the site reports with what it delivered and writes the verdict to the `COMPLETENESS` record and the run's status message. Past your threshold it ends as FAILED rather than passing a partial dataset off as a full one.
- **JSON, CSV, Excel or API** — billed per car.

#### 🎯 Use cases

| You are a… | Use it to… |
|---|---|
| EV analyst | Track used electric prices against battery capacity, range and declared state of health |
| Battery diagnostics | Find cars listed without any condition figure — the gap your test fills |
| Dealer / valuation | Monitor Finnish asking prices by make, model, year and mileage, with the full spec record |
| Marketplace / portal | Seed a Finnish vehicle dataset with photos, specs and dealer details |
| Lead generation | Reach dealers through the phone numbers and websites published on their ads |
| Importer / exporter | Compare Finnish prices against your own market, VIN by VIN |

#### 📥 Supported inputs

- **Search URLs** — `https://www.nettiauto.com/pikalinkit/sahkoautot?posted_by=dealer` (the quick links) or `https://www.nettiauto.com/vaihtoautot?…` (the filter form), with any filters you set on the site. A `priceFrom`/`priceTo` in the URL is applied by the actor, not sent to the site (see the FAQ).
- **Ad URLs** — `https://www.nettiauto.com/{make}/{model}/{id}`. Always fully detailed.
- **Dealer inventory pages** — `https://www.nettiauto.com/yritys/{id}` paginates like any other search.
- **Filter builder** — dealers only, electric only and a price range, when you leave the URL list empty.

#### ⚙️ How it works

![How the Nettiauto Scraper works](https://raw.githubusercontent.com/muhamed-didovic/muhamed-didovic.github.io/main/assets/how-it-works-nettiauto.png)

1. **Classify** every pasted URL into search or ad, or build a search from your filters.
2. **Open a session** — the site bounces the first request through a single sign-on refresh and will not serve anything until those cookies are set. The actor carries them for the whole run.
3. **Paginate** each search, 30 cars to a page, until the results genuinely end. The four "Näyteikkuna" showcase cards above each page are skipped: they are paid placements of cars that also appear in their own place in the results.
4. **Read** each car's own page from its three data sources, and follow the call button for the phone number when asked.
5. **Fill in the map coordinates** once per address, cached, rather than once per car, and the dealer's website from their profile page on the rare ad without one.
6. **Check** what the site reported against what was delivered, and fail the run if the gap is bigger than your threshold.
7. **Export** one row per car — JSON, CSV, Excel or API.

#### ⚙️ Input parameters

| Field | Type | Default | What it does |
|---|---|---|---|
| `startUrls` | array | `[]` | Nettiauto search or ad URLs, auto-classified |
| `dealersOnly` | boolean | `true` | Only cars posted by dealers |
| `electricOnly` | boolean | `true` | Restrict to electric cars |
| `priceFrom` / `priceTo` | integer | — | Price range in EUR, applied by the actor on a price-sorted search |
| `scrapeDetails` | boolean | `true` | Open each car's page for the full record |
| `scrapePhone` | boolean | `false` | Return the number behind the call button |
| `listOnly` | boolean | `false` | Emit id and URL only, opening no ad pages |
| `includeRawData` | boolean | `false` | Attach the ad's own `window.digitalData` and ld+json to every row as `raw` |
| `failOnIncomplete` | boolean | `true` | End the run as FAILED when the shortfall exceeds the threshold |
| `completenessTolerancePercent` | integer | `1` | Relative half of the threshold |
| `completenessToleranceAbsolute` | integer | `25` | Absolute floor of the threshold, in cars |
| `maxItems` | integer | `1000` | Hard cap on rows for the whole run |
| `maxItemsPerSearch` | integer | `1000` | Cap per search URL |
| `maxConcurrency` | integer | `8` | Parallel HTTP requests (1–20) |
| `maxRequestRetries` | integer | `6` | Retry budget on transient 403/429/5xx |
| `proxy` | object | none | Optional override; not needed |

#### 📊 Output overview

One row per car. Nested objects keep related values together: `price` carries the amount and currency, `battery` every EV figure the ad carries, `dealer` the whole seller block, `coordinates` the map point.

#### 📦 Output sample

```json
{
  "adId": "16025210",
  "url": "https://www.nettiauto.com/audi/e-tron/16025210",
  "title": "Audi e-tron 50 quattro 313 hv / SOH 94.3 % / Ilmajousitus / Metalliväri",
  "make": "Audi",
  "model": "e-tron",
  "variant": "50 quattro 313 hv / SOH 94.3 % / Ilmajousitus / Metalliväri / 21\" vanteet / 2 x vanteet / LED-valot",
  "bodyType": "Maastoauto suv",
  "modelYear": 2021,
  "price": { "amount": 27990, "currency": "EUR" },
  "priceType": null,
  "mileageKm": 67000,
  "firstRegistered": "2021-04-21",
  "firstRegisteredMonth": "2021-04",
  "conditionTags": [],
  "vehicleType": "Henkilöauto",
  "vin": "WAUZZZGE7MB029666",
  "registrationNumber": "LSN-922",
  "battery": {
    "capacityKwh": 71,
    "rangeKm": 341,
    "stateOfHealthPercent": 94.3,
    "stateOfHealthSource": "Aviloo",
    "chargingType": "CCS Type 2",
    "chargingPowerAcKw": 11,
    "chargingPowerDcKw": 150,
    "warranty": "96 kk / 160 000 km",
    "consumptionKwhPer100Km": 14.5
  },
  "powerKw": 230,
  "powerHp": 313,
  "fuel": "Sähkö",
  "transmission": "Automaatti",
  "drivetrain": "Neliveto",
  "colour": "Punainen",
  "seats": 5,
  "equipment": ["Ajonvakautusjärjestelmä", "Isofix-valmius", "LED-ajovalot", "Lukkiutumattomat jarrut (ABS)"],
  "dealer": {
    "id": "1801",
    "dealerId": "0013X00002al2d6QAA",
    "dealerParentId": null,
    "name": "Loimaan Laatuauto Oy",
    "street": "Lamminkatu 7",
    "postcode": "32200",
    "city": "Loimaa",
    "sellerType": "Dealer",
    "website": "http://www.loimaanlaatuauto.fi",
    "profileUrl": "https://www.nettiauto.com/yritys/1801"
  },
  "coordinates": { "latitude": 60.850512, "longitude": 23.0359916, "address": "Lamminkatu 7, 32200 Loimaa" },
  "location": { "city": "Loimaa", "region": "Varsinais-Suomi" },
  "phone": "+358 40 450 7001",
  "imageUrls": ["https://images.nettiauto.com/live/2026/10/02/383662fd633908cd-large.jpg"],
  "searchMatchCount": 9687,
  "detailScraped": true
}
```

#### 🗂 Key output fields

| Field | Description |
|---|---|
| `adId` / `url` / `title` | Nettiauto ad id, canonical URL, the seller's headline |
| `make` / `model` / `variant` / `bodyType` / `modelYear` | Vehicle identity. `variant` is the trim line printed under the title |
| `vehicleType` | `Auton tyyppi`, e.g. `Henkilöauto` (car) or `Pakettiauto` (van) |
| `price` | `{ amount, currency }` — asking price in EUR |
| `priceType` | The VAT label under the price: `Sis. ALV` (VAT included) or `ALV väh.kelp.` (VAT deductible); null when the ad prints the price bare |
| `mileageKm` | Odometer reading. `0` when the ad says `Ajamaton` (undriven) |
| `firstRegistered` | `Käyttöönottopäivä` as an ISO date; null when the ad omits it |
| `firstRegisteredMonth` | `YYYY-MM`, from the full date or, when that is missing, the `ensirek.` month the model-year row carries |
| `vin` / `registrationNumber` | Chassis number and plate, as published |
| `battery` | `{ capacityKwh, rangeKm, stateOfHealthPercent, stateOfHealthSource, chargingType, chargingPowerAcKw, chargingPowerDcKw, warranty, consumptionKwhPer100Km }`. `warranty` stays verbatim because ads state a distance, a term, or both (`96 kk / 1 600 000 km`) |
| `powerKw` / `powerHp` | Both figures the site prints, split apart |
| `fuel` / `transmission` / `drivetrain` / `colour` / `seats` | Running gear and body |
| `conditionTags` | Condition markers the ad prints. The site has no condition field; the one marker it shows is `Ajamaton` (undriven) in the mileage row |
| `description` | The seller's full text |
| `equipment` | Fitted equipment, verbatim and in page order, e.g. `Ilmastointi: Automaattinen` |
| `towBar` | Whether a tow bar is fitted |
| `imageUrls` | Every gallery photo at the `-large` rendition (1080 px high), in the order the ad's slider shows them, without the "similar ads" thumbnails |
| `dealer` | `{ id, dealerId, dealerParentId, name, sellerType, street, postcode, city, website, profileUrl }`. `website` is where the ad's "Www-sivut" button points, verbatim, so it is whatever the dealer entered; `profileUrl` is the dealer's page on nettiauto |
| `location` | `{ city, region }` — where the car stands, as the ad header prints it. Can differ from the dealer's own address |
| `raw` | Only with `includeRawData`: `{ digitalData, ldJson }`, the page's own analytics object and every ld+json block, unchanged. The few values `digitalData` computes in the browser (viewport, window width, user agent, page title, page location) are null |
| `coordinates` | `{ latitude, longitude, address }` — where the ad's map points, i.e. the dealer's address as Google places it. Null on the few ads with no map button |
| `phone` / `whatsapp` | The number behind the call button, and the seller's WhatsApp when published |
| `searchMatchCount` | What the site reported for the search this row came from |

#### ❓ FAQ

**How often are the fields actually populated?** Measured over 474 electric dealer ads in October 2026: price, dealer block, photos, description, phone and tow-bar flag 100%, last-updated date 100%, plate 99%, VIN 97%, WhatsApp 93%, battery capacity 93%, mileage 91%, electric range 89%, charging connector 80%, AC charging power 78%, DC charging power 75%, battery warranty 68%, electricity consumption 58%, state of health 17%. Measured on two later samples of 73 and 40 ads for the fields added since: variant, seller type, dealer website and car location 100%, coordinates 98%, equipment 97%, VAT label 50%. Where a field is empty it is empty on the site too.

**Why is state of health so rare?** Because the site has no field for it. Sellers who have had the battery tested write the figure into the description or the title, and most have not. Around one car in six carries one, and the actor pulls out both the percentage and the name of whoever measured it.

**Why is `publishedAt` always empty?** Nettiauto prints only one date on an ad page, the `Päivitetty` (updated) date, which the row carries as `modifiedAt` on every ad. There is no posted date anywhere in the page, its analytics payload or its schema.org block, so there is nothing to read.

**Is there a fitted-equipment list?** Yes, on almost every ad. The page groups it under safety, comfort, electronics and other; the row flattens it into `equipment`, each item verbatim, in page order.

**Where do the coordinates come from?** The ad's HTML has none. Its "Kartta" button sends the dealer's address to Google Maps with the site's own key, and Google returns the point. The actor makes that same request once per address per run, so a full sweep adds a few hundred requests, not one per car. If the site ever changes its Maps key, `coordinates` comes back null until the actor is updated; the address stays on the row either way.

**Is the dealer's business ID (Y-tunnus) included?** No. Nettiauto does not print it on the ad or on the dealer's profile page.

**Do I need a proxy?** No. The site sits behind Cloudflare, but once the session cookies from its sign-on redirect are in place it serves plain datacenter IPs. Now and then a run lands on an IP Cloudflare has flagged, and every request gets its JavaScript challenge. Apify's datacenter and residential proxies get the same challenge (measured October 2026), so the actor does not switch to them: after three refused requests in a row it continues through Apify's Unblocker, which answers the challenge, and tries direct again every hundred requests so it returns as soon as the block lifts. That costs you nothing extra. The one gap: the phone number comes from a redirect the Unblocker cannot pass, so cars fetched while the IP was blocked have no `phone`, and the run log says how many. The `proxy` input exists only if you want your own egress.

**What happens if Apify moves my run to another server?** Apify sometimes migrates a running actor. It restarts from the top while its dataset keeps everything already delivered, so on start the actor reads the ids already in the dataset and skips them: they are not fetched or charged again, and still count toward the completeness check.

**How do I know the dataset is complete?** Every search page carries the count the site reports. The actor records it, counts what it delivered, and writes both to the `COMPLETENESS` record in the run's key-value store and the run's status message. Past your threshold — by default 1% or 25 cars, whichever is larger — the run ends as FAILED. The threshold exists because the count moves while a run is in flight. Runs you cut short with your own row limits are exempt, and delivering more rows than reported never fails, since rows are deduplicated by ad id.

**Why would I use list mode?** `listOnly` returns id and URL without opening a single ad page. Run it first, compare against your last sweep, then scrape only the new ids. `modifiedAt` stays null in this mode: search results carry no date, and the updated date is only printed on the ad itself.

**How is a price range applied?** By the actor, not the site. Measured in October 2026: the electric quick link ignores price parameters, and the filter form answers any price with a saved search that keeps fuel and price but drops the dealer filter. Electric dealer cars up to 15,000 € came back as 85 cars there, private sellers included, against 71 from dealers. So the actor keeps the quick link, which applies fuel and seller correctly, sorts it by price, keeps the cars inside your range and stops at the first page past it. Only the few pages around the range are read. The completeness check then counts the in-range cars the pages listed, since the site's own total covers the whole unfiltered search.

**Can I scrape petrol and diesel cars too?** Yes. Turn off *Electric cars only*, or paste any search URL from the site.

#### 💬 Support

Found a bug or need a field that is not here? Open an issue on the actor's Issues tab.

#### 🤖 For AI Agents & LLM Apps

Compact reference for agents calling this actor through the [Apify MCP server](https://mcp.apify.com) or the Apify API (actor: `memo23/nettiauto-scraper`).

**Purpose:** scrape nettiauto.com, Finland's largest car marketplace, into one flat row per car, including battery figures, the dealer block and the revealed phone number.

**Minimal input:**

```json
{ "dealersOnly": true, "electricOnly": true, "scrapeDetails": true, "maxItems": 50 }
```

**Output fields:** `adId`, `url`, `title`, `make`, `model`, `variant`, `bodyType`, `modelYear`, `price {amount, currency}`, `priceType`, `mileageKm`, `firstRegistered`, `firstRegisteredMonth`, `conditionTags`, `vehicleType`, `vin`, `registrationNumber`, `battery {capacityKwh, rangeKm, stateOfHealthPercent, stateOfHealthSource, chargingType, chargingPowerAcKw, chargingPowerDcKw, warranty, consumptionKwhPer100Km}`, `powerKw`, `powerHp`, `fuel`, `transmission`, `drivetrain`, `colour`, `seats`, `description`, `equipment`, `towBar`, `imageUrls`, `dealer {id, dealerId, dealerParentId, name, sellerType, street, postcode, city, website, profileUrl}`, `coordinates {latitude, longitude, address}`, `location {city, region}`, `phone`, `whatsapp`, `publishedAt`, `modifiedAt`, `searchMatchCount`, `raw {digitalData, ldJson}` (only with `includeRawData`), `detailScraped`, `sourceUrl`, `scrapedAt`. In `listOnly` mode a row is just `adId`, `url`, `modifiedAt` (null), `sourceUrl`, `scrapedAt`.

**Behaviours an agent should know:**

- Always set `maxItems` — the electric dealer search alone runs to about 9,700 cars.
- `scrapeDetails: true` is what populates everything beyond the id and URL; without it there is no row worth paying for.
- `scrapePhone: true` adds `phone` at no extra charge. It needs the ad page, so it implies detail fetching.
- `startUrls` and the filter builder are alternatives: the builder only fires when no URLs are pasted.
- Results paginate with no ceiling, so no splitting of a large query is needed.
- Read the `COMPLETENESS` key-value record for reported-versus-delivered. With `failOnIncomplete` on, a shortfall past `max(completenessTolerancePercent%, completenessToleranceAbsolute)` ends the run as FAILED.
- Billing: one `result` event per emitted row, whatever that row carries. Failed and deduplicated requests are never billed.
- No proxy is required; passing one is an explicit override.

### ⚠️ Disclaimer

This scraper is an independent tool and is **not affiliated with, endorsed by, or sponsored by** Nettiauto, Nettix Oy, Alma Media, or any of their subsidiaries. All trademarks, service marks and trade names are the property of their respective owners.

It collects only **publicly available** listing data from nettiauto.com. It does not access private accounts, bypass authentication, or collect personal data beyond the contact details advertisers themselves publish on public ads. You are responsible for using the scraped data in compliance with Nettiauto's terms, Finnish and EU law (including GDPR), and any applicable regulations. Use it for legitimate research, aggregation and analysis.

### SEO Keywords

nettiauto scraper, nettiauto api, finland car scraper, finnish car marketplace data, scrape nettiauto listings, used car data finland, ev battery data finland, electric car listings finland, car dealer data finland, vehicle vin scraper, car price monitoring finland, nettiauto crawler, finnish vehicle dataset, state of health battery data, car gallery scraper, finland automotive market data

# Actor input Schema

## `startUrls` (type: `array`):

nettiauto.com search-result or ad URLs, auto-classified. Search: `https://www.nettiauto.com/pikalinkit/sahkoautot?posted_by=dealer`. Filter search: `https://www.nettiauto.com/vaihtoautot?posted_by=dealer`. Ad: `https://www.nettiauto.com/audi/e-tron/16025210`. A dealer's own inventory page works too. Leave empty to build a search from the filters below. Example: \["https://www.nettiauto.com/pikalinkit/sahkoautot?posted_by=dealer"]. Default: empty.

## `dealersOnly` (type: `boolean`):

Only cars posted by dealers, which is what `posted_by=dealer` does on the site. Turn off to include private sellers. Example: true. Default: true.

## `electricOnly` (type: `boolean`):

Restrict to electric cars, the way the site's own quick link does. Turn off for every fuel type. Example: true. Default: true.

## `priceFrom` (type: `integer`):

Lowest asking price in euros. nettiauto's quick links ignore price and its filter form drops the dealer filter when a price is set, so the actor sorts the search by price and applies the range itself, stopping once prices pass it. A price in a pasted search URL (`priceFrom`/`priceTo`) is handled the same way. Example: 10000. Default: no minimum.

## `priceTo` (type: `integer`):

Highest asking price in euros, applied the same way as the minimum. Example: 15000. Default: no maximum.

## `scrapeDetails` (type: `boolean`):

Open each car's own page for the full record: spec table, battery capacity and range, VIN, plate, first-registration date, description, the whole gallery and the dealer's block. Without it there is nothing to return but an id and a URL, so leave it on unless you want the list-only mode below. Example: true. Default: true.

## `scrapePhone` (type: `boolean`):

Return the number behind the call button as `phone`. The page shows it as an image, but the button answers with a redirect carrying the number in plain text, so no image is read and no login is needed. Costs one extra request per car and is included in the per-result price. While Cloudflare blocks a run's IP the actor continues through Apify's Unblocker, which cannot pass that redirect, so cars fetched during a block come without a phone; the run log says how many. Example: true. Default: false.

## `listOnly` (type: `boolean`):

Emit one thin row per car, with its id and URL, without opening a single ad page. Useful for working out which cars are new since your last run before paying to scrape them in full. `modifiedAt` stays empty in this mode: search results carry no date, and the updated date is printed only on the ad itself. Example: false. Default: false.

## `includeRawData` (type: `boolean`):

Attach the ad's own `window.digitalData` object and every ld+json block to each row as `raw`, unchanged, for anyone who wants to read fields themselves. The handful of values the browser computes at load time (viewport, window width, user agent, page title, page location) are null, because they have no value outside a browser. Adds about 5 KB per row and many columns to CSV and Excel exports, so it is off by default. Included in the per-result price. Example: true. Default: false.

## `enrichEmails` (type: `boolean`):

Look up the dealer's email address on their own website and return it as `contactEmail`. The ad's own "Www-sivut" link gives the site on nearly every row, and where it does not, the dealer's name is used to find it. Each dealer is looked up once per run however many cars they list, and only a found address is charged. Example: false. Default: false.

## `failOnIncomplete` (type: `boolean`):

End the run as FAILED when the shortfall against the reported count exceeds the threshold below. Runs stopped by your own row limits are exempt — those are partial by request. Delivering more rows than reported never fails: ads are published while a run is in flight and rows are deduplicated by ad id. Example: true. Default: true.

## `completenessTolerancePercent` (type: `integer`):

Relative half of the threshold. The count moves while a run is in flight, so a strict match would fail nearly every run. Example: 1. Default: 1.

## `completenessToleranceAbsolute` (type: `integer`):

Absolute floor of the threshold; the larger of the two wins. On a small search 1% is only a handful of cars, well inside a day's churn — this floor is what stops those runs failing on noise. Example: 25. Default: 25.

## `maxItems` (type: `integer`):

Hard cap on rows across the entire run; each row is one paid result. Free-tier accounts are additionally capped at 100. Example: 500. Default: 1000.

## `maxItemsPerSearch` (type: `integer`):

Cap on rows from any one search URL. Example: 1000. Default: 1000.

## `maxConcurrency` (type: `integer`):

How many HTTP requests run at once. Eight is comfortable against this site; raise for speed, lower to be gentler. Example: 8. Default: 8.

## `maxRequestRetries` (type: `integer`):

Per-request retry budget for 403/429/5xx and network errors, with exponential backoff. Example: 6. Default: 6.

## `proxy` (type: `object`):

Leave empty. The actor clears the site's Cloudflare setup from plain datacenter IPs once the session cookies are in place, and when Cloudflare does block a run's IP it falls back to Apify's Unblocker on its own, at no extra charge to you. Set this only if you need requests to exit through your own proxy. Example: {"useApifyProxy": true}. Default: no proxy.

## Actor input object example

```json
{
  "startUrls": [],
  "dealersOnly": true,
  "electricOnly": true,
  "scrapeDetails": true,
  "scrapePhone": false,
  "listOnly": false,
  "includeRawData": false,
  "enrichEmails": false,
  "failOnIncomplete": true,
  "completenessTolerancePercent": 1,
  "completenessToleranceAbsolute": 25,
  "maxItems": 1000,
  "maxItemsPerSearch": 1000,
  "maxConcurrency": 8,
  "maxRequestRetries": 6
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `completeness` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/nettiauto-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "startUrls": [] }

# Run the Actor and wait for it to finish
run = client.actor("memo23/nettiauto-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": []
}' |
apify call memo23/nettiauto-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,memo23/nettiauto-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/ZQSBKoXn4SrEdXdQW/builds/i6LLSREwYgaEzhiYt/openapi.json
