# Halfords Scraper — Bikes, Car Parts & Prices (`studio-amba/halfords-scraper`) Actor

Scrape product data from halfords.com, the UK's largest automotive parts and cycling retailer. Extract names, brands, prices, stock levels and images for any product category, unblocked with Bright Data Web Unlocker.

- **URL**: https://apify.com/studio-amba/halfords-scraper.md
- **Developed by:** [Studio Amba](https://apify.com/studio-amba) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 result scrapeds

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Halfords Scraper

Pull product data from halfords.com, the UK's largest automotive parts and cycling retailer. This scraper reads directly from Halfords' own server-rendered product pages, so it returns clean, structured data — name, brand, price, live stock level, images — for real catalogue listings, not a scraped screenshot of a page.

### How to scrape Halfords data

Give the actor a category path prefix (`categoryFilter`, e.g. `"/bikes/mountain-bikes/"` or `"/motoring/car-parts/"`) and it enumerates matching product URLs from Halfords' own public sitemap, then fetches each product page for full data.

This is a category-based actor, not a free-text search box. Halfords' `/search` endpoint and its category-page pagination parameter (`?start=`) are both disallowed in `robots.txt` — confirmed live — so this actor works entirely from Halfords' public sitemap (`sitemap_index.xml`, ~65,000 product URLs across 13 files) instead, filtered to whichever category you're after. Every URL this actor visits is a clean, robots-allowed product detail path.

Halfords sits behind Akamai's bot-management layer: a plain request to any `halfords.com` path — including `/robots.txt` itself — gets rejected outright with a 403 "Access Denied" edge page. This actor routes every request through Bright Data's Web Unlocker, which clears the block, so you don't need your own proxy or browser-automation setup.

Under the hood, Halfords runs on Salesforce Commerce Cloud via a React/PWA-Kit storefront. Every server-rendered page embeds a `<script id="mobify-data">` blob holding React Query's client-side cache, and on a product page one of those cached entries is the full Salesforce Shopper Products API object for that SKU — name, brand, price, currency, EAN, live inventory, rating, category breadcrumb and complete image galleries. There's no separate hidden API call to reverse-engineer; the same Bright Data-fetched HTML page the actor already downloads for you IS the data source.

If you don't provide any input, it defaults to scraping Halfords' "Mountain Bikes" category as a demo — a real, live category with dozens of matching products.

### Input

| Field | Type | Description |
|-------|------|-------------|
| `categoryFilter` | string | Path prefix to filter products from Halfords' sitemap, e.g. `"/bikes/mountain-bikes/"` or `"/motoring/car-parts/"`. Defaults to `"/bikes/mountain-bikes/"` if left empty. |
| `maxResults` | integer | Maximum number of products to scrape (default: 100, hard-capped at 2000) |
| `maxSitemapFiles` | integer | Safety cap on how many sitemap files (each ~5,000 URLs) to scan looking for matches (default: 13, the full sitemap) |
| `brightDataApiKey` | string | Your own Bright Data API key, if you want to use your own Web Unlocker zone instead of the actor's built-in one |
| `proxyConfiguration` | object | Not used — this actor reaches halfords.com exclusively through Bright Data, which handles unblocking on its own. Kept for input-shape compatibility. |

### What you get back

| Field | Type | Example |
|-------|------|---------|
| `name` | string | `"Boardman MHT 8.9 Mountain Bike - S, M, L Frames"` |
| `brand` | string | null | `"Boardman"` — falls back to the first word of the product name when Halfords doesn't expose a dedicated brand field (e.g. motoring/car-parts) |
| `price` | number | null | `800` — current selling price in GBP |
| `originalPrice` | number | null | Pre-discount list price, only present when the source marks a markdown |
| `currency` | string | `"GBP"` — halfords.com's UK storefront is priced in GBP |
| `sku` | string | null | `"189326"` — Halfords manufacturer SKU or internal product ID |
| `ean` | string | null | European Article Number / barcode, when the source provides one |
| `url` | string | Full product page URL |
| `inStock` | boolean | null | Live stock availability from Halfords' inventory API. Null when the source doesn't confirm stock status — never coerced to `false`. |
| `stockLevel` | integer | null | `27` — live unit count in Halfords' inventory system |
| `category` | string | null | `"Cycling/Bikes/Mountain Bikes"` — full breadcrumb |
| `rating` | number | null | `4.24` — average customer rating out of 5 |
| `reviewCount` | integer | null | `49` |
| `imageUrl` | string | null | Primary product image |
| `imageUrls` | array | Up to 8 product image URLs |
| `scrapedAt` | string | ISO timestamp |

### Sample output

```json
{
  "name": "Boardman MHT 8.9 Mountain Bike - S, M, L Frames",
  "brand": "Boardman",
  "price": 800,
  "originalPrice": 1050,
  "currency": "GBP",
  "sku": "189326",
  "url": "https://www.halfords.com/bikes/mountain-bikes/boardman-mht-8.9-mountain-bike-189326.html",
  "inStock": true,
  "stockLevel": 27,
  "category": "Cycling/Bikes/Mountain Bikes",
  "rating": 4.24,
  "reviewCount": 49,
  "imageUrl": "https://cdn.media.halfords.com/i/washford/189358/Boardman-MHT-8.9-Mountain-Bike---S-M-L-Frames",
  "imageUrls": [
    "https://cdn.media.halfords.com/i/washford/189358/Boardman-MHT-8.9-Mountain-Bike---S-M-L-Frames",
    "https://cdn.media.halfords.com/i/washford/189358d/Boardman-MHT-8.9-Mountain-Bike---S-M-L-Frames"
  ],
  "scrapedAt": "2026-09-04T14:45:25.683Z"
}
```

### Use cases

- **Price monitoring** — track Halfords' own price and markdown changes on the bikes, parts or accessories you sell against.
- **Stock tracking** — `inStock` and `stockLevel` come straight from Halfords' live inventory API, so you can watch availability change over time, not just price.
- **Assortment research** — see the full product range for a category (car parts, bike parts, tools, camping gear) without clicking through pages by hand.
- **Market comparison** — Halfords is the reference UK retailer for automotive parts and cycling; useful alongside other UK/EU retail scrapers when benchmarking pricing.

### Coverage

halfords.com is Halfords' UK storefront, browsed here through a GB Bright Data exit — pricing and catalogue reflect the UK market (GBP). Halfords also runs a separate Ireland storefront (halfords.ie, EUR) that this actor does not cover.

### Performance and cost

Halfords has no crawlable category-listing page — unlike some retailers, its category pages don't embed a product grid, and its own pagination parameter is robots.txt-disallowed. So this actor fetches one Bright Data request per product, sourced from Halfords' public sitemap, at roughly $0.0015 Bright Data cost per result. A 20-product run typically completes in a couple of minutes. Your run's usage cost only settles after the run reports SUCCEEDED — checking cost mid-run will undercount it.

### Limitations

- Input is a category path prefix, not free-text search — Halfords' own `/search` endpoint is disallowed by `robots.txt`, so this actor works from the site's public product sitemap instead.
- `originalPrice` is only populated when Halfords marks an item as discounted against its own list price; full-price items will show it as absent, which is correct, not missing data.
- `brand` comes straight from the source on most categories, but falls back to the first word of the product name on categories (like motoring/car-parts) where Halfords doesn't expose a dedicated brand field at all.
- `rating` and `reviewCount` are absent on products with no customer reviews yet, rather than showing as `0`.
- Product variants (size, colour) aren't expanded into separate rows — each sitemap URL maps to one product record, generally the master or default variant Halfords itself surfaces at that URL.
- If Halfords restructures its Salesforce PWA-Kit storefront or the embedded `mobify-data` React Query cache shape, the actor will need updating.

### Support

Found an issue or need a category that isn't resolving correctly? Open an issue on this actor's Apify Store page and we'll take a look.

### Why Bright Data instead of a standard proxy

Halfords' Akamai layer rejects a plain request outright with a 403 "Access Denied" page — confirmed against `/robots.txt` itself, which is normally the one path every crawler is allowed to fetch unhindered. Bright Data's Web Unlocker handles the fingerprinting and challenge-solving needed to get a clean response, so this actor can focus on parsing real product data instead of maintaining an anti-bot bypass.

# Actor input Schema

## `categoryFilter` (type: `string`):

Path prefix used to filter products from halfords.com's sitemap, e.g. "/bikes/mountain-bikes/" or "/motoring/car-parts/". Leave empty to use the default ("/bikes/mountain-bikes/"). halfords.com's own search box and category-page pagination are both robots.txt-disallowed, so this actor works from the site's public product sitemap instead.

## `maxResults` (type: `integer`):

Maximum number of products to scrape. Hard-capped at 2000 per run.

## `maxSitemapFiles` (type: `integer`):

Safety cap on how many of halfords.com's sitemap\_N.xml files (each ~5,000 URLs) to scan while looking for matching products. Rarely needs changing.

## `brightDataApiKey` (type: `string`):

Bright Data API key (Web Unlocker zone) used to reach halfords.com behind its Akamai anti-bot protection. Falls back to the actor's own BRIGHT\_DATA\_API\_KEY environment variable if left empty.

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

Not used — this actor reaches halfords.com exclusively through Bright Data Web Unlocker (Akamai-protected, confirmed live), which handles unblocking on its own. Kept for input-shape compatibility.

## Actor input object example

```json
{
  "categoryFilter": "/motoring/car-parts/",
  "maxResults": 20,
  "maxSitemapFiles": 13,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

No description

# 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 = {
    "categoryFilter": "/motoring/car-parts/",
    "maxResults": 20,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("studio-amba/halfords-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 = {
    "categoryFilter": "/motoring/car-parts/",
    "maxResults": 20,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("studio-amba/halfords-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 '{
  "categoryFilter": "/motoring/car-parts/",
  "maxResults": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call studio-amba/halfords-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,studio-amba/halfords-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/Kq7SY7NMku0XO4mpb/builds/Rq2jq3yc7Ucg2auYH/openapi.json
