# 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](https://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](https://docs.apify.com/).

Install the Apify client library via pip:

```bash
pip install "apify-client~=3.0"
```

Set your API token in your environment:

```bash
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](http://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:

```python
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:

```python
# 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:

```python
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:

```json
{
    "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](https://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 ends the run `FAILED` when zero items are saved because a page was blocked or could not be read. A search that loads normally but matches nothing ends `SUCCEEDED` with an empty dataset and a status message saying so. If you get 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 summary lines near the end 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](https://apify.com/reventadata/gumtree-scraper).

---

## 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

- [Gumtree Scraper on Apify Store](https://apify.com/reventadata/gumtree-scraper)
- [Apify Python Client Documentation](https://docs.apify.com/api/client/python/)
