# SUUMO Property Investment Analyzer (`woolen_snake/suumo-investment-analyzer`) Actor

An Apify Actor that fetches SUUMO used-condo listings (by individual URL or by area × building-age filters), then cross-references area and floor-plan rent averages to estimate gross rental yield and an age-adjusted net yield benchmark. Pay-per-event pricing.

- **URL**: https://apify.com/woolen\_snake/suumo-investment-analyzer.md
- **Developed by:** [coco zizi](https://apify.com/woolen_snake) (community)
- **Categories:** Real estate, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.00005 / actor start

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/platform/actors/running/actors-in-store#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

## SUUMO Property Investment Analyzer

An Apify Actor that fetches used-condo listings from [SUUMO](https://suumo.jp) (a real estate/housing
listing site operated by Recruit Co., Ltd.) **on a per-property basis**, and cross-references each one
against area and layout-tier rent benchmarks to estimate the **gross yield** and an **age-adjusted net
yield**. Pay-per-event pricing.

Existing SUUMO-related Actors tend to lean toward either "raw bulk listing data" or "area-level rent
summaries." This Actor's differentiator is that it provides an investment analysis at the individual
property level, cross-referenced with rent benchmarks.

### ⚠️ Disclaimer (please read)

- This Actor automatically retrieves information that is publicly available on SUUMO. **You are
  responsible for how you retrieve and use this data.**
- SUUMO's [Terms of Use](https://cdn.p.recruit.co.jp/terms/suu-t-1003/index.html) generally prohibit
  commercial use (except where Recruit Co., Ltd. grants permission). If you publish or operate this
  Actor commercially / with Pay-per-event pricing, please make that decision with the understanding that
  it may conflict with that clause. The developer makes no legal warranty on this point. **How you scope
  the release (private / restricted / public on the Store) is a separate decision you should make with
  this contractual risk in mind.**
- **The site owner's Terms of Use, robots.txt, or access blocking (e.g. IP blocks) could change without
  notice, which could stop this Actor from working or change its output unexpectedly.**
- The investment figures (gross yield / net yield estimate) are **reference values** based on
  statistical rent-market estimation and simple assumptions (e.g. an assumed vacancy rate) — they are not
  an appraisal or a guarantee of rent or yield. Please consult a professional for actual investment
  decisions.

### PII design

This Actor only targets a property's **physical attributes and price information** (price, floor area,
building age, address, nearest station, structure, management fee, repair reserve fee, etc.) — by design,
it does **not** capture or output an agent's **personal name or personal phone number**, since those
labels are not included in the extraction targets.

During research, we confirmed that SUUMO's used-condo detail pages sometimes embed an agent's personal
name and mobile phone number inside the free-text "Property highlights" section (not a labeled,
structured field — it's mixed into the prose). This Actor doesn't parse that free-text section at all, so
it normally wouldn't be affected, but we apply two layers of defense regardless:

1. **Extraction is limited to clearly labeled attribute fields** (`src/suumoClient.js`). Free-text
   sections, promotional copy, and comments are never parsed.
2. **A safety net right before output** (`src/privacyFilter.js`): every output field is scanned for a
   phone-number-like regex pattern (`0\d{1,4}-\d{1,4}-\d{3,4}`) and person-label wording like "Agent:";
   any match nulls out that field. Detections are recorded in the output's `personalInfoRedactions`
   field, so you can review them before redistributing the data externally.

That said, future changes to SUUMO's markup could still introduce PII into a field not covered by
defense #1 above, so **always review the output before redistributing it externally.**

### What it does

- **byUrl mode**: analyze the specified property detail page URL(s) individually
- **byArea mode**: crawl a used-condo listing for a given prefecture/city + max building age, and analyze
  matching properties
- For each property, fetch the area/layout-tier rent benchmark (from SUUMO's own published rent-market
  data) and compute:
  - **Gross yield** (estimated annual rent ÷ price)
  - **Net yield estimate** (estimated annual rent − annual management fee & repair reserve fee − an
    assumed vacancy-rate deduction, all divided by price)

### Input parameters

| Parameter | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `byArea` | `byUrl` (specify URLs) / `byArea` (search by area + max building age) |
| `propertyUrls` | array | `[]` | For `byUrl`: property detail page URL(s) to analyze (`suumo.jp/ms/chuko/.../nc_.../` format) |
| `prefecture` | string | `""` | For `byArea`: prefecture URL segment (e.g. `tokyo`, `osaka`) |
| `city` | string | `""` | For `byArea`: city/ward URL segment (e.g. `sc_setagaya`) |
| `maxBuildingAgeYears` | integer | `30` | For `byArea`: only properties at or under this building age are targeted |
| `maxItems` | integer | `20` | Cap on the number of properties output/charged (max 200) |
| `maxDetailFetches` | integer | `100` | For `byArea`: safety cap on detail pages actually accessed, including non-matching properties (max 1000) |
| `requestDelayMs` | integer | `2000` | Delay between requests in ms. Cannot be set below 1000ms for safety |

For `prefecture`/`city`, open the actual used-condo listing page for your target area
(`https://suumo.jp/ms/chuko/[prefecture]/[city]/`) and read the corresponding segments off the URL.

### Example output

The following is actual output from a real run of this Actor (as of August 5, 2026). The address and
property name naturally stay in Japanese, since that's the language of the source listing.

```json
{
  "propertyUrl": "https://suumo.jp/ms/chuko/osaka/sc_suita/nc_20946138/",
  "propertyName": "メゾン碧荘",
  "priceYen": 6980000,
  "priceDisplay": "698万円",
  "floorAreaSqm": 50.51,
  "layout": "2LDK",
  "builtYearMonth": "1974-03",
  "buildingAgeYears": 52,
  "address": "大阪府吹田市千里丘上",
  "traffic": "ＪＲ東海道本線「千里丘」歩5分 阪急京都線「摂津市」歩14分 大阪モノレール本線「宇野辺」歩24分",
  "walkMinutes": 5,
  "floorAndStructure": "4階/RC4階建",
  "managementFeeYen": 7300,
  "repairReserveFeeYen": 10240,
  "totalUnits": 29,
  "prefectureSegment": "osaka",
  "citySegment": "sc_suita",
  "rentBenchmark": {
    "layout": "2LDK",
    "matchedLayout": "2LDK",
    "avgMonthlyRentYen": 83000,
    "buildingAgeBucket": "20年〜30年",
    "walkMinutesBucket": "1分〜5分",
    "isExtrapolated": true,
    "sourceUrl": "https://suumo.jp/chintai/soba/osaka/sc_suita/?et=5&cn=30"
  },
  "investmentAnalysis": {
    "estimatedMonthlyRentYen": 83000,
    "annualGrossRentYen": 996000,
    "grossYieldPercent": 14.27,
    "vacancyRateAssumption": 0.15,
    "annualHoldingCostYen": 210480,
    "netAnnualIncomeYen": 636120,
    "netYieldEstimatePercent": 9.11,
    "note": "表面利回りはエリア・間取りの賃料相場(統計的な集計値)から推定した想定賃料に基づく参考値です。実質利回り目安は、管理費・修繕積立金(物件の実額)と築年数に応じた空室率の仮定(目安値)を差し引いたものであり、個別物件の内装・向き・実際の運用結果を反映したものではありません。 なお、この物件の築年数または駅徒歩分数はSUUMO家賃相場データが対応する範囲(築30年・徒歩20分まで)を超えているため、相場データは最も近い上限バケットの値を代用した外挿値です。実際の相場との差が大きくなる可能性があるため、参考値としての精度はより低くなります。"
  },
  "disclaimer": "SUUMO(株式会社リクルート運営)上の公開情報を基に自動生成した参考データです。取得・利用は利用者ご自身の責任で行ってください。サイト運営者の規約変更やアクセス遮断により、本Actorは予告なく停止・変化する可能性があります。投資分析値(表面利回り・実質利回り目安)は統計的な賃料相場推定に基づく参考値であり、鑑定・保証ではありません。実際の投資判断は専門家にご相談ください。",
  "scrapedAt": "2026-08-05T07:15:51.994Z",
  "personalInfoRedactions": []
}
```

This property is 52 years old and 5 minutes' walk from the nearest station, which is outside the range
SUUMO's rent benchmark data covers (up to 30 years old / 20 minutes' walk), so `isExtrapolated: true` is
set — flagging that this is a coarser approximation than usual.

### Investment analysis logic

1. SUUMO's rent-benchmark page (`/chintai/soba/[prefecture]/[city]/`) can be filtered by **minutes' walk
   to the nearest station (et)** and **building age (cn)** as query parameters (available up to 20
   minutes / 30 years). The target property's actual walk time and building age are mapped into these
   buckets; when they exceed the available range, the nearest upper-bound bucket is used instead and
   `isExtrapolated: true` is set.
2. From that area × walk-bucket × age-bucket rent table, the average monthly rent for the **same layout**
   (1K, 1LDK, 2LDK, ...) as the target property is taken as the "estimated rent."
3. **Gross yield** = estimated annual rent ÷ price × 100
4. **Net yield estimate** = (estimated annual rent − annual management fee & repair reserve fee −
   estimated annual rent × assumed vacancy rate) ÷ price × 100
   - The assumed vacancy rate is a rough age-band figure (under 10 years: 5%, 10-20 years: 8%, 20-30
     years: 12%, 30+ years: 15%) and is not based on precise statistical survey data.

**Key assumption**: the estimated rent is not the target property's own rental history — it's the
**statistical area × walk-time × building-age × layout market rate**. It does not reflect variation from
that specific property's interior, orientation, floor, etc. Properties flagged `isExtrapolated: true` are
extrapolated beyond the rent data's covered range, so treat them as lower-precision reference values.

### Pricing (Pay-per-event)

| Event | Price | Trigger |
|---|---|---|
| Actor Start (`apify-actor-start`) | $0.00005 | Once per run (Apify's standard synthetic event) |
| Property analyzed (`property-analyzed`) | $0.08 | Per property output with an investment analysis. Not charged for properties that don't match the building-age filter or couldn't be analyzed due to missing data |

#### How the price was set

The measured Apify infrastructure cost was about $0.0004/item (based on compute time, Compute Units, and
data transfer). Given Apify's pay-per-event revenue share (`profit = 0.8 × PPE revenue − infra cost`),
the break-even price is about $0.0005/item — meaning cost is not the real constraint on pricing. So the
price here is set based on delivered value ("supports an individual property's investment decision")
rather than cost — following the same logic our BOOTH Actor used (raw listing data at $0.002 vs. detailed
data at $0.03 to reflect the value gap), adjusted upward here because this Actor's output feeds into
purchase decisions worth tens of millions of yen. That said, since these are statistical reference values
rather than a formal appraisal, we've kept the price from being set too aggressively high.

### Load management (avoiding IP blocks)

Based on other existing SUUMO-related Actor implementations, we insert a default **2000ms (2s)** delay
between each request (listing pages, detail pages, rent-benchmark pages). Adjustable via
`requestDelayMs`, but it cannot be set below 1000ms for safety. Area × layout-tier rent benchmarks are
cached for the duration of a run to avoid re-fetching the same combination.

### Known limitations / things to verify before production use

- The selectors in `src/suumoClient.js` were verified against real SUUMO response HTML (used-condo
  detail pages, rent-benchmark pages) as of August 5, 2026. SUUMO's markup may change over time; if this
  stops working, run `npm run test-connectivity` to inspect the actual response and adjust accordingly.
- SUUMO's rent-benchmark data **only covers up to 30 years of building age and 20 minutes' walk time** —
  properties beyond that (common for older, pre-current-code buildings in city centers) get an
  `isExtrapolated: true` extrapolated value. Keep this in mind when analyzing areas with a lot of older
  buildings.
- In `byArea` mode, the listing page's own summary fields (price, build date overview, etc.) are not used
  for filtering — each collected property detail URL is fetched individually and judged on the accurate
  values from that page (see `main.js`). This means properties that don't match `maxBuildingAgeYears`
  will still be accessed (but not charged). **Depending on the listing page's default sort order, it's
  possible for zero matching properties to fall within the `maxDetailFetches` range** (especially in
  areas with many pre-current-code buildings when `maxBuildingAgeYears` is set low). If you get zero
  results, try increasing `maxDetailFetches`.
- A "full sync" feature that crawls an entire area across multiple runs (equivalent to our BOOTH Actor's
  `startPage`/`SYNC_STATE`) is not implemented. The current design crawls up to `maxDetailFetches`
  properties in a single run.
- Property types other than used condos (detached houses, rental-property investment analysis, etc.) are
  out of scope.

# Actor input Schema

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

byUrl: analyze only the properties given in propertyUrls. byArea: crawl an area using the prefecture/city/maxBuildingAgeYears criteria and analyze matching properties.

## `propertyUrls` (type: `array`):

SUUMO used-condo detail page URLs to analyze (format: https://suumo.jp/ms/chuko/.../nc\_.../, e.g. https://suumo.jp/ms/chuko/osaka/sc\_suita/nc\_20946138/). Used when mode is byUrl.

## `prefecture` (type: `string`):

The prefecture segment used in SUUMO's URL path (e.g. "tokyo" for Tokyo, "osaka" for Osaka Prefecture). Used as https://suumo.jp/ms/chuko/\[this value]/\[city]/. Open the used-condo listing page for your target area to confirm it.

## `city` (type: `string`):

The city/ward segment used in SUUMO's URL path (e.g. "sc\_setagaya" for Setagaya Ward). Open the used-condo listing page for your target area and check the part starting with "sc\_" in the URL.

## `maxBuildingAgeYears` (type: `integer`):

Only properties at or under this building age are analyzed.

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

Cap on the number of properties analyzed and output. A charge event is generated per property, so this directly drives run cost.

## `maxDetailFetches` (type: `integer`):

In byArea mode, the upper limit on how many detail pages are actually accessed, including properties that don't match maxBuildingAgeYears (only matching, output properties are charged).

## `requestDelayMs` (type: `integer`):

Delay between each request, to reduce load on SUUMO's servers and avoid access blocking (IP blocks). Cannot be set below 1000ms for safety.

## Actor input object example

```json
{
  "mode": "byArea",
  "propertyUrls": [],
  "prefecture": "",
  "city": "",
  "maxBuildingAgeYears": 30,
  "maxItems": 20,
  "maxDetailFetches": 100,
  "requestDelayMs": 2000
}
```

# Actor output Schema

## `analyzedProperties` (type: `string`):

Property attributes cross-referenced against area/layout rent benchmarks, with estimated gross and net yield figures.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("woolen_snake/suumo-investment-analyzer").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("woolen_snake/suumo-investment-analyzer").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 '{}' |
apify call woolen_snake/suumo-investment-analyzer --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,woolen_snake/suumo-investment-analyzer"
        }
    }
}

```

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/Wqjk9ts4wlL7SIyBu/builds/VModt9XbYyz8qDcQc/openapi.json
