Standvirtual Scraper - Car Prices, Mileage & Specs
Pricing
$1.50 / 1,000 cars
Standvirtual Scraper - Car Prices, Mileage & Specs
Scrape Standvirtual used-car listings by make and model: price, year, mileage, fuel, gearbox, engine size, power, location and dealer. New-ad alerts on a schedule can go to Discord, Telegram, Slack or any webhook. No login, no private-seller personal data.
Pricing
$1.50 / 1,000 cars
Rating
0.0
(0)
Developer
Luke Hunter
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
10 days ago
Last modified
Categories
Share
For car dealers, flippers, importers and market analysts who need Standvirtual data without copying it by hand. Give it a make (and optionally a model) or your own Standvirtual search URLs and it returns every used-car listing on the results pages - title, price, year, mileage, fuel, gearbox, engine size, power, location, seller type and dealer - read straight from Standvirtual's own public search pages. No login, no private-seller personal data. Turn on new-ad alerts (onlyNewSinceLastRun) and run it on a schedule to get only the cars listed since your last run.
Pay-per-result: $0.0015 per car — 1,000 cars = $1.50.
Quick start (1 minute)
- Open the Input tab and enter a make, e.g.
bmw. Add a model such as320if you want one model only. - Click Start. Export the dataset to CSV/Excel/JSON, or pull it through the API shown below.
{"make": "bmw","maxPagesPerSearch": 1,"maxItems": 20}
That prefilled example finishes in well under a minute and costs $0.03.
How it works
A Standvirtual search page (standvirtual.com/carros/<make>/<model>) is server-rendered and carries its own listing data, so no login or session is needed and no private API is used. This Actor:
- Fetches
robots.txtand checks every URL it is about to request, including?page=N. A disallowed page is skipped (never fetched) and reported in the run's status message. - Reads the search page by page - 32 results plus a few promoted ads per page - and stops at the end of the results, at
maxPagesPerSearchor atmaxItems. - Turns each ad into one flat dataset row. A promoted ad that Standvirtual repeats on the next page is delivered once.
- Reads politely: one request about every 3 seconds, an honest self-identifying User-Agent, no proxies and no cookies. If Standvirtual blocks a request or shows a challenge page, the Actor reports it and stops that search - it never tries to get around it. Pages already delivered are kept.
Use cases
- Standvirtual price tracker: run
bmw+320on a schedule and chartpriceagainstyearandmileageKmto see what a used BMW 320 really sells for in Portugal. - Dealer stock and competitor monitoring: export
dealerName,price,mileageKmandlocationfor a make to see who stocks what, at which price, and where. - Car flipping and bargain alerts: sort by newest, set a
priceTo, turn on new-ad alerts and get only freshly listed cars under your threshold, as soon as the next scheduled run finds them.
New-ad alerts: get only new cars
Turn on onlyNewSinceLastRun and put this Actor on a schedule: every run after the first delivers (and charges for) only cars it hasn't delivered before for that exact search.
- Set your search as usual, with the newest ads first so a short run catches the latest listings:
{"make": "bmw","model": "320","priceTo": 15000,"yearFrom": 2015,"sortBy": "newest","maxPagesPerSearch": 2,"onlyNewSinceLastRun": true,"maxItems": 100}
- Click Schedule on the run page (or create one under Schedules in the Apify Console) - every 30-60 minutes works well for a fast-moving search.
- The first scheduled run is a baseline: it delivers every car currently listed (it's all new to you) and remembers it; you are charged normally for that run. Every later run only delivers cars it hasn't seen before for this exact search.
- Add a Webhook under the schedule's Integrations for
ACTOR.RUN.SUCCEEDED, pointing at Apify's Slack/email integration (or a Zapier/Make step), so you hear about new matching cars as they appear.
The run's status message tells you what happened: "First run for 1 search: 64 car(s) delivered and remembered; next runs return only new ones." on the baseline, then "3 new car(s) since last run across 1 search (61 already seen)." later. A run that finds nothing new still succeeds with an empty dataset - that is the point of the mode, not a failure.
Each make/model search and each pasted URL is tracked as its own independent watch (keyed on its make, model, price/year range, sort or exact URL), so several saved searches in one task never share history. stateStoreName only needs changing to isolate or reset a watch's memory.
Get Standvirtual alerts in Discord, Telegram, Slack, Teams, Google Chat, your phone (ntfy/Pushover) or any app (webhook)
Want a Standvirtual Discord alert, a Standvirtual Telegram bot, or new cars pushed to Slack, Teams, Google Chat or your phone instead of checking a dataset? Add one or more destinations in the Actor's Notifications (optional) section and each run sends the cars it delivered. It is a monitor that notifies you: it only reads public Standvirtual search pages and never contacts sellers, buys anything or logs in anywhere, and the messages go only to the places you set up.
| Channel | Fill in | What you get |
|---|---|---|
| Discord | discordWebhookUrl | One card per car, 10 per message |
| Telegram | telegramBotToken + telegramChatId | Up to 10 cars per message, one line each |
| Slack | slackWebhookUrl | Block Kit message, up to 10 cars per message |
| Mattermost, Rocket.Chat | slackCompatibleWebhookUrl | Slack-style incoming webhook: a Markdown list (these apps do not render Slack Block Kit) |
| Microsoft Teams | teamsWebhookUrl | Adaptive Card from a Teams Workflows webhook, up to 10 cars per card |
| Google Chat | googleChatWebhookUrl | Card in a space, up to 10 cars per message |
| ntfy (phone push, no account) | ntfyTopic (optional ntfyServer, ntfyToken) | A push per car, up to 10, then one "...and N more" |
| Pushover (phone push) | pushoverUserKey + pushoverAppToken | A push per car, up to 10, then one "...and N more" |
| Any app: Zapier, Make, n8n, Pipedream, IFTTT, Home Assistant | webhookUrl | One JSON POST per run with the delivered rows |
Fill in as many as you like; each channel works on its own and a failing one never stops the others.
Setup, channel by channel
- Discord (1 minute): Server Settings → Integrations → Webhooks → New Webhook → pick the channel → Copy Webhook URL.
- Telegram (2 minutes): chat with @BotFather, send
/newbot, paste the token intotelegramBotToken. Add the bot to the chat or channel (administrator for a channel; for a chat with yourself press Start on the bot).telegramChatIdis a numeric id (123456789, or-1001234567890for a group or channel) or a public@channelusername. - Slack: at api.slack.com/apps create an app (From scratch) → Incoming Webhooks → switch it on → Add New Webhook to Workspace → pick the channel → copy the
https://hooks.slack.com/services/...URL. - Mattermost or Rocket.Chat: create an incoming webhook in the app and paste its public
https://URL intoslackCompatibleWebhookUrl. Localhost, private IP addresses and.local/.internalnames are rejected. - Microsoft Teams: in the channel open ... → Workflows → Post to a channel when a webhook request is received → finish the wizard and copy the URL (a host ending in
.logic.azure.comor.powerplatform.com). The old "Office 365 connector" webhooks are retired and not supported. - Google Chat: in the space open Apps & integrations → Webhooks → Add webhook, name it and copy the
https://chat.googleapis.com/v1/spaces/...URL (needs a Google Workspace account that allows webhooks). - ntfy (free, no account): install the ntfy app (or open ntfy.sh), subscribe to a topic with a long random name, and put the same name in
ntfyTopic. Anyone who knows the topic can read it, so make it unguessable. Own server or a protected topic: setntfyServer(publichttps://address) andntfyToken. - Pushover: copy your user key from pushover.net into
pushoverUserKey, create an application at pushover.net/apps/build and put its API token inpushoverAppToken. Pushover itself is a separate paid app after its trial: check their pricing. - Any app (webhook): paste the catch-hook URL from Zapier, Make, n8n, Pipedream, IFTTT or Home Assistant into
webhookUrl. It must be a publichttps://address.
The recipe: a Standvirtual new-car alert in five steps
- Set
make(and optionallymodel, price and year range), or paste a standvirtual.com search URL intostartUrls. - Turn on
onlyNewSinceLastRun, so only cars not delivered before are sent. - Add your destination(s) from the table above.
- Run it once. The first run is the baseline and sends what is currently listed (up to
maxNotifications). - Create a Schedule (for example every few hours). From then on you only hear about cars it has not delivered before for that search.
{"make": "bmw","onlyNewSinceLastRun": true,"sortBy": "newest","maxItems": 100,"ntfyTopic": "your-long-random-topic-name","maxNotifications": 25}
The generic webhook (webhookUrl)
Each run sends one POST per 100 delivered rows (so usually one) with this body, followed by whatever your tool does with it:
{"source": "apify","actor": "standvirtual-scraper","runId": "HG7ML7M8z78YcAPEB","count": 2,"total": 2,"part": 1,"parts": 1,"items": [ { "...": "the full dataset rows" } ]}
count is the number of rows in this POST, total the number in the run, and part / parts number the POSTs when a run delivers more than 100 rows. maxNotifications does not cap the webhook. Your endpoint should answer with a 2xx status; redirects are not followed and a failure is reported in the run status, never retried in a loop.
What to expect
- What a message contains: title, price in euro, year, mileage, fuel type, gearbox, power, location, the dealer name for professional sellers (private sellers are only labelled, never named), the listed date and a photo, plus the link.
- Only delivered cars are sent. A run that finds nothing new sends nothing, so there is no "no new cars" spam. Rows the run could not deliver (for example because your spending limit was reached) are not sent.
maxNotifications(default 25, max 100) caps how many cars go to chat and phone channels per run (the webhook is not capped). The rest are still in the dataset and the status message says so. ntfy and Pushover send at most 10 individual pushes, then one "...and N more" summary.- Cost: notifications are free. You pay only for delivered cars at the usual price, exactly as without alerts. There is no extra charge for any channel.
- Failures never fail the run. A deleted webhook or an unreachable service leaves the run successful and the dataset complete, and the status message says what happened, for example
Notified: Discord 12, Slack 12, ntfy 10 (+summary). Teams failed (HTTP 404) - check the webhook URL.The logs show the channel name and HTTP status only, never a URL or token. - Your URLs, tokens and ntfy topic are stored as secret inputs. Anyone holding a webhook URL can post to that channel, so treat it like a password.
- The dataset is unchanged. The output rows are identical whether or not you set a destination; the run's status message just gains a short note such as
Notified: Discord 12, ntfy 10 (+summary). - It is a monitor that notifies you. It never contacts sellers, buys anything or logs in to Standvirtual, and the messages go only to the destinations you set up.
Input
{"make": "bmw","model": null,"startUrls": [],"priceFrom": null,"priceTo": null,"yearFrom": null,"yearTo": null,"sortBy": "relevance","maxPagesPerSearch": 2,"maxItems": 100,"onlyNewSinceLastRun": false,"stateStoreName": "standvirtual-scraper-seen-cars"}
| Field | Type | Default | Description |
|---|---|---|---|
make | string | (none) | Make as in the Standvirtual address, e.g. bmw, audi, mercedes-benz. Spaces and accents are cleaned up. Required unless startUrls is given. |
model | string | (none) | One model of the make, e.g. 320, golf, x1. Needs make. |
startUrls | string[] | (none) | Full standvirtual.com/carros search URLs, e.g. https://www.standvirtual.com/carros/bmw/320?search[filter_enum_fuel_type]=diesel. Filters in the URL are kept; only the page number is added. Single ad pages are rejected. Up to 50. |
priceFrom / priceTo | integer | (none) | Price range in EUR for the make/model search. |
yearFrom / yearTo | integer | (none) | First-registration year range for the make/model search. |
sortBy | "relevance" | "newest" | "price_low" | "price_high" | "relevance" | Result order for the make/model search. Pasted URLs keep their own order. |
maxPagesPerSearch | integer | 2 | 1-10. Pages fetched per search; about 36 cars per page. |
maxItems | integer | 100 | 1-5,000. Hard cap on cars delivered across the whole run - your cost cap. |
onlyNewSinceLastRun | boolean | false | New-ad alerts mode - see above. |
stateStoreName | string | standvirtual-scraper-seen-cars | Store that remembers what each alert-mode search has delivered. |
discordWebhookUrl | string (secret) | (none) | Discord webhook URL. Must look like https://discord.com/api/webhooks/<id>/<token>. Empty means no Discord messages. |
telegramBotToken | string (secret) | (none) | Telegram bot token from @BotFather. Needs telegramChatId. |
telegramChatId | string | (none) | Telegram chat to post to: a numeric chat id or @channelusername. The bot must be added to that chat or channel. Needs telegramBotToken. |
slackWebhookUrl | string (secret) | (none) | Slack incoming webhook, https://hooks.slack.com/services/.... |
slackCompatibleWebhookUrl | string (secret) | (none) | Mattermost / Rocket.Chat incoming webhook (public https:// URL; Markdown list, no Block Kit). |
teamsWebhookUrl | string (secret) | (none) | Microsoft Teams Workflows webhook (host ending .logic.azure.com or .powerplatform.com). |
googleChatWebhookUrl | string (secret) | (none) | Google Chat space webhook, https://chat.googleapis.com/v1/spaces/.... |
ntfyTopic | string (secret) | (none) | ntfy topic name; setting it turns ntfy phone pushes on. Pick a long, unguessable name. |
ntfyServer | string | https://ntfy.sh | ntfy server. Only change it for your own public https:// ntfy server. |
ntfyToken | string (secret) | (none) | Optional ntfy access token for a self-hosted server or protected topic. Needs ntfyTopic. |
pushoverUserKey | string (secret) | (none) | Pushover user key. Needs pushoverAppToken. |
pushoverAppToken | string (secret) | (none) | Pushover application API token. Needs pushoverUserKey. |
webhookUrl | string (secret) | (none) | Generic JSON webhook (Zapier, Make, n8n, Pipedream, IFTTT, Home Assistant): the delivered rows, 100 per POST. Public https:// only. |
maxNotifications | integer | 25 | 1–100. Most cars sent to chat and phone channels per run (the webhook is not capped). Extra rows are still in the dataset. Only used when a destination is set. |
The price and year ranges are sent to Standvirtual and re-checked on every row, so a car outside your range is never delivered or charged.
Output fields
| Category | Fields |
|---|---|
| Identity | id, url, title |
| Price | price, currency |
| Car | make, model, version, year, mileageKm, fuelType, gearbox, engineCc, powerHp |
| Place and seller | location, sellerType, dealerName |
| Dates and media | createdAt, imageUrl |
| Search context | searchUrl |
Missing values are returned as null. Nothing is guessed. fuelType and gearbox are the labels Standvirtual shows, in Portuguese (Gasolina, Diesel, Elétrico, Híbrido Plug-In, Manual, Automática). powerHp is Standvirtual's cv. dealerName is filled only for professional sellers.
Example output
{"id": "8097700319","url": "https://www.standvirtual.com/carros/anuncio/bmw-i3-ver-94-ah-ID8Q17LF.html","title": "BMW i3 (94 Ah)","price": 11900,"currency": "EUR","make": "BMW","model": "i3","version": "(94 Ah)","year": 2016,"mileageKm": 126443,"fuelType": "Elétrico","gearbox": "Automática","engineCc": null,"powerHp": 170,"location": "São Mamede de Infesta e Senhora da Hora, Porto","sellerType": "professional","dealerName": "Drive Point","createdAt": "2026-10-02T14:07:11Z","imageUrl": "https://ireland.apollo.olxcdn.com/v1/files/3sis59kmlmpn-STDVTLPT/image;s=640x480","searchUrl": "https://www.standvirtual.com/carros/bmw"}
Use it as an API
from apify_client import ApifyClientclient = ApifyClient("YOUR_APIFY_TOKEN")run = client.actor("lukehunter/standvirtual-scraper").call(run_input={"make": "bmw","model": "320","yearFrom": 2015,"maxPagesPerSearch": 3,"maxItems": 200,})for car in client.dataset(run["defaultDatasetId"]).iterate_items():print(car["title"], car["price"], car["year"], car["mileageKm"])
Pricing and cost control
Pay per delivered car.
$0.0015 per car — 1,000 cars = $1.50.
| Cars delivered | Cost |
|---|---|
| 20 | $0.03 |
| 1,000 | $1.50 |
| 10,000 | $15.00 |
There is no charge for starting a run, and none for a search that is skipped, blocked or finds nothing. In alert mode you pay only for the new cars. maxItems gives you a hard upper bound on cost.
Notifications to Discord, Telegram, Slack, Teams, Google Chat, ntfy, Pushover and webhooks are free: you pay only for delivered cars, never for messages sent.
Limitations
- Search results only. The Actor reads Standvirtual's search pages, not individual ad pages, so it does not return an ad's full description, equipment list, all photos, VIN or phone number.
- No private-seller personal data, by design. A private ad is reported as
sellerType: "private"with no name or contact detail. For dealerships only the public trading name (dealerName) is returned. - Cars only. Standvirtual's
/carrossection; motorbikes, vans and other sections are not supported. - Fuel and gearbox are Portuguese labels, exactly as Standvirtual shows them.
- Promoted ads are included at the top of each page, as on the site. A promoted ad repeated on a later page is delivered once per search.
- If Standvirtual blocks a request or serves a challenge page, the Actor reports it per search in the run's status message and stops that search rather than trying to evade it. You are not charged for a blocked search.
- If robots.txt disallows a page, that page is skipped and reported.
- Prices and availability are as at scrape time; an advertiser can change or remove an ad at any moment.
- This is an independent tool, not affiliated with, endorsed by or connected to Standvirtual or OLX. You are responsible for using the data in line with Standvirtual's terms and applicable law.
Use it from your code, Make, Zapier or n8n
Built for automated pipelines: small runs, stable field names, one row per item, and you pay only for rows actually delivered.
One API call that runs the Actor and returns the data (replace $APIFY_TOKEN with your token from Apify Console → Settings → Integrations):
curl -s -X POST "https://api.apify.com/v2/acts/lukehunter~standvirtual-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H 'Content-Type: application/json' \-d '{"make": "bmw", "maxPagesPerSearch": 1, "maxItems": 20}'
JavaScript (npm i apify-client):
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('lukehunter~standvirtual-scraper').call({"make": "bmw","maxPagesPerSearch": 1,"maxItems": 20});const { items } = await client.dataset(run.defaultDatasetId).listItems();
Python (pip install apify-client):
import osfrom apify_client import ApifyClientclient = ApifyClient(os.environ['APIFY_TOKEN'])run = client.actor('lukehunter~standvirtual-scraper').call(run_input={"make": "bmw", "maxPagesPerSearch": 1, "maxItems": 20})items = client.dataset(run['defaultDatasetId']).list_items().items
Schedules, webhooks and no-code tools: save your input as a Task, add a Schedule (e.g. hourly or daily), and attach a webhook or one of the Integrations tab's connectors (Make, Zapier, n8n, Google Sheets, Slack) to push each run's results into your own system.
Only new items each run: set "onlyNewSinceLastRun": true on a scheduled Task and every run after the first delivers, and charges for, only items it hasn't delivered before for that same input. That makes it a ready-made change feed.
Reliability: an automated test runs this Actor's parser against the live site every day, and failures are fixed fast.
Feedback: an honest rating on the Store page, good or bad, helps others decide if this Actor fits their needs. Missing a field or found a problem? Open an issue on the Actor page.
FAQ
Does this collect seller personal information?
No. Private sellers appear only as sellerType: "private". Dealerships appear by their public trading name.
Can I filter by price and year?
Yes: priceFrom/priceTo (EUR) and yearFrom/yearTo (first registration). Every row is also checked against your range before delivery.
Can I use a Standvirtual search URL with filters I already set (fuel, body type, region)?
Yes - paste it into startUrls. Every filter in the URL is kept; only the page number is added.
How do I get alerted to new cars instead of the same cars every time?
Set onlyNewSinceLastRun: true, choose sortBy: "newest" and put the Actor on a schedule - see "New-ad alerts" above.
Why are some engineCc or version values null?
Standvirtual does not show them on every ad (electric cars have no engine capacity). The Actor never fills a gap with a guess.
Can I get Standvirtual alerts by email?
Not natively. There are two routes. (1) Set webhookUrl to a Zapier, Make or n8n catch hook and add an email or Gmail step: the webhook delivers the cars as JSON, so the email can list them. (2) Apify's own run integrations and run notifications can email you when a run finishes, but they only send a link to the run or dataset, not the cars themselves. For instant alerts without building anything, Telegram, ntfy or Pushover reach your phone directly.
Can I get Standvirtual alerts in WhatsApp?
Not natively, and we do not use unofficial WhatsApp APIs, which risk getting your number banned. The official route is the WhatsApp Business Platform (Cloud API) with its own business account, message templates and pricing: point webhookUrl at a Zapier, Make or n8n flow that calls it. For free phone alerts use Telegram or ntfy instead.
More from this publisher
Other data tools from the same developer, built to the same standard: public sources, hard cost caps, and honest documentation of limits.
- Vinted Scraper: Vinted search results with prices, brands, sizes and favourites, across any Vinted country, with new-listing alerts.
- Zillow ZIP Code Scraper: Zillow homes for sale, for rent and recently sold by ZIP code, with prices and property details.