# Gumtree UK Scraper - Classified Ads & Listings (`dami_studio/gumtree-uk-scraper`) Actor

Scrape Gumtree UK classifieds: ad id, title, price, category, town and postcode, seller type, posted date, full ad text, attributes and photos. Search by keyword, category, town or postcode, or paste a Gumtree URL. No login, no API key. Phone numbers and emails are stripped from every field.

- **URL**: https://apify.com/dami\_studio/gumtree-uk-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** E-commerce, Lead generation, Real estate
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.60 / 1,000 listing returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Gumtree UK Scraper

Pulls listings off Gumtree UK and writes one dataset row per ad: the ad id, title, price, category,
town, seller type, posted date, description, photos and the listing URL.

Search by keyword, by category, by town or postcode, or just paste the Gumtree search URL you
already have open in a tab. No account, no cookies, no API key.

**Phone numbers and email addresses are stripped out of every text field before the row is
written.** Sellers type their mobile into the ad body constantly and Gumtree does nothing about it,
so this Actor does. More on that below, including what it means for your data.

***

### What you get

Two modes, and the difference between them is worth understanding before you start a big run.

**Search results only** (the default) is fast: roughly 30 ads per page load. You get the id, title,
price, town, the short description Gumtree shows on the card, the thumbnail, the photo count and the
listing URL. On Cars, Vans and Motorbikes you also get year, mileage, fuel, engine size and whether
the seller is private or trade, because Gumtree puts a spec strip on those cards.

**Open each listing page** (`fetchDetails: true`) adds the things that only exist on the ad itself:
the full description, the attribute table, seller type and seller name, the exact posted date, the
whole photo set and, on property ads, the postcode and map coordinates.

It costs one extra page load per listing. A 50-listing run took **51 seconds** with it on and about
**4 seconds** with it off, measured on this Actor. If you are monitoring a category for new ads,
leave it off. If you are building a dataset you will analyse later, turn it on.

#### A real row

```json
{
  "listingId": "1802135686",
  "title": "2015 Ford Focus 2.0T EcoBoost ST-2 Euro 6 (s/s) 5dr HATCHBACK Petrol Manual",
  "price": 6695,
  "priceText": "£6,695",
  "currency": "GBP",
  "description": "Full service history, two keys, HPI clear. MOT until August.",
  "descriptionIsTruncated": false,
  "categorySlug": "ford",
  "categoryName": "Ford FOCUS",
  "categoryPath": "Motors > Cars > Ford > Ford FOCUS",
  "location": "Headingley, West Yorkshire",
  "postcode": null,
  "postcodeArea": null,
  "postcodeOutward": null,
  "latitude": null,
  "longitude": null,
  "sellerType": "trade",
  "sellerName": "LEEDS AUTO SALES LTD",
  "condition": "Used",
  "postedAt": "2026-09-18T14:02:11.000Z",
  "postedAtIsApproximate": true,
  "postedText": "1 day ago",
  "expiresAt": null,
  "attributes": {
    "Seller Type": "Trade",
    "Posted": "1 day ago",
    "Year": "2015",
    "Mileage": "103,149 miles",
    "Body Type": "Hatchback",
    "Transmission": "Manual",
    "Colour": "Blue",
    "Seats": "5",
    "Doors": "5"
  },
  "images": ["https://img.gumtree.com/ePR8PyKf84wPHx7_RYmEag/..."],
  "imageCount": 35,
  "isFeatured": false,
  "url": "https://www.gumtree.com/p/ford/2015-ford-focus-.../1802135686",
  "searchUrl": "https://www.gumtree.com/search?search_location=leeds&search_category=cars&q=ford+focus",
  "scrapedAt": "2026-09-19T14:02:11.000Z",
  "source": "search+listing"
}
```

A property ad fills in `postcode`, `postcodeArea`, `postcodeOutward`, `latitude` and `longitude`.
A job ad has no price at all, so `price` is `null` and the salary, hours and contract type turn up in
`attributes`.

***

### Input

The minimum is a location, or a search term, or a category. Anything else is optional.

```json
{
  "query": "ford focus",
  "location": ["leeds"],
  "category": ["cars"],
  "maxItems": 200,
  "distanceMiles": 30,
  "sort": "date",
  "sellerType": "private",
  "fetchDetails": true
}
```

Or paste URLs and skip the form:

```json
{
  "searchUrls": [
    "https://www.gumtree.com/search?search_category=property-to-rent&search_location=manchester",
    "https://www.gumtree.com/cars-vans-motorbikes/cars/uk/leeds"
  ],
  "maxItems": 500
}
```

Both URL shapes work: the `/search?…` form and the browse path. Filters already in the URL are
kept; anything you set in the form overrides them.

| Field | What it does |
|---|---|
| `query` | Keywords. Leave empty to take the whole category. |
| `location` | A town, city, county or postcode. `"uk"` for the whole country. Several run the same search in several places. |
| `category` | A section, a leaf category, or a plain label. 115 leaf categories are recognised. |
| `searchUrls` | Paste Gumtree URLs instead of filling the fields. |
| `maxItems` | Total listings across every search. One listing is one charged result. |
| `fetchDetails` | Open each listing page for the full record. |
| `sort` | Most relevant, newest, cheapest or dearest. |
| `distanceMiles` | Radius around the location. Gumtree's own default is narrow, so widening this is usually the quickest way to more results. |
| `minPrice` / `maxPrice` | Price band in pounds. See the caveat below. |
| `sellerType` | Private or trade. |
| `includeFeatured` | Keep Gumtree's paid "Featured" cards. Off by default. |

#### Categories

Pass a section (`cars-vans-motorbikes`, `for-sale`, `flats-houses`, `pets`, `jobs`, `community`,
`business-services`), a leaf slug (`cars`, `property-to-rent`, `dogs`, `phones`, `home-garden`,
`video-games-consoles`, `music-instruments`, `freebies`), or a plain label. `"Cars"`,
`"Flats to rent"`, `"Puppies"` all resolve. If a name is not recognised the run writes an uncharged
row telling you so instead of quietly searching everything.

***

### What this does not do

This is the section people skip and then file a support ticket about, so it is near the top.

**Search results carry no posted date.** Gumtree does not put one on the cards. If you need to know
when an ad went up, turn on `fetchDetails`. The one exception is that sorting by newest gets you the
ads in date order even without the timestamps.

**Some posted dates are approximate.** Motors ads and a lot of for-sale ads render the age as text
rather than a date: "17 mins ago", "72 days ago". Those are converted to a timestamp relative to
when the run happened, and the row carries `postedAtIsApproximate: true` so you can tell which ones
are exact and which are derived. Property ads and most for-sale ads carry a real date and come back
exact.

**A price range only narrows the search inside a category.** On an all-category keyword search
Gumtree accepts `min_price` and `max_price` and then ignores them. Rather than hand you rows outside
the band you asked for, this Actor drops them itself and does not charge for them, but it means a
tightly-banded all-category search returns fewer rows per page than you might expect. Pick a
category and Gumtree does the filtering properly.

**`sellerType` only applies where Gumtree has both.** Cars, Vans, Property and the other sections
with a private/trade split honour it. An all-category search ignores it.

**Asking for any sort other than relevance narrows the result set.** This is Gumtree's behaviour,
not a bug here: a "sofa in London" search reports about 8,900 ads sorted by relevance and about 2,700
sorted by date, because relevance mode pulls in adjacent categories and date mode does not.

**30 results per page, and that is fixed.** Gumtree accepts `pageSize`, `size` and `perPage` and
ignores all three. Deep runs are page loads, so a 1,000-listing pull is about 34 of them.

**An unknown town is not an error.** Gumtree answers "0 ads in zzzznotaplace" with a normal page, so
a typo in the location shows up as an empty run. When a search returns nothing the Actor writes an
uncharged row saying so and reminding you to check the spelling.

**Postcodes and coordinates are property-only, and the postcode depends on the agent.** Property ads
carry map coordinates reliably; whether they carry a postcode depends on what the letting agent
filled in, so expect it on some and not others. Other sections give you the town and neighbourhood
("Headingley, West Yorkshire") and nothing more precise, because that is all Gumtree publishes. When
a postcode does appear in a title or a location line it is picked up anyway and split into
`postcode` / `postcodeOutward` / `postcodeArea`.

**Jobs have no price.** `price` and `priceText` are `null` on job ads; pay shows up in `attributes`
when the advertiser filled it in, which many do not.

**Featured ads are skipped by default.** Gumtree pins a handful of paid cards to the top of every
page of a search, so the same advert reappears on page 1, page 2 and page 3. They are dropped and not
charged. Set `includeFeatured: true` if you are specifically tracking paid placements.

**Gumtree sometimes asks a request to prove it is a browser.** It is rare, and the Actor retries
and carries on. If a page genuinely cannot be fetched, the run writes an uncharged
row naming the search and the page number rather than pretending the search was empty.

***

### Contact details are removed

Classifieds are written by people who want to be phoned, so ad bodies are full of mobile numbers and
email addresses, often deliberately mangled to get past Gumtree's own filter. Every string this
Actor emits is scanned before it is written, and anything that looks like a phone number or an email
address is replaced with `[removed]`.

What it looks for: email addresses in plain form and obfuscated form (`name (at) example dot com`,
`name[at]example.co.uk`); UK mobile and landline numbers in every separator style, with or without
the leading zero, with or without `+44`; and any run of eleven or more digits sitting in ad copy.

It errs on the side of removing too much. A redacted product code is a nuisance; a published mobile
number is somebody's phone ringing. Prices, years, mileages, dimensions, postcodes and Gumtree's own
ad ids are left alone. Those were checked against real listings, not assumed.

Gumtree already masks the number in its own contact panel (`0788275XXXX`) and this Actor does not
collect it, the seller's email, or the token behind the "reveal number" button. If you need to
contact a seller, use the listing URL in the row and Gumtree's own message button.

The run log tells you what was removed: *"Removed contact details from 8 listing(s): 8 phone
number(s) and 0 email address(es)."*

***

### Billing

One returned listing is one charged result. That is the whole model.

Nothing else bills:

- the sample row you get back from an empty run
- diagnostic rows: an unknown category, a search that found nothing, a page that did not load
- paid "Featured" cards that were skipped
- rows dropped because they fell outside the price range you asked for

So a run that finds nothing costs you the run fee and nothing more, and a run that returns 87
listings charges for 87.

`maxItems` is a hard ceiling, so it is also your budget control: set it to what you are willing to
pay for and the run stops there.

***

### FAQ

**Does this need a Gumtree account or an API key?**
No. It reads the same public pages a signed-out visitor sees. There is no login, no cookie and no key
to supply.

**Can it scrape Gumtree Australia or South Africa?**
No. This one is Gumtree UK (`gumtree.com`) only. The other Gumtree sites run on different software
and would need a different Actor.

**How do I get the full ad text instead of the two-line summary?**
Set `fetchDetails` to `true`. The search page only carries a truncated snippet; the full body only
exists on the listing page.

**How many listings can I get from one search?**
Gumtree serves 30 per page and paginates deep, so several hundred from one search is routine. If you
need tens of thousands, split the search by town, by sub-category or by price band rather than
paging one broad search forever. Narrower searches also return fresher, more relevant ads.

**Can I monitor a category for new ads?**
Yes. Set `sort` to `date`, keep `maxItems` small, and run it on a schedule. Each row carries
`listingId`, so de-duplicating against what you already have is a single comparison.

**Why is the seller's phone number missing?**
It is removed on purpose. See the section above. Gumtree masks it too.

**Why did my run return one row that says `_sample`?**
That is what an empty input produces: a labelled example row so you can see the output shape. It is
not charged. Fill in a location, a search term or a category and run it again.

**Why does `postedAt` say `2026-09-19T14:02:11Z` with `postedAtIsApproximate: true`?**
Because the ad rendered "1 day ago" rather than a date, so the timestamp was worked out from when the
run happened. Treat it as accurate to the unit Gumtree showed.

**Can I use my own proxies?**
Yes. Put them in `proxyConfiguration.proxyUrls` and they are used exactly as given. You do not need
to.

**What happens if Gumtree blocks a page?**
The request is retried. If it still cannot get through, you get an
uncharged row naming the search and the page, so a partial run is visible rather than silent.

***

### Output fields

| Field | Notes |
|---|---|
| `listingId` | Gumtree's own ad id, from the listing URL |
| `title` | |
| `price` | Number in pounds. `0` for a free listing, `null` for jobs and "please contact" ads |
| `priceText` | As shown: `"£6,695"`, `"£600pm"`, `"Free"` |
| `currency` | `"GBP"` when there is a price |
| `description` | Card snippet in search mode, full body with `fetchDetails`. Contact details removed |
| `descriptionIsTruncated` | `true` when it is the card snippet |
| `categorySlug` | From the listing URL |
| `categoryName`, `categoryPath` | The breadcrumb Gumtree shows. Listing pass only |
| `location` | Town and county as Gumtree prints it |
| `postcode`, `postcodeOutward`, `postcodeArea` | Property ads, and anywhere a postcode is written into the text |
| `latitude`, `longitude` | Property ads |
| `sellerType` | `private`, `trade`, `agency` or `landlord`. On motors this comes from the search card; elsewhere it needs the listing pass |
| `sellerName` | The seller's display name on Gumtree. Listing pass only |
| `condition` | `New` or `Used` where Gumtree states it |
| `postedAt`, `postedAtIsApproximate`, `postedText` | Listing pass only |
| `expiresAt` | When Gumtree publishes one |
| `attributes` | Whatever the category has: mileage, bedrooms, breed, hours, engine size |
| `images`, `imageCount` | One thumbnail in search mode, the full set with `fetchDetails`. `imageCount` is the number of photos on the ad either way |
| `isFeatured` | `true` for Gumtree's paid placements |
| `url`, `searchUrl`, `searchPage` | The listing, and the search it came from |
| `scrapedAt`, `source` | `"search"` or `"search+listing"` |

Diagnostic rows carry `_diagnostic: true`, `charged: false` and an `errorCode`:
`BAD_INPUT`, `NO_RESULTS`, `NO_NEW_RESULTS`, `BLOCKED`, `BOT_CHECK` or `REQUEST_FAILED`, plus a
plain-English `error` and `hint`. Filter on `ok === true` if you only want listings.

***

### Legal

This reads pages that Gumtree serves publicly to anyone, signed out. It does not log in, does not
create accounts, and does not touch anything behind Gumtree's contact-reveal button. Personal contact
details are removed from the output rather than collected.

You are responsible for what you do with the data. If you are in the UK or the EU and you intend to
keep anything that identifies a person, that is your processing to justify under UK GDPR, not
Gumtree's and not this Actor's.

# Actor input Schema

## `query` (type: `string`):

What to look for, e.g. "ford focus", "sofa", "2 bed flat". Leave it empty to take everything in the category you pick.

## `location` (type: `array`):

A UK town, city, county or postcode — "london", "leeds", "west-midlands", "OL8 3TS". Use "uk" for the whole country. Add several to run the same search in several places. Gumtree answers an unknown place with zero ads rather than an error, so a typo shows up as an empty run.

## `category` (type: `array`):

Optional. A section ("cars-vans-motorbikes", "for-sale", "flats-houses", "pets", "jobs", "community", "business-services"), a leaf category ("cars", "property-to-rent", "dogs", "phones", "home-garden", "video-games-consoles"), or a plain label like "Cars" or "Flats to rent". 115 leaf categories are recognised. Leave empty to search everything.

## `searchUrls` (type: `array`):

Optional. Paste Gumtree search or browse URLs instead of filling the fields above — https://www.gumtree.com/search?search\_category=cars\&search\_location=leeds or https://www.gumtree.com/cars-vans-motorbikes/cars/uk/leeds. Filters already in the URL are kept; anything you set above overrides them.

## `maxItems` (type: `integer`):

How many listings to return in total across every search (1-50000). One returned listing is one charged result. Sample and diagnostic rows are never charged.

## `fetchDetails` (type: `boolean`):

Adds the full ad text, the attribute table (mileage, bedrooms, breed, hours), seller type, exact posted date, the full photo set and — on property ads — the postcode and map coordinates. Costs one extra page load per listing, so a 100-listing run takes roughly 90 seconds instead of 10.

## `sort` (type: `string`):

Newest first is the one to use for monitoring. Note that asking Gumtree for any order other than relevance also narrows the result set it will serve.

## `distanceMiles` (type: `integer`):

Optional. How far around the location to search. Gumtree's own default radius is small, so widening this is usually the fastest way to get more results.

## `minPrice` (type: `integer`):

Optional. Gumtree only applies a price range when a category is set — on an all-category keyword search it accepts the range and ignores it, so this Actor drops the out-of-range rows itself and does not charge for them.

## `maxPrice` (type: `integer`):

Optional upper bound. Same caveat as the minimum.

## `sellerType` (type: `string`):

Private sellers or trade sellers only. Gumtree applies this inside Cars, Vans, Property and the other sections that have both; it has no effect on an all-category search.

## `includeFeatured` (type: `boolean`):

Gumtree pins a handful of paid "Featured" cards to the top of every page of a search, so the same advert comes back on page 1, page 2 and page 3. They are skipped by default and not charged. Turn this on if you are tracking paid placements.

## `proxyConfiguration` (type: `object`):

Optional. Leave this alone unless you need the run to go out through a particular network. Your own proxy servers are used exactly as given.

## Actor input object example

```json
{
  "query": "sofa",
  "location": [
    "london"
  ],
  "category": [],
  "searchUrls": [],
  "maxItems": 100,
  "sort": "relevance",
  "distanceMiles": 30,
  "sellerType": "any",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (type: `string`):

One row per Gumtree UK ad: id, title, price, category path, town and postcode area, seller type and name, posted date, description, attributes, photo URLs and the listing URL. Turning on "Open each listing page" fills in the full ad text, the attribute table, the exact posted date and — on property ads — the postcode and coordinates. Phone numbers and email addresses are stripped from every text field before the row is written. Empty input, an unknown category or a search that returns nothing writes an uncharged sample or diagnostic row instead.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "query": "sofa",
    "location": [
        "london"
    ],
    "category": [],
    "searchUrls": [],
    "maxItems": 100,
    "fetchDetails": false,
    "sort": "relevance",
    "distanceMiles": 30,
    "sellerType": "any",
    "includeFeatured": false,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/gumtree-uk-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "query": "sofa",
    "location": ["london"],
    "category": [],
    "searchUrls": [],
    "maxItems": 100,
    "fetchDetails": False,
    "sort": "relevance",
    "distanceMiles": 30,
    "sellerType": "any",
    "includeFeatured": False,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/gumtree-uk-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "query": "sofa",
  "location": [
    "london"
  ],
  "category": [],
  "searchUrls": [],
  "maxItems": 100,
  "fetchDetails": false,
  "sort": "relevance",
  "distanceMiles": 30,
  "sellerType": "any",
  "includeFeatured": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call dami_studio/gumtree-uk-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/gumtree-uk-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/AVkLmEOS9wE6JXld6/builds/w4VcLZQwvCdjP7yyy/openapi.json
