Skip to main content

Complete Guide: How to Scrape Gumtree UK Listings Using Python

Run the Gumtree Scraper Apify Actor from Python, extract vehicle specs and seller types, and export clean JSON without browser maintenance.

Updated 11 min readMarkdown
Complete Guide: How to Scrape Gumtree UK Listings Using Python

This complete guide to Gumtree Scraper shows how to run a Gumtree classifieds scraper from Python through the Apify API and get back structured, typed listing records: listing titles, prices in British Pounds, geographic locations, vehicle attributes (year, mileage, engine size, fuel type, seller type), search-card descriptions (empty in this run's 200 car listings), and parsed device specifications.

Gumtree is a classifieds marketplace in the United Kingdom. A custom crawler attempting to harvest data from Gumtree meets two significant obstacles: unproxied requests are blocked after about five pages (while residential completed 25 of 25 pages and datacenter 22 of 25 in August 2026 testing), and its search pagination enforces a hard ceiling of 50 pages per query.

In this tutorial, you will learn how to automate Gumtree data extraction using the Gumtree Scraper Apify Actor (reventadata/gumtree-scraper). Prerequisites are minimal: Python 3.11+, the official apify-client package, and an Apify API token. Estimated time to complete: about 30 minutes. The live benchmark run supporting this guide (configured with searchQueries: ["bmw"], category: "cars", maxPrice: 5000, and maxItems: 200) collected 200 structured vehicle records in 141.0 seconds.


TL;DR

In this tutorial, you will learn to configure and trigger the Gumtree Scraper Apify Actor from Python, inspect run execution states, extract clean dataset items, and filter Trade listings from private sales. Key steps: install apify-client, export your APIFY_API_TOKEN, define target keywords and category slugs in run_input, call the Actor, and iterate over items in run.default_dataset_id.

Expected outcome: a JSON list of Gumtree classifieds with price, location, vehicle specs or parsed hardware attributes, ready to load into a spreadsheet, database, or analytics pipeline.

The Actor is available at apify.com/reventadata/gumtree-scraper.


Prerequisites and setup

Before running our extraction script, verify the following prerequisites:

To follow this tutorial, you will need:

  1. Python 3.11+ installed locally (apify-client 3.x requires it).
  2. The Apify API client for Python (pip install "apify-client~=3.0").
  3. An Apify account and API token.

An Apify account. Apify Console is the web interface where runs, datasets, and tokens are managed; your token lives on the API & Integrations page in Apify Console. The platform documentation index is at docs.apify.com.

Install the Apify client library via pip:

pip install "apify-client~=3.0"

Set your API token in your environment:

export APIFY_API_TOKEN="your_apify_token_here"

Understanding Gumtree Scraper

How it works behind the scenes

The Gumtree Scraper extracts structured listing records from the Gumtree UK classifieds platform:

  1. Category Routing & Pagination: The Actor accepts one of 11 category slugs per run: for-sale, cars, phones, mobile-phones, vans, motorbikes, property-to-rent, flats-houses, jobs, furniture, and bicycles. That list is the Actor's own allowlist, taken from the category filter links on gumtree.com when the Actor was built (August 2026); each slug is sent as Gumtree's search_category URL parameter. It is a short list of common categories, not Gumtree's full category tree. Any other slug is refused before the run starts. Pagination respects Gumtree's 50-page ceiling—with about 30 listing tiles per page, a search term yields about 1,500 ads.
  2. Residential Proxy Transport: In August 2026 testing, unproxied requests were blocked after about five pages, whereas residential completed 25 of 25 pages and datacenter 22 of 25. The Actor uses Apify residential proxies by default.
  3. Tile Extraction & Filtering: Gumtree server-renders each listing card with its price, location and, for vehicles, attribute values. The Actor extracts typed records, parses battery health and storage from the ad text via regular expressions, flags promoted (Featured, Urgent or Spotlight) cards (isFeatured), and applies keyword matching and exclusion filters before pushing records to the Apify dataset.

Step-by-step walkthrough

Step 1: Initialize the client and define inputs

In Python, initialize the client using your environment token. Configure run_input with your target search queries, category, and price parameters:

import os
from apify_client import ApifyClient

# 1. Initialize ApifyClient with token from environment
client = ApifyClient(os.environ["APIFY_API_TOKEN"])

# 2. Define the extraction parameters
run_input = {
    "searchQueries": ["bmw"],
    "category": "cars",
    "maxPrice": 5000,
    "maxItems": 20,
}

(Note: This demonstration snippet uses maxItems: 20 for a fast initial test. The benchmark run cited in this guide ran with searchQueries: ["bmw"], category: "cars", maxPrice: 5000, and maxItems: 200.)

Step 2: Trigger execution on Apify

Invoke the Actor using .call(run_input=run_input). This blocks until the crawler finishes, streaming progress updates:

# 3. Start the Actor run and wait for it to complete
print("Starting Gumtree UK extraction...")
run = client.actor("reventadata/gumtree-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. Fetch the resulting dataset items
dataset = client.dataset(run.default_dataset_id).list_items().items

print(f"Extraction complete! Retrieved {len(dataset)} listings.\n")

Step 3: Iterate and process dataset items

Iterate through the returned items and access the extracted attributes:

for item in dataset:
    title = item.get("title")
    price = item.get("price")
    location = item.get("location")
    year = item.get("year")
    mileage = item.get("mileage")
    seller_type = item.get("sellerType")

    print(f"Listing: {title}")
    if price is None:
        print("  Price: unpriced")
    else:
        print(f"  Price: £{price}")
    print(f"  Location: {location}")
    if year:
        miles = f"{mileage} miles" if mileage is not None else "mileage not stated"
        print(f"  Vehicle: {year} ({miles})")
    if seller_type:
        print(f"  Seller: {seller_type}")
    print("-" * 50)

Step 4: Inspect real dataset output

Here is a sample record extracted during our benchmark run:

{
    "id": "1802688843",
    "title": "BMW, X1, Estate, 2011, Manual, 1995 (cc), 5 doors",
    "description": "",
    "url": "https://www.gumtree.com/p/bmw/bmw-x1-estate-2011-manual-1995-cc-5-doors/1802688843",
    "imageUrl": "https://img.gumtree.com/ePR8PyKf84wPHx7_RYmEag/dc7ed13c2c2a4a2993_52814dfe9ce4/86",
    "price": 2250,
    "currency": "GBP",
    "location": "Ballyclare, County Antrim",
    "sellerType": "Private",
    "isFeatured": true,
    "photoCount": 19,
    "year": 2011,
    "mileage": 153948,
    "engineCc": 1995,
    "fuelType": "Diesel",
    "batteryHealth": null,
    "storageGb": null,
    "storageOptionsGb": [],
    "isMultiVariant": false,
    "matchedKeyword": "bmw"
}

Understanding your results

The dataset fields map directly to Gumtree listing attributes:

  • id: Gumtree listing identifier string.
  • title: Ad title as written by the seller.
  • description: The search-card text, empty in this run's 200 car listings.
  • url: Canonical link to the ad on Gumtree.
  • imageUrl: First thumbnail URL, or null.
  • price: Asking price in GBP (numeric, or null if unpriced).
  • currency: Always "GBP".
  • location: Town and county from the listing card.
  • sellerType: "Private" or "Trade" (motoring only; null otherwise).
  • isFeatured: Boolean flag for promoted (Featured, Urgent or Spotlight) ads.
  • photoCount: Number of photos on the ad.
  • year: Vehicle year. Motoring categories only.
  • mileage: Odometer reading in miles (motoring only).
  • engineCc: Engine displacement in cc (motoring only).
  • fuelType: Stated fuel type seen in this run ("Diesel", "Petrol", "Hybrid Electric").
  • batteryHealth: Parsed battery percentage, or null.
  • storageGb: Storage in GB, only when the ad states exactly one capacity.
  • storageOptionsGb: Array of detected storage capacities.
  • isMultiVariant: True when one ad sells several capacities at several prices, so the tile price belongs to one unstated variant.
  • matchedKeyword: Search query that matched this listing.

Working with the API

You can automate Gumtree data extraction programmatically by integrating the Apify API into backend services, ETL pipelines, or scheduled workflows.

When initiating extractions from code, the Apify Python client provides two primary execution methods: .call() and .start(). Calling client.actor("reventadata/gumtree-scraper").call(run_input=run_input) triggers the container and blocks until execution finishes, returning the settled run record. For asynchronous or distributed workflows where holding open an HTTP connection is impractical, invoke client.actor("reventadata/gumtree-scraper").start(run_input=run_input) instead. This initiates the container on Apify infrastructure and immediately returns a run object containing the run ID and execution status.

To read the extracted data once execution finishes, query the default dataset through client.dataset(dataset_id).list_items().items or stream records using iterate_items(). The dataset retains clean, structured JSON items matching the schema detailed above.

For event-driven architectures, add an Apify webhook to the run when you start it through the API. You can configure the webhook to fire on the ACTOR.RUN.SUCCEEDED event. When the Actor completes, Apify sends an HTTP POST request containing the run object to your application endpoint. A webhook carries the run object whose defaultDatasetId you read to fetch and process dataset items without manual polling. Always inspect the run status in your webhook handler or response object to confirm successful execution before reading rows.


Advanced usage patterns

1. Batch keyword processing across multiple queries

Instead of starting separate Actor runs for each keyword, pass multiple search terms in searchQueries (for example, ["bmw", "audi", "mercedes"]). The Actor iterates through each search query sequentially within a single run, consolidating records into one default dataset instead of one per run.

2. Scheduled runs and automated monitoring

To rerun a search on a timetable, set up an Apify schedule that runs this Actor or a saved task. A schedule runs an Actor or task on a cron expression (see docs.apify.com/platform/schedules). Combine schedules with webhooks configured for ACTOR.RUN.SUCCEEDED to alert downstream services or trigger ingestion pipelines whenever a run succeeds.

3. Filtering Trade sellers and promoted listings

In motoring searches, separate private sellers from Trade sellers by setting "sellerType": "private". This filter drops Trade sellers, as Gumtree labels them, inside the Actor after fetching. To exclude promoted (Featured, Urgent or Spotlight) placements (five sit at the top of each page measured: 1, 2, 3 and 5), pass "excludeFeatured": true.

4. Cost optimisation and volume bounds

Always pass maxItems to cap total output. maxItems and maxPages cap a crawl; minPrice and maxPrice only narrow the search Gumtree returns.


Best practices

  1. Respect Pagination Limits: Gumtree caps searches at 50 pages (about 1,500 ads per search). Partition broad queries across locations (e.g., "london" or "manchester") or price brackets (minPrice, maxPrice) to split one broad search into several smaller ones.
  2. Set maxItems to Control Spending: Always set maxItems to bound extraction to the exact volume your workflow requires.
  3. Use Valid Category Slugs: Pass recognized category slugs such as "cars", "phones", or "property-to-rent", from the 11 the input accepts; the platform refuses the input before the run starts for any other.
  4. Leverage Negation Filtering: Keep excludeDamaged active to drop damaged goods while preserving listings with negations like "no cracks" (note that negation applies only to damage checks).
  5. Schedule Recurring Runs: Save the input as a task and add an Apify schedule to rerun the same search on a cron expression.

Troubleshooting guide

  • Zero items returned / Run failed: Gumtree Scraper raises an error if zero items are saved. This intentional failure prevents automated pipelines from treating empty runs as successful. If your run fails with zero items, check whether minPrice, maxPrice, or sellerType filters were too restrictive, or if query terms matched no active listings.
  • sellerType is null for all records: Gumtree exposes seller classification (Private or Trade) only for motoring categories. In non-motoring searches (such as phones, furniture, or jobs), sellerType is expected to return null.
  • Unexpected or accessory items in results: Gumtree search is fuzzy; searching for a device can return accessories. Keep requireKeywordMatch set to true (the default) so items missing any search term are dropped. For electronics, keep excludeAccessories enabled to filter out cases and chargers.
  • Invalid category slug error: If the run does not start, check that category is one of the Actor's 11 accepted slugs; the platform refuses the input before the run starts for any other value.
  • Reading the log: the last summary lines are Rejected N: reason (when any were) and Pushed N row(s).; read it from Python with client.log(run.id).get(). For help, see the Store page (https://apify.com/reventadata/gumtree-scraper).

Pricing model

For current pricing and details, see the Store page.


Conclusion and next steps

With Gumtree Scraper and the Apify Python client, you can extract structured classifieds data across UK motoring, electronics, and rental listings without managing rotating proxies or browser sessions.

To take your extraction pipeline further, consider these next steps:

  • Schedule the same search to rerun, then compare the datasets yourself.
  • Export dataset records to analytics databases or spreadsheets to analyze vehicle depreciation and market trends.
  • Set up automated webhook listeners to trigger downstream processing when runs complete.

Try Gumtree Scraper yourself and let me know what you build!

I build and maintain marketplace scrapers under the reventadata name on the Apify Store.


Resources

About the author

  • Hi, I’m the engineer behind ReventaData. I build automated web scrapers, reverse-engineer undocumented APIs, and design data pipelines on Apify that turn complex, unstructured websites into clean, reliable feeds. Here on Hashnode, I share practical tutorials on modern web scraping with Python, bypassing aggressive anti-bot protections (Cloudflare, Akamai, PerimeterX), and data pipeline automation, reverse-engineering hidden endpoints, and automating data extraction. Explore our turnkey scrapers: https://apify.com/reventadata

honestly the python version and the default_dataset_id thing look like they're just copied from the v3 docs, but the 11 category slugs are a different story, nobody says where that list comes from. the keyword matching part is fine since you can turn it off, but the guide really should warn about a title like "BMW 320d M Sport" falling out of a "bmw 3 series" search. same goes for a narrow slice coming back empty and tripping the fail-on-empty error, that bit would’ve saved me a lot of debugging

Fair point, thanks. The 11 slugs are the Actor's own allowlist, taken from the category filter links on gumtree.com when it was built in August. Each one is sent as Gumtree's search_category URL parameter. It's a short list of common categories, not Gumtree's full tree, and only cars was benchmarked for this post. The allowlist exists so a typo fails the run instead of maybe returning an unfiltered search that looks like success. I've added this to the post.

More from this blog