Otomoto.pl Scraper — Polish Auto Classifieds
Pricing
from $0.60 / 1,000 car listing extracteds
Otomoto.pl Scraper — Polish Auto Classifieds
Scrape car listings from Otomoto.pl — Poland's largest vehicle marketplace with 240k+ listings. Get price, make, model, year, mileage, fuel type, engine specs, and location.
Pricing
from $0.60 / 1,000 car listing extracteds
Rating
0.0
(0)
Developer
Automation Lab
Maintained by CommunityActor stats
1
Bookmarked
42
Total users
5
Monthly active users
a month ago
Last modified
Categories
Share
Extract car listings from Otomoto.pl — Poland's largest automotive marketplace with 240,000+ active listings. Get price, make, model, year, mileage, fuel type, engine specs, location, seller type, and more — all in a clean, structured dataset ready for analysis, valuation models, or lead generation.
🚗 What does Otomoto.pl Scraper do?
This actor crawls Otomoto.pl search results and category pages, extracting structured data from every car listing. It uses the site's own Next.js SSR data (embedded in every page), so it is fast, lightweight, and reliable — no browser automation needed.
For each listing it extracts:
- Car specs: make, model, version/trim, year, mileage, fuel type, transmission, engine capacity, engine power
- Price: in PLN with currency code
- Location: city and voivodeship (region)
- Listing metadata: unique ID, URL, posting date, badges (e.g. LOW_MILEAGE), seller type (private or dealer), thumbnail image
- Extraction metadata:
scrapedAton every emitted listing - Optional detail-page enrichment:
fullDescription,photoUrls,equipment,bodyColor,bodyType,doorCount,seatCount,driveType,condition, andfirstRegistrationDate
Detail-page enrichment uses only anonymous public pages. It does not collect seller names, phone numbers, email addresses, VINs, login-gated data, or other private fields. Start URLs must use HTTP(S) on otomoto.pl or www.otomoto.pl; credential-bearing and third-party URLs are rejected before fetching. Results are deduplicated by stable listing ID (URL fallback) across every start URL, so maxResults and result charges count unique listings only.
👥 Who is it for?
- Polish used-car dealers who need to monitor competitor pricing and stock in their region
- Residual-value analysts and automotive market researchers building pricing models for PLN-denominated assets
- OEM data teams tracking brand-level inventory levels or model popularity on the secondary market
- Classified-data aggregators building pan-CEE automotive datasets alongside similar scrapers for Mobile.de, AutoScout24, OLX, and OtoDom
- PropTech / FinTech analysts who need correlated auto + real-estate data for Polish consumers
- Individual buyers automating their car search across thousands of listings
✅ Why use it?
- ⚡ HTTP + Cheerio — no browser, runs in 256 MB, extremely cost-efficient
- 🔁 Metadata-driven pagination — follows Otomoto's page metadata while preserving every search filter and preventing page loops
- 🔗 Validated search URLs — paste an HTTP(S)
otomoto.plURL with your own filters already applied - 🌍 Polish market coverage — 240,000+ listings across all voivodeships
- 💰 Structured pricing — price in PLN as a number (not a string), ready for aggregation
- ✨ Opt-in detail-page enrichment — add descriptions, media, equipment, body/drive/seat/door facts, condition, and first registration only when needed
- 🛡️ Resilient — bounded retries, optional proxy routing, configurable detail concurrency, and card preservation when an individual detail page fails
📦 Data extracted
| Field | Type | Example |
|---|---|---|
id | string | "6147784510" |
url | string | "https://www.otomoto.pl/osobowe/oferta/kia-picanto-..." |
title | string | "Kia Picanto 1.0 ISG Spirit" |
shortDescription | string | "bezwypadkowa, pewny przebieg, kamera" |
make | string | "Kia" |
model | string | "Picanto" |
version | string | "1.0 ISG Spirit" |
year | number | 2017 |
mileageKm | number | 91000 |
fuelType | string | "Benzyna" |
gearbox | string | "Manualna" |
engineCapacityCc | number | 998 |
enginePowerHp | number | 67 |
pricePLN | number | 27900 |
priceCurrency | string | "PLN" |
city | string | "Olsztyn" |
region | string | "Warmińsko-mazurskie" |
badges | array | ["LOW_MILEAGE"] |
sellerType | string | "ProfessionalSeller" |
thumbnailUrl | string | "https://ireland.apollo.olxcdn.com/..." |
createdAt | string | "2026-05-10T08:06:39Z" |
scrapedAt | string | "2026-09-19T12:00:00.000Z" |
fullDescription | string or null | "Samochód z polskiego salonu..." |
photoUrls | array or null | ["https://ireland.apollo.olxcdn.com/..."] |
equipment | array or null | ["Klimatyzacja automatyczna", "Tempomat adaptacyjny ACC"] |
bodyColor | string or null | "Biały" |
bodyType | string or null | "SUV" |
doorCount | number or null | 5 |
seatCount | number or null | 5 |
driveType | string or null | "4x4" |
condition | string or null | "Używany" |
firstRegistrationDate | string or null | "2022-03-01" |
The detail fields are added when includeDetails is true. Their availability depends on each public listing. A failed detail request keeps the card fields and sets all requested detail fields to null.
How much does it cost to scrape Otomoto.pl listings?
This actor uses Pay-Per-Event pricing — you pay per listing extracted, not for actor runtime. There is a small one-time start fee per run plus a per-result charge.
Typical cost examples:
| Use case | Results | Estimated cost |
|---|---|---|
| Quick price check | 50 listings | < $0.10 |
| Regional market snapshot | 500 listings | ~$0.60 |
| Full category crawl | 5,000 listings | ~$5.50 |
| Daily monitoring feed | 200 listings/day | ~$0.25/day |
New Apify users receive a free-tier credit that covers hundreds of results — enough to evaluate the actor at zero cost.
🚀 How to use it
- Open the actor on Apify and click Try for free
- Paste a Start URL — copy any search URL from Otomoto.pl (with your filters applied) or use a category shortcut like
https://www.otomoto.pl/osobowe/bmw - Set Max Results — start with 20 to test, then increase for production runs
- Click Start — results appear in the dataset within seconds
Using Otomoto.pl search filters
The easiest way to apply filters is to use Otomoto.pl's own search UI:
- Go to otomoto.pl/osobowe
- Select brand, model, price range, year, region, etc.
- Copy the URL from your browser's address bar
- Paste it as a Start URL in this actor
The actor will walk through every result page for that filter combination.
⚙️ Input parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
startUrls | array | ["https://www.otomoto.pl/osobowe"] | One or more Otomoto.pl category or search URLs to scrape |
maxResults | integer | 50 | Maximum number of listings to extract across all start URLs |
includeDetails | boolean | false | Enable privacy-safe public detail-page enrichment |
detailConcurrency | integer | 5 | Parallel detail requests when enrichment is enabled (1–10) |
maxRequestRetries | integer | 3 | Bounded retry attempts for transient search or detail failures (1–10) |
proxyConfiguration | object | none | Optional Apify/custom proxy settings; omitted means direct HTTP. Residential routing is limited to card-only collection. |
Residential proxy transfer is supported when includeDetails is false. Detail enrichment performs one additional request per listing, so the Actor rejects the uneconomic RESIDENTIAL + includeDetails: true combination before charging or fetching. Use direct or datacenter routing for enriched results.
Example input
{"startUrls": ["https://www.otomoto.pl/osobowe/volkswagen/golf?search%5Bfilter_float_price%3Ato%5D=50000&search%5Bfilter_float_year%3Afrom%5D=2015"],"maxResults": 200,"includeDetails": true,"detailConcurrency": 5}
📤 Output
Results are saved to the default Apify dataset. Each item corresponds to one car listing.
Example output item
{"id": "6147784510","url": "https://www.otomoto.pl/osobowe/oferta/kia-picanto-ID6I3tAG.html","title": "Kia Picanto 1.0 ISG Spirit","shortDescription": "bezwypadkowa, pewny przebieg, kamera, grzana kierownica","make": "Kia","model": "Picanto","version": "1.0 ISG Spirit","year": 2017,"mileageKm": 91000,"fuelType": "Benzyna","gearbox": "Manualna","engineCapacityCc": 998,"enginePowerHp": 67,"pricePLN": 27900,"priceCurrency": "PLN","city": "Olsztyn","region": "Warmińsko-mazurskie","badges": [],"sellerType": "ProfessionalSeller","thumbnailUrl": "https://ireland.apollo.olxcdn.com/v1/files/wzwce1kactjz2-OTOMOTOPL/image;s=320x240","createdAt": "2026-05-10T08:06:39Z","scrapedAt": "2026-09-19T12:00:00.000Z","fullDescription": "Samochód z polskiego salonu...","photoUrls": ["https://ireland.apollo.olxcdn.com/v1/files/.../image"],"equipment": ["Klimatyzacja automatyczna", "Tempomat adaptacyjny ACC"],"bodyColor": "Biały","bodyType": "SUV","doorCount": 5,"seatCount": 5,"driveType": "4x4","condition": "Używany","firstRegistrationDate": "2022-03-01"}
💡 Tips & tricks
- Use multiple start URLs to combine brands or regions in one dataset — overlapping listings are emitted and charged only once
- Low maxResults for testing — set
maxResults: 20for a quick test, then bump to production volume - Filter by voivodeship — use Otomoto.pl's location filter in the search UI and copy the URL to target a specific region
- Monitor new listings — schedule a daily run with
maxResults: 200and the "Najnowsze" (newest first) sort to capture fresh listings - Private vs dealer — filter by
sellerType: "PrivateSeller"in your downstream code to target only private sellers (often priced lower) - Badges — the
badgesarray can includeLOW_MILEAGE,PRICE_DROP, and similar signals useful for deal-finding
🔗 Integrations
Pricing dashboard (Google Sheets)
Schedule a daily run → connect dataset to Google Sheets via the Apify integration → build a pivot table showing average price by make/model/year. Auto-refreshes every morning.
Lead generation CRM (Zapier/Make)
Trigger on new dataset items → filter by sellerType: "PrivateSeller" → push listing URLs and vehicle metadata to HubSpot or another CRM for dealer review. The Actor does not collect seller contact details.
Valuation model (Python + pandas)
import pandas as pdimport requestsdataset_id = "YOUR_DATASET_ID"token = "YOUR_APIFY_TOKEN"url = f"https://api.apify.com/v2/datasets/{dataset_id}/items?token={token}&format=json"df = pd.read_json(url)# Average price by make and yearpivot = df.groupby(['make', 'year'])['pricePLN'].mean().reset_index()print(pivot)
Daily price alert (n8n)
Schedule run → compare pricePLN against your threshold → send Slack alert when a listing drops below target price.
🖥️ API usage
Node.js
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });const run = await client.actor('automation-lab/otomoto-scraper').call({startUrls: ['https://www.otomoto.pl/osobowe/bmw'],maxResults: 100,});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items);
Python
from apify_client import ApifyClientclient = ApifyClient("YOUR_APIFY_TOKEN")run = client.actor("automation-lab/otomoto-scraper").call(run_input={"startUrls": ["https://www.otomoto.pl/osobowe/bmw"],"maxResults": 100,})for item in client.dataset(run["defaultDatasetId"]).iterate_items():print(item["make"], item["model"], item["pricePLN"])
cURL
curl -X POST "https://api.apify.com/v2/acts/automation-lab~otomoto-scraper/runs?token=YOUR_APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"startUrls": ["https://www.otomoto.pl/osobowe/bmw"],"maxResults": 100}'
🤖 MCP (Claude / AI assistant integration)
Use this actor directly from Claude Code, Claude Desktop, Cursor, or VS Code with the Apify MCP server.
Claude Code
$claude mcp add --transport http apify "https://mcp.apify.com?tools=automation-lab/otomoto-scraper"
Claude Desktop / Cursor / VS Code
Add to your MCP config:
{"mcpServers": {"apify": {"type": "http","url": "https://mcp.apify.com?tools=automation-lab/otomoto-scraper","headers": { "Authorization": "Bearer YOUR_APIFY_TOKEN" }}}}
Example prompts
- "Scrape the top 50 BMW listings from Otomoto.pl under 80,000 PLN"
- "Get all VW Golf listings from 2018–2022 with under 100,000 km and export to CSV"
- "Monitor new listings on Otomoto.pl in the Mazowieckie region every morning"
⚖️ Legality
Web scraping public data is broadly legal in the EU under CJEU C-30/23. Otomoto.pl's public listing pages are accessible without login and constitute publicly available information. This actor does not extract private user data, bypass authentication, or submit forms.
Users are responsible for complying with Otomoto.pl's Terms of Service and applicable Polish/EU law in their jurisdiction. Do not use this actor to re-publish Otomoto.pl data in a way that competes with their core business.
This independent Actor is not affiliated with, endorsed by, or sponsored by Otomoto. It uses deterministic HTTP parsing and does not call AI models. Results, diagnostics, and logs stay in the user's Apify run storage and follow that account's retention/deletion settings; the Actor keeps no external copy.
❓ FAQ
Q: How many listings can I scrape per run?
A: maxResults controls the unique-listing cap. Runtime is also bounded by the Actor's configured timeout, and detail enrichment adds one request per listing. For large collections, split non-overlapping filtered URLs across scheduled runs and choose a limit that fits your timeout.
Q: Does it scrape detail pages (full description, all photos, equipment list)?
A: Yes, optionally. Set includeDetails: true to add descriptions, photos, equipment, color, body type, doors, seats, drive, condition, and first-registration date. It is slower because it makes one extra public request per listing. Fields are nullable because not every listing publishes every value, and a failed detail request still returns the valid card record. VIN and seller contacts are not collected.
Q: Are prices in EUR or PLN?
A: All prices are extracted in PLN (priceCurrency: "PLN"). Otomoto.pl is a Polish platform and lists in Polish Złoty. Convert to EUR using the current exchange rate in your downstream code.
Q: The actor returned 0 results. What went wrong?
A: The Actor rejects malformed, credential-bearing, and non-Otomoto URLs before fetching. A valid category with no matching listings can return an empty dataset; request/parser failures are counted separately, and a run where every search route fails ends non-zero instead of reporting a false-success empty match. Try https://www.otomoto.pl/osobowe first, then add your filters.
Q: Why are fuel types in Polish (e.g. "Benzyna")? A: Otomoto.pl returns fuel types in Polish. "Benzyna" = Petrol/Gasoline, "Diesel" = Diesel, "Hybryda" = Hybrid, "Hybryda Plug-in" = Plug-in Hybrid, "Elektryczny" = Electric, "Benzyna+LPG" = LPG.
🔗 Related scrapers
- automation-lab/olx-scraper — OLX.pl general classifieds
- automation-lab/otodom-scraper — Polish real estate classifieds
- automation-lab/allegro-scraper — Poland's largest e-commerce marketplace