# HardwareZone SG Forum Scraper (`lexac94/hardwarezone-scraper`) Actor

Scrape HardwareZone, Singapore's largest tech and lifestyle forum. Board mode returns one row per thread; thread mode returns every reply. Filter from newest replies, keywords, bulk input.

- **URL**: https://apify.com/lexac94/hardwarezone-scraper.md
- **Developed by:** [Lexa N](https://apify.com/lexac94) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 thread listings

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

### What does HardwareZone Scraper do?

HardwareZone Scraper extracts **threads and posts from [HardwareZone Forums](https://forums.hardwarezone.com.sg)**, Singapore's largest technology and lifestyle community. Point it at a board to get a structured list of threads, or at specific threads to get every reply — with **keyword filtering, date filtering, and newest-first traversal** so you only pay for the content you actually want.

It runs on the Apify platform, so you get scheduling, an HTTP API, dataset exports (JSON, CSV, Excel, HTML), proxy rotation, and integrations with Make, Zapier, Google Drive and others without hosting anything yourself.

### Why use HardwareZone Scraper?

- **Brand and product monitoring** — track what Singaporeans say about your product in EDMW, the phone user groups, or Money Mind.
- **Market research** — HWZ threads run for years and thousands of replies; the date filter pulls just the recent stretch.
- **Sentiment and trend analysis** — feed structured post data into your own NLP pipeline.
- **Deal and price tracking** — the Marketplace and deals boards move fast; schedule a board run hourly.
- **Community archiving** — capture a thread before it scrolls away.

### How to use HardwareZone Scraper

1. Choose a **Mode**: *Board threads* (one row per thread) or *Full thread* (one row per reply).
2. For board mode, enter one or more **Board IDs**. For thread mode, enter **Thread IDs** or **Thread URLs**.
3. Optionally set **Max items**, **Only items since this time**, and a **Title keyword filter**.
4. Click **Save & Start**.
5. When the run finishes, open the **Output** tab, or download the dataset as JSON/CSV/Excel.

#### Finding a board ID

Open a board on HWZ and read the number at the end of the URL:

```
https://forums.hardwarezone.com.sg/forums/android-user-group.323/
                                                            ^^^ board ID
```

**Important:** HardwareZone nests boards inside categories, and both look identical in the URL. A *category* (e.g. `16` Eat-Drink-Man-Woman, `9` Mobile Communication Technology) lists sub-forums, not threads, and returns no results. Use a leaf board such as `234` (Chill-out / Hangout Den), `323` (Android User Group) or `242` (Events, Entertainment & Celebrity Buzz). If you pick a category, the log tells you so.

### Input

| Field | Type | Notes |
| --- | --- | --- |
| `mode` | string | `forum` (board threads) or `thread` (full thread). Required. |
| `forumIds` | string\[] | Board IDs. Board mode only. |
| `keywords` | string\[] | Keep only threads whose title contains one of these. Board mode only. |
| `excludeKeywords` | string\[] | Drop threads whose title contains one of these. Wins over `keywords`. Board mode only. |
| `matchWholeWords` | boolean | Off (default): a keyword matches anywhere in the title, so `vivo` also matches `vivoX300`. On: it must be its own word. Applies to both keyword fields. |
| `threadIds` | string\[] | Thread IDs. Thread mode only. |
| `threadUrls` | string\[] | Full thread URLs. Thread mode only. |
| `maxPagesPerThread` | integer | Pages of replies per thread, counting back from newest. `0` = unlimited. |
| `bulkInput` | string | One item per line; merged into the matching field above. |
| `maxItems` | integer | Board mode: threads per board. Thread mode: number of threads. |
| `dateFrom` | string | `3d`, `12h`, `2w`, or `MM/DD/YY`. |
| `proxyConfiguration` | object | Apify datacenter proxy, on by default. |

```json
{
  "mode": "forum",
  "forumIds": ["323"],
  "keywords": ["Pixel", "Vivo"],
  "maxItems": 50,
  "dateFrom": "7d"
}
```

### Output

Two row shapes share one dataset. Board mode emits **post rows** only. Thread mode emits one **post row** per thread (built from the opening post) followed by **reply rows**.

**Post row**

```json
{
  "post_id": "6885306",
  "site": "hardwarezone",
  "board": "323",
  "title": "(SIC) Smartphone deals",
  "author": "8-lane",
  "reply_count": "2K",
  "view_count": "251K",
  "created_at": "2023-03-23T10:16:18+0800",
  "last_reply_at": "2026-09-09T21:07:41+0800",
  "post_url": "https://forums.hardwarezone.com.sg/threads/6885306/",
  "content": null,
  "is_pinned": false,
  "source_mode": "forum",
  "source_query": "323",
  "scraped_at": "2026-09-09T14:14:26.593Z"
}
```

**Reply row**

```json
{
  "reply_id": "159846039",
  "post_id": "7147770",
  "site": "hardwarezone",
  "floor": 11698,
  "author": "human_shieldz",
  "content": "I play safe for a $2000 device lol",
  "created_at": "2026-09-09T20:34:04+0800",
  "scraped_at": "2026-09-09T14:14:26.593Z"
}
```

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

#### Data fields

| Field | Description |
| --- | --- |
| `post_id` | Thread ID. Present on both row types; joins replies to their thread. |
| `reply_id` | HardwareZone post ID. Reply rows only. |
| `floor` | Position of the reply in the thread (`#8,661` → `8661`). |
| `site` | Always `hardwarezone`. |
| `board` | Board ID. Board mode only. |
| `title` | Thread title. Post rows only. |
| `author` | Username of the thread starter or reply author. |
| `content` | Post body as plain text. `null` on board-mode rows. |
| `reply_count`, `view_count` | As HWZ displays them, e.g. `"2K"`, `"251K"`. Board mode only. |
| `created_at` | Post time, HWZ's raw format with a `+0800` offset. |
| `last_reply_at` | Last activity on the thread. Board mode only. |
| `post_url` | Canonical thread URL. |
| `is_pinned` | Whether the thread is a sticky. |
| `source_mode`, `source_query` | Which mode produced the row, and the input value it came from. |
| `scraped_at` | UTC timestamp, identical for every row in a run. |

### How much does it cost to scrape HardwareZone?

Runs are billed in Apify compute units (CUs). Measured figures:

- **Board mode, 10 threads from one board:** 0.032 CU, 29 seconds.
- **Thread mode, a 585-page thread with `dateFrom: 2d`:** 2 page loads, ~17 seconds.

The Apify free tier includes a monthly CU allowance that comfortably covers scheduled board monitoring. Thread mode costs more per run because it loads full pages — use `dateFrom` and `maxPagesPerThread` to keep it bounded.

### Tips

- **Use `dateFrom` on long threads.** Threads are walked newest-page-first, so the run stops as soon as a page falls past your cutoff. Pulling the last 2 years of a thread started in 2008 costs a handful of pages, not hundreds.
- **`maxItems` does not limit replies.** In thread mode it caps how many *threads* are scraped. Use `maxPagesPerThread` to bound replies.
- **Pinned threads ignore `dateFrom`.** Boards pin announcements that can be a decade old; without the exemption a single sticky would stop the crawl on the first row. They are still subject to the keyword filter.
- **The opening post always comes back.** Even when `dateFrom` excludes it, so replies always have a thread record to join to.
- **Leave the proxy on** for platform runs. Without it your run shares the platform's egress IPs and HWZ will rate-limit you.
- **Zero rows is not always a failure.** If the run parsed items but filters excluded them all, it exits successfully and says so. It only fails when nothing could be parsed at all — wrong ID, a category instead of a board, or unreachable pages.

### FAQ

**Does it need a HardwareZone account?**
No. It reads publicly visible content only. Some sections show an age-verification wall to logged-out visitors; the run logs a warning when it hits one, and post content there may be hidden.

**Can it scrape multiple boards at once?**
Yes. `maxItems` applies per board, so three board IDs with `maxItems: 50` yields up to 150 rows.

**Why did I get no results?**
Most often the board ID is a category rather than a leaf board — check the log. Otherwise your `dateFrom` or keyword filter may be too narrow.

**Is scraping HardwareZone legal?**
This Actor collects only publicly available content. You are responsible for how you use the data, including compliance with HardwareZone's Terms and Conditions, Singapore's PDPA, and any other applicable law. Do not collect or republish personal data without a lawful basis.

### Limitations

- Category boards are not resolved to their sub-forums; you must supply leaf board IDs.
- `keywords` and `excludeKeywords` filter thread titles only, in board mode only. Post
  bodies are not searched, and neither field does anything in thread mode.
- A `keywords` filter does not stop pagination the way `dateFrom` does — it keeps
  reading pages looking for matches until `maxItems` is reached. Pair the two when
  sweeping a large board.
- There is no cap on total rows in thread mode — `dateFrom` and `maxPagesPerThread` are the available brakes.
- `reply_count` and `view_count` are kept as HWZ's abbreviated strings (`"2K"`) rather than parsed to integers.

# Actor input Schema

## `mode` (type: `string`):

Board threads returns one row per thread from a board's listing pages and never opens a thread. Full thread returns the thread's opening post plus one row per reply.

## `forumIds` (type: `array`):

HardwareZone board IDs — the {id} in forums/{slug}.{id}/. e.g. 234 (Chill-out / Hangout Den), 323 (Android User Group). Must be a board that lists threads, not a category that lists sub-forums: 16 and 9 are categories and return nothing.

## `keywords` (type: `array`):

Optional. Keeps only threads whose title contains at least one keyword (case-insensitive). Leave empty for no filtering.

## `excludeKeywords` (type: `array`):

Drops any thread whose title contains one of these words, even if it also matches the filter above. Useful for stripping out spam and off-topic threads.

## `matchWholeWords` (type: `boolean`):

Off, a keyword matches anywhere in the title, so "vivo" also matches "vivoX300". On, it must appear as its own word. Applies to both fields above.

## `threadIds` (type: `array`):

HardwareZone thread IDs — the {id} in threads/{slug}.{id}/, e.g. 7147770.

## `threadUrls` (type: `array`):

Full HardwareZone thread URLs, e.g. https://forums.hardwarezone.com.sg/threads/vivo-x300-series.7147770/. The thread ID is extracted automatically.

## `maxPagesPerThread` (type: `integer`):

Caps how many pages of replies to walk per thread, counting back from the NEWEST page. 0 means unlimited — be careful: HWZ megathreads run to 500+ pages. Replies are always returned newest first.

## `bulkInput` (type: `string`):

One item per line (press Enter for a new line, no quotes or commas). Treated as board IDs, thread IDs, or thread URLs depending on the current mode. Fastest way is to paste a column straight from Google Sheets or Excel.

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

Board threads mode: max threads per board. Full thread mode: max threads to scrape — replies within a thread are not limited by this, use Max pages per thread instead.

## `dateFrom` (type: `string`):

Formats: 3d, 12h, 2w, or MM/DD/YY. Leave empty for no filtering. Board threads mode: filters threads by the newer of post time and last reply time, and stops paging early — pinned threads are exempt from both. Full thread mode: filters replies by post time; because threads are walked newest-first this stops as soon as a page falls past the cutoff, so pulling the last 2 years of a thread started in 2008 costs a few pages, not all of them. The thread's opening post is always returned.

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

Without a proxy your run shares the platform's egress IPs with every other run, so HardwareZone rate-limits you (429s, missing pages). HTML responses are small, so proxy bandwidth cost is negligible. Turn it off only for local testing.

## Actor input object example

```json
{
  "mode": "forum",
  "forumIds": [
    "323"
  ],
  "keywords": [],
  "excludeKeywords": [],
  "matchWholeWords": false,
  "threadIds": [],
  "threadUrls": [],
  "maxPagesPerThread": 0,
  "bulkInput": "",
  "maxItems": 50,
  "dateFrom": "",
  "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 = {
    "forumIds": [
        "323"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("lexac94/hardwarezone-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 = { "forumIds": ["323"] }

# Run the Actor and wait for it to finish
run = client.actor("lexac94/hardwarezone-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 '{
  "forumIds": [
    "323"
  ]
}' |
apify call lexac94/hardwarezone-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,lexac94/hardwarezone-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/WTaVH0JtvCVW0Ak3I/builds/PshStN4eoyBROErRt/openapi.json
