By the end of this tutorial you will have a Python script that pulls used car listings from autotrader.co.uk into structured rows: title, year, mileage, asking price as a number, seller type, dealer, location with its distance in miles, and photo links.

Autotrader UK is a UK car marketplace. Autotrader UK Scraper (reventadata/autotrader-uk-scraper) is the Apify Actor built for this site. You send it a search, and it returns one row per advert. The script below runs it from Python with the Apify API client, and the sample rows come from a real run of the Actor's public example task (BMW, 10 rows, one page) on 2026-11-15, not from an invented example.

Prerequisites

You need three things:

  1. Python 3.11 or newer (the Apify client for Python 3.x requires 3.11).
  2. A free Apify account, which you can create with an email address or a GitHub login.
  3. Your API token, from Settings > API & Integrations in Apify Console.

Install the client and export the token:

pip install -U "apify-client~=3.0" export APIFY_API_TOKEN="your_apify_token_here" 

No scraping code is needed on your side. The Actor does the fetching from public pages, with no login.

What you'll achieve

You will search autotrader.co.uk for BMWs, read the rows in Python, and print two lines per car: the title and trim, year, mileage, price, seller type and location on the first line, and the site's price rating and the dealer rating on the second.

The same script works for a buyer comparing asking prices for one model, a dealer watching the private sellers in an area, or an analyst building a table of mileage against price. Only the input changes.

Step 1: Open Autotrader UK Scraper

Open the Autotrader UK Scraper page on Apify Store. The Try for free button there opens the Actor in Apify Console. This tutorial uses the API from Python, so all you need is the Actor name, reventadata/autotrader-uk-scraper.

Step 2: Choose the search

The search is described by a handful of input fields, all optional:

  • make and model: spelled as Autotrader spells them, for example BMW and 3 Series. A model needs a make; a model without one ends the run with a message asking for both.
  • minPrice and maxPrice: in pounds.
  • minYear and maxYear: year of manufacture.
  • maxMileage: the highest mileage, in miles.
  • fuelType: Petrol or Diesel. Any other value is refused before the run starts.
  • sellerType: any (the default), private or trade.
  • postcode and distance: a UK postcode and a radius in miles.
  • sortBy: relevance (the site's own order) or price_asc (labelled "Cheapest first" in the input form).
  • maxItems and maxPages: caps on rows and on result pages.

The postcode defaults to SW1A 1AA. On its own it does not narrow the search: without a distance, cars from all over the UK come back, and each row says how far it is from your postcode.

To look for diesel 3 Series from trade sellers within 100 miles of Manchester, priced 5,000 to 6,000 pounds and sorted with price_asc, the input would look like this:

{ "make": "BMW", "model": "3 Series", "minPrice": 5000, "maxPrice": 6000, "fuelType": "Diesel", "sellerType": "trade", "postcode": "M1 1AE", "distance": 100, "sortBy": "price_asc", "maxItems": 20, "maxPages": 1 } 

Step 3: Run it from Python

This script starts the Actor, waits for it, reads the dataset and prints two lines per car:

import os from apify_client import ApifyClient # 1. Initialise the client with your token from the environment client = ApifyClient(os.environ["APIFY_API_TOKEN"]) # 2. Describe the search: BMW, 10 rows, one page of results run_input = { "make": "BMW", "maxItems": 10, "maxPages": 1, } # 3. Start the Actor and wait for it to finish run = client.actor("reventadata/autotrader-uk-scraper").call(run_input=run_input) if run is None or run.status != "SUCCEEDED": status = run.status if run else "unknown" raise SystemExit(f"Run did not succeed (status: {status}).") # 4. Read the rows from the run's default dataset items = client.dataset(run.default_dataset_id).list_items().items print(f"Retrieved {len(items)} cars.\n") # 5. Print two lines per car; mileage and dealer rating can be empty for item in items: mileage = item.get("mileage") miles = f"{mileage:,} miles" if mileage is not None else "mileage not shown" print(f"{item['title']} {item['subTitle']} | {item['year']} | {miles} | " f"£{item['price']:,.0f} | {item['sellerType']} | {item['location']}") rating = item.get("dealerRating") print(f" price rating: {item['priceRating']}, " f"dealer rating: {rating if rating is not None else 'none'}") 

Keep maxPages small while you test: each result page holds 20 cars.

Step 4: Read the output

The loop prints one block per car. This is what it printed for the first three of the ten rows captured on the Actor's example run of 2026-11-15:

BMW 3 Series 2.0 320i M Sport Euro 5 (s/s) 2dr | 2012 | 71,000 miles | £2,495 | TRADE | Wembley (9 miles) price rating: LOW, dealer rating: 4.9 BMW 3 Series 2.0 318Ci 318 Sport Auto 2dr | 2003 | 103,000 miles | £1,200 | PRIVATE | Stepney (4 miles) price rating: NOANALYSIS, dealer rating: none BMW 5 Series 2.0 518d SE Euro 6 (s/s) 4dr | 2014 | 91,274 miles | £2,200 | PRIVATE | London (2 miles) price rating: NOANALYSIS, dealer rating: none 

The second car is a private sale, so it has no dealer rating and the script prints none. The Actor reports a missing value as null and does not guess one.

Step 5: Add specification and seller details (optional)

Set includeDetails to true and the Actor also opens each car's own page. The rows then carry extra fields: description, engine size, gearbox, body type, fuel type, doors, seats, colour, owners, registration date, CO2, insurance group, annual tax, fuel economy, performance figures, the feature list, and the seller's name, phone, email, address and website as the seller published them.

{ "make": "BMW", "model": "3 Series", "maxItems": 5, "maxPages": 1, "includeDetails": true } 

It costs one extra page read per car, so the run is slower. If a car's page cannot be read, that row still ships with its card fields only, and the run log says how many detail pages failed. The ten sample rows in this post were captured with this switch off, so they carry no detail fields.

Understanding your results

Every card row carries these fields. The ones you will use most:

  • price (a number) and priceText (as shown, such as £2,495), plus currency, always GBP.
  • year, registration (such as 2012 (61 reg)), mileage (an integer, null when the advert shows none) and condition.
  • title and subTitle (engine, trim, gearbox, doors), make, model.
  • sellerType (PRIVATE or TRADE), and for trade sellers dealerId, dealerUrl and dealerRating.
  • location and distanceMiles from your postcode.
  • priceRating (the site's indicator, such as LOW, GREAT or NOANALYSIS), writeOffCategory, badges and attentionGrabber.
  • numberOfImages, imageUrl, imageUrls (800 px wide) and scrapedAt.

One complete row from the example run:

{ "id": "202609216219216", "url": "https://www.autotrader.co.uk/car-details/202609216219216", "title": "BMW 3 Series", "subTitle": "2.0 320i M Sport Euro 5 (s/s) 2dr", "make": "BMW", "model": "3 Series", "year": 2012, "registration": "2012 (61 reg)", "mileage": 71000, "price": 2495, "priceText": "£2,495", "currency": "GBP", "priceRating": "LOW", "writeOffCategory": null, "badges": [ "Lower price" ], "attentionGrabber": "Great spec", "condition": "used", "sellerType": "TRADE", "dealerId": "10049986", "dealerUrl": "https://www.autotrader.co.uk/dealers/middlesex/wembley/borz-motors-10049986?channel=cars", "dealerRating": 4.9, "location": "Wembley (9 miles)", "distanceMiles": 9, "isManufacturerApproved": false, "isFranchiseApproved": false, "isPreRegistered": false, "numberOfImages": 30, "imageUrl": "https://m.atcdn.co.uk/a/media/w800/13e69f7c20684d63b5c8dd5eaa3cb6ea.jpg", "imageUrls": [ "https://m.atcdn.co.uk/a/media/w800/13e69f7c20684d63b5c8dd5eaa3cb6ea.jpg", "https://m.atcdn.co.uk/a/media/w800/434af1c28f5e4d79a8d7727ff754ffe0.jpg", "https://m.atcdn.co.uk/a/media/w800/6d2b7b3bc3154f43bbb4fccb0b94d0b8.jpg", "https://m.atcdn.co.uk/a/media/w800/892c2763950a4b3a84c4e8dc20eecf89.jpg" ], "scrapedAt": "2026-11-15T19:31:36+00:00" } 

In these ten rows, the six TRADE rows carry a dealer id, dealer URL and dealer rating, and the four PRIVATE rows have null in all three. writeOffCategory is null on all ten.

Pro tips

  1. Pick the sort. price_asc is the option labelled "Cheapest first"; relevance follows the site's own order.
  2. Use a distance when location matters. Without it the search covers the whole UK. In the example rows distanceMiles ranges from 2 to 359.
  3. Split large searches. Autotrader serves at most 100 pages of 20 adverts, 2,000 per search. A search that matches more returns the first 2,000 and the run log says so; narrow it by model, price band, year band, or postcode and distance.
  4. Treat null as a value. Check mileage and dealerRating before arithmetic, as the script does.
  5. Leave includeDetails off until you need it. Card fields are enough for price and mileage comparisons.

Troubleshooting

  • Zero rows, status SUCCEEDED. The site answered but nothing matched. The status message says so; check the spelling of make and model as Autotrader spells them, or loosen a filter.
  • Zero rows, status FAILED. A model without a make, a postcode that is not a UK postcode, or a minimum above its maximum ends the run with a message to fix the input.
  • Fewer rows than expected. Check maxItems and maxPages, then read the run log with client.log(run.id).get().

Autotrader's terms of use restrict copying or extracting content from the site, so check that your use of the data complies with them and with data-protection law.

Next steps

For current pricing and details, see the Store page.

To repeat a search, save it as an Apify task and attach a schedule, which runs an Actor or task on a cron expression. Datasets export as JSON, CSV, XML, Excel, HTML table, RSS or JSONL (dataset docs), and a webhook can notify your application when a run finishes (webhook docs).

What are you planning to search for on Autotrader? Drop a comment! 💬

About the author: reventadata builds and publishes data extraction Actors on the Apify Store.